]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mimetmac.cpp
87a187113cbb2b531669ca72fda8f699662dedf6
[wxWidgets.git] / src / mac / carbon / mimetmac.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/mimetype.cpp
3 // Purpose: Mac Carbon implementation for wx mime-related classes
4 // Author: Ryan Norton
5 // Modified by:
6 // Created: 04/16/2005
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "mimetype.h"
14 #endif
15
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/string.h"
25 #if wxUSE_GUI
26 #include "wx/icon.h"
27 #endif
28 #endif //WX_PRECOMP
29
30
31 #if wxUSE_MIMETYPE
32
33 #include "wx/log.h"
34 #include "wx/file.h"
35 #include "wx/intl.h"
36 #include "wx/dynarray.h"
37 #include "wx/confbase.h"
38
39 #include "wx/mac/mimetype.h"
40 #include "wx/mac/private.h" //wxMacMakeStringFromPascal
41
42 // other standard headers
43 #include <ctype.h>
44 #include <InternetConfig.h> //For mime types
45
46
47 /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
48
49 /* IsRemoteVolume can be used to find out if the
50 volume referred to by vRefNum is a remote volume
51 located somewhere on a network. the volume's attribute
52 flags (copied from the GetVolParmsInfoBuffer structure)
53 are returned in the longword pointed to by vMAttrib. */
54 OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) {
55 HParamBlockRec volPB;
56 GetVolParmsInfoBuffer volinfo;
57 OSErr err;
58 volPB.ioParam.ioVRefNum = vRefNum;
59 volPB.ioParam.ioNamePtr = NULL;
60 volPB.ioParam.ioBuffer = (Ptr) &volinfo;
61 volPB.ioParam.ioReqCount = sizeof(volinfo);
62 err = PBHGetVolParmsSync(&volPB);
63 if (err == noErr) {
64 *isRemote = (volinfo.vMServerAdr != 0);
65 *vMAttrib = volinfo.vMAttrib;
66 }
67 return err;
68 }
69
70
71 /* BuildVolumeList fills the array pointed to by vols with
72 a list of the currently mounted volumes. If includeRemote
73 is true, then remote server volumes will be included in
74 the list. When remote server volumes are included in the
75 list, they will be added to the end of the list. On entry,
76 *count should contain the size of the array pointed to by
77 vols. On exit, *count will be set to the number of id numbers
78 placed in the array. If vMAttribMask is non-zero, then
79 only volumes with matching attributes are added to the
80 list of volumes. bits in the vMAttribMask should use the
81 same encoding as bits in the vMAttrib field of
82 the GetVolParmsInfoBuffer structure. */
83 OSErr BuildVolumeList(Boolean includeRemote, short *vols,
84 long *count, long vMAttribMask) {
85 HParamBlockRec volPB;
86 Boolean isRemote;
87 OSErr err;
88 long nlocal, nremote;
89 long vMAttrib;
90
91 /* set up and check parameters */
92 volPB.volumeParam.ioNamePtr = NULL;
93 nlocal = nremote = 0;
94 if (*count == 0) return noErr;
95
96 /* iterate through volumes */
97 for (volPB.volumeParam.ioVolIndex = 1;
98 PBHGetVInfoSync(&volPB) == noErr;
99 volPB.volumeParam.ioVolIndex++) {
100
101 /* skip remote volumes, if necessary */
102 err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib);
103 if (err != noErr) goto bail;
104 if ( ( includeRemote || ! isRemote )
105 && (vMAttrib & vMAttribMask) == vMAttribMask ) {
106
107 /* add local volumes at the front, remote
108 volumes at the end */
109 if (isRemote)
110 vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum;
111 else {
112 if (nremote > 0)
113 BlockMoveData(vols+nlocal, vols+nlocal+1,
114 nremote*sizeof(short));
115 vols[nlocal++] = volPB.volumeParam.ioVRefNum;
116 }
117
118 /* list full? */
119 if ((nlocal + nremote) >= *count) break;
120 }
121 }
122 bail:
123 *count = (nlocal + nremote);
124 return err;
125 }
126
127
128 /* FindApplication iterates through mounted volumes
129 searching for an application with the given creator
130 type. If includeRemote is true, then remote volumes
131 will be searched (after local ones) for an application
132 with the creator type. */
133
134 #define kMaxVols 20
135
136 /* Hacked to output to appName */
137
138 OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) {
139 short rRefNums[kMaxVols];
140 long i, volCount;
141 DTPBRec desktopPB;
142 OSErr err;
143
144 /* get a list of volumes - with desktop files */
145 volCount = kMaxVols;
146 err = BuildVolumeList(includeRemote, rRefNums, &volCount,
147 (1<<bHasDesktopMgr) );
148 if (err != noErr) return err;
149
150 /* iterate through the list */
151 for (i=0; i<volCount; i++) {
152
153 /* has a desktop file? */
154 desktopPB.ioCompletion = NULL;
155 desktopPB.ioVRefNum = rRefNums[i];
156 desktopPB.ioNamePtr = NULL;
157 desktopPB.ioIndex = 0;
158 err = PBDTGetPath(&desktopPB);
159 if (err != noErr) continue;
160
161 /* has the correct app?? */
162 desktopPB.ioFileCreator = appCreator;
163 desktopPB.ioNamePtr = appName;
164 err = PBDTGetAPPLSync(&desktopPB);
165 if (err != noErr) continue;
166
167 /* make a file spec referring to it */
168 err = FSMakeFSSpec(rRefNums[i],
169 desktopPB.ioAPPLParID, appName,
170 appSpec);
171 if (err != noErr) continue;
172
173 /* found it! */
174 return noErr;
175
176 }
177 return fnfErr;
178 }
179
180 /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
181
182 //yeah, duplicated code
183 pascal OSErr FSpGetFullPath(const FSSpec *spec,
184 short *fullPathLength,
185 Handle *fullPath)
186 {
187 OSErr result;
188 OSErr realResult;
189 FSSpec tempSpec;
190 CInfoPBRec pb;
191
192 *fullPathLength = 0;
193 *fullPath = NULL;
194
195
196 /* Default to noErr */
197 realResult = result = noErr;
198
199 /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
200 /*
201 if ( spec->name[0] == 0 )
202 {
203 result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
204 }
205 else
206 {
207 */
208 /* Make a copy of the input FSSpec that can be modified */
209 BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
210 /* }*/
211
212 if ( result == noErr )
213 {
214 if ( tempSpec.parID == fsRtParID )
215 {
216 /* The object is a volume */
217
218 /* Add a colon to make it a full pathname */
219 ++tempSpec.name[0];
220 tempSpec.name[tempSpec.name[0]] = ':';
221
222 /* We're done */
223 result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
224 }
225 else
226 {
227 /* The object isn't a volume */
228
229 /* Is the object a file or a directory? */
230 pb.dirInfo.ioNamePtr = tempSpec.name;
231 pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
232 pb.dirInfo.ioDrDirID = tempSpec.parID;
233 pb.dirInfo.ioFDirIndex = 0;
234 result = PBGetCatInfoSync(&pb);
235 /* Allow file/directory name at end of path to not exist. */
236 realResult = result;
237 if ( (result == noErr) || (result == fnfErr) )
238 {
239 /* if the object is a directory, append a colon so full pathname ends with colon */
240 if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
241 {
242 ++tempSpec.name[0];
243 tempSpec.name[tempSpec.name[0]] = ':';
244 }
245
246 /* Put the object name in first */
247 result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
248 if ( result == noErr )
249 {
250 /* Get the ancestor directory names */
251 pb.dirInfo.ioNamePtr = tempSpec.name;
252 pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
253 pb.dirInfo.ioDrParID = tempSpec.parID;
254 do /* loop until we have an error or find the root directory */
255 {
256 pb.dirInfo.ioFDirIndex = -1;
257 pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
258 result = PBGetCatInfoSync(&pb);
259 if ( result == noErr )
260 {
261 /* Append colon to directory name */
262 ++tempSpec.name[0];
263 tempSpec.name[tempSpec.name[0]] = ':';
264
265 /* Add directory name to beginning of fullPath */
266 (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]);
267 result = MemError();
268 }
269 } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
270 }
271 }
272 }
273 }
274
275 if ( result == noErr )
276 {
277 /* Return the length */
278 *fullPathLength = GetHandleSize(*fullPath);
279 result = realResult; /* return realResult in case it was fnfErr */
280 }
281 else
282 {
283 /* Dispose of the handle and return NULL and zero length */
284 if ( *fullPath != NULL )
285 {
286 DisposeHandle(*fullPath);
287 }
288 *fullPath = NULL;
289 *fullPathLength = 0;
290 }
291
292 return ( result );
293 }
294
295 //
296 // On the mac there are two ways to open a file - one is through apple events and the
297 // finder, another is through mime types.
298 //
299 // So, really there are two ways to implement wxFileType...
300 //
301 // Mime types are only available on OS 8.1+ through the InternetConfig API
302 //
303 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
304 // Low - which means you have to iterate yourself through the mime database
305 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
306 // High - which requires access to the database every time
307 //
308 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
309 // and mid-level functions
310 //
311 // TODO: Should we call ICBegin/ICEnd? Then where?
312 //
313
314 // debug helper
315 inline void wxLogMimeDebug(const wxChar* szMsg, OSStatus status)
316 {
317 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg, __LINE__, (int)status));
318 }
319
320 // in case we're compiling in non-GUI mode
321 class WXDLLEXPORT wxIcon;
322
323 bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt)
324 {
325 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
326
327 return FALSE;
328 }
329
330 bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index)
331 {
332 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
333
334 return FALSE;
335 }
336
337 bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
338 const wxFileType::MessageParameters& params) const
339 {
340 wxString cmd = GetCommand(wxT("open"));
341
342 *openCmd = wxFileType::ExpandCommand(cmd, params);
343
344 return !openCmd->empty();
345 }
346
347 bool
348 wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
349 const wxFileType::MessageParameters& params)
350 const
351 {
352 wxString cmd = GetCommand(wxT("print"));
353
354 *printCmd = wxFileType::ExpandCommand(cmd, params);
355
356 return !printCmd->empty();
357 }
358
359 //
360 // Internet Config vs. Launch Services
361 //
362 // From OS 8 on there was internet config...
363 // However, OSX and its finder does not use info
364 // from Internet Config at all - the Internet Config
365 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
366 // OR REGISTERED THROUGH INTERNET CONFIG
367 //
368 // Therefore on OSX in order for the open command to be useful
369 // we need to go straight to launch services
370 //
371
372 #if defined(__DARWIN__)
373
374 //on darwin, use launch services
375 #include "LaunchServices.h"
376
377 wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
378 {
379 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
380
381 if(verb == wxT("open"))
382 {
383 ICMapEntry entry;
384 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
385 (Handle) m_manager->m_hDatabase,
386 m_lIndex, &entry);
387
388 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
389 sCurrentExtension = sCurrentExtension.Right(sCurrentExtension.Length()-1 );
390
391 //type, creator, ext, roles, outapp (FSRef), outappurl
392 CFURLRef cfurlAppPath;
393 OSStatus status = LSGetApplicationForInfo (kLSUnknownType,
394 kLSUnknownCreator,
395 wxMacCFStringHolder(sCurrentExtension, wxLocale::GetSystemEncoding()),
396 kLSRolesAll,
397 NULL,
398 &cfurlAppPath);
399
400 if(status == noErr)
401 {
402 CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(cfurlAppPath, kCFURLPOSIXPathStyle);
403 CFRelease(cfurlAppPath);
404
405 //PHEW! Success!
406 if(cfsUnixPath)
407 return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding());
408 }
409 else
410 {
411 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
412 __LINE__,
413 wxT("LSGetApplicationForInfo failed."),
414 (int)status));
415 }
416 }
417
418 return wxEmptyString;
419 }
420
421 #else //carbon/classic implementation
422
423 wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
424 {
425 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
426
427 if(verb == wxT("open"))
428 {
429 ICMapEntry entry;
430 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
431 (Handle) m_manager->m_hDatabase,
432 m_lIndex, &entry);
433
434 //The entry in the mimetype database only contains the app
435 //that's registered - it may not exist... we need to remap the creator
436 //type and find the right application
437
438 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
439 // on here.
440 Str255 outName;
441 FSSpec outSpec;
442 if(FindApplication(entry.fileCreator, false, outName, &outSpec) != noErr)
443 return wxEmptyString;
444
445 Handle outPathHandle;
446 short outPathSize;
447 OSErr err = FSpGetFullPath(&outSpec, &outPathSize, &outPathHandle);
448
449 if(err == noErr)
450 {
451 char* szPath = *outPathHandle;
452 wxString sClassicPath(szPath, wxConvLocal, outPathSize);
453 #if defined(__DARWIN__)
454 //Classic Path --> Unix (OSX) Path
455 CFURLRef finalURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
456 wxMacCFStringHolder(sClassicPath, wxLocale::GetSystemEncoding()),
457 kCFURLHFSPathStyle,
458 false); //false == not a directory
459
460 //clean up memory from the classic path handle
461 DisposeHandle(outPathHandle);
462
463 if(finalURL)
464 {
465 CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(finalURL, kCFURLPOSIXPathStyle);
466 CFRelease(finalURL);
467
468 //PHEW! Success!
469 if(cfsUnixPath)
470 return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding());
471 }
472 #else //classic HFS path acceptable
473 return sClassicPath;
474 #endif
475 }
476 else
477 {
478 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus)err);
479 }
480 }
481 return wxEmptyString;
482 }
483 #endif //!DARWIN
484
485 bool wxFileTypeImpl::GetDescription(wxString *desc) const
486 {
487 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
488
489 ICMapEntry entry;
490 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
491 (Handle) m_manager->m_hDatabase,
492 m_lIndex, &entry);
493
494 *desc = wxMacMakeStringFromPascal(entry.entryName);
495 return true;
496 }
497
498 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
499 {
500 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
501
502 ICMapEntry entry;
503 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
504 (Handle) m_manager->m_hDatabase,
505 m_lIndex, &entry);
506
507 //entry has period in it
508 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
509 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
510 return true;
511 }
512
513 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
514 {
515 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
516
517 ICMapEntry entry;
518 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
519 (Handle) m_manager->m_hDatabase,
520 m_lIndex, &entry);
521
522 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
523 return true;
524 }
525
526 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
527 {
528 wxString s;
529
530 if (GetMimeType(&s))
531 {
532 mimeTypes.Clear();
533 mimeTypes.Add(s);
534 return TRUE;
535 }
536 else
537 return FALSE;
538 }
539
540 bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
541 {
542 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
543
544 // no such file type or no value or incorrect icon entry
545 return FALSE;
546 }
547
548 size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
549 const wxFileType::MessageParameters& params) const
550 {
551 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
552
553 wxString sCommand;
554 size_t ulCount = 0;
555
556 if(GetOpenCommand(&sCommand, params))
557 {
558 verbs->Add(wxString(wxT("open")));
559 commands->Add(sCommand);
560 ++ulCount;
561 }
562
563 return ulCount;
564 }
565
566 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
567 {
568 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
569
570 //start internet config - log if there's an error
571 //the second param is the signature of the application, also known
572 //as resource ID 0. However, as per some recent discussions, we may not
573 //have a signature for this app, so a generic 'APPL' which is the executable
574 //type will work for now
575 OSStatus status = ICStart( (ICInstance*) &m_hIC, 'APPL');
576
577 if(status != noErr)
578 {
579 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
580 wxASSERT( false );
581 m_hIC = NULL;
582 return;
583 }
584
585 ICAttr attr;
586 m_hDatabase = (void**) NewHandle(0);
587 status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase );
588
589 //the database file can be corrupt (on OSX its
590 //~/Library/Preferences/com.apple.internetconfig.plist)
591 //- bail if it is
592 if(status != noErr)
593 {
594 ClearData();
595 wxLogDebug(wxT("Corrupt Mime Database!"));
596 return;
597 }
598
599 //obtain the number of entries in the map
600 status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount );
601 wxASSERT( status == noErr );
602 /*
603 //debug stuff
604 ICMapEntry entry;
605 long pos;
606
607 for(long i = 1; i <= m_lCount; ++i)
608 {
609 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
610
611 if(status == noErr)
612 {
613 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
614 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
615 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
616
617 wxFileTypeImpl impl;
618 impl.Init(this, pos);
619
620 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
621 {
622 wxString cmd;
623 impl.GetOpenCommand (&cmd,
624 wxFileType::MessageParameters (wxT("http://www.google.com"),
625 _T("")));
626
627 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
628 }
629 }
630 }
631 */
632 }
633
634 void wxMimeTypesManagerImpl::ClearData()
635 {
636 if(m_hIC != NULL)
637 {
638 DisposeHandle((Handle)m_hDatabase);
639 //this can return an error, but we don't really care that much about it
640 ICStop( (ICInstance) m_hIC );
641 m_hIC = NULL;
642 }
643 }
644
645 //
646 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
647 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
648 //
649
650 // extension -> file type
651 wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
652 {
653 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
654
655 //low level functions - iterate through the database
656 ICMapEntry entry;
657 long pos;
658
659 for(long i = 1; i <= m_lCount; ++i)
660 {
661 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
662
663 if(status == noErr)
664 {
665 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
666 if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it
667 {
668 wxFileType* pFileType = new wxFileType();
669 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
670 return pFileType;
671 }
672 }
673 }
674
675 return NULL;
676 }
677
678 // MIME type -> extension -> file type
679 wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
680 {
681 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
682
683 //low level functions - iterate through the database
684 ICMapEntry entry;
685 long pos;
686
687 for(long i = 1; i <= m_lCount; ++i)
688 {
689 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
690 wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) );
691
692 if(status == noErr)
693 {
694 if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType)
695 {
696 wxFileType* pFileType = new wxFileType();
697 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
698 return pFileType;
699 }
700 }
701 }
702
703 return NULL;
704 }
705
706 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
707 {
708 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
709
710 //low level functions - iterate through the database
711 ICMapEntry entry;
712 long pos;
713
714 long lStartCount = (long) mimetypes.GetCount();
715
716 for(long i = 1; i <= m_lCount; ++i)
717 {
718 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
719 if( status == noErr )
720 mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) );
721 }
722
723 return mimetypes.GetCount() - lStartCount;
724 }
725
726
727 pascal OSStatus MoreProcGetProcessTypeSignature(
728 const ProcessSerialNumberPtr pPSN,
729 OSType *pProcessType,
730 OSType *pCreator)
731 {
732 OSStatus anErr = noErr;
733 ProcessInfoRec infoRec;
734 ProcessSerialNumber localPSN;
735
736 infoRec.processInfoLength = sizeof(ProcessInfoRec);
737 infoRec.processName = nil;
738 infoRec.processAppSpec = nil;
739
740 if ( pPSN == nil ) {
741 localPSN.highLongOfPSN = 0;
742 localPSN.lowLongOfPSN = kCurrentProcess;
743 } else {
744 localPSN = *pPSN;
745 }
746
747 anErr = GetProcessInformation(&localPSN, &infoRec);
748 if (anErr == noErr)
749 {
750 *pProcessType = infoRec.processType;
751 *pCreator = infoRec.processSignature;
752 }
753
754 return anErr;
755 }//end MoreProcGetProcessTypeSignature
756
757 #include "wx/mac/corefoundation/cfwrappers.h"
758
759 void wxCFDictionary::MakeValidXML()
760 {
761 {
762 CFIndex cfiCount = GetCount();
763 CFTypeRef* pKeys = new CFTypeRef[cfiCount];
764 CFTypeRef* pValues = new CFTypeRef[cfiCount];
765
766 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
767
768 //for plist xml format all dictionary keys must be cfstrings and no values in
769 //the dictionary or subkeys/values can be NULL
770 //Also, CFURLs are not allowed
771 for(CFIndex i = 0; i < cfiCount; ++i)
772 {
773 //must be an array, dictionary, string, bool, or int and cannot be null
774 //and dictionaries can only contain cfstring keys
775 CFTypeRef cfRef = pValues[i];
776 if(!pKeys[i] ||
777 CFGetTypeID(pKeys[i]) != CFStringGetTypeID() ||
778 !cfRef)
779 {
780 Remove(pKeys[i]);
781 --i;
782 --cfiCount;
783 delete[] pKeys;
784 delete[] pValues;
785 pKeys = new CFTypeRef[cfiCount];
786 pValues = new CFTypeRef[cfiCount];
787 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
788 }
789 else if (CFGetTypeID(cfRef) == CFArrayGetTypeID())
790 {
791 CFRetain(cfRef);
792 wxCFArray cfaCurrent(cfRef);
793 cfaCurrent.MakeMutable();
794 cfaCurrent.MakeValidXML();
795 Set(pKeys[i], cfaCurrent);
796 }
797 else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID())
798 {
799 CFRetain(cfRef);
800 wxCFDictionary cfdCurrent(cfRef);
801 cfdCurrent.MakeMutable();
802 cfdCurrent.MakeValidXML();
803 Set(pKeys[i], cfdCurrent);
804 }
805 else if( ( CFGetTypeID(cfRef) == CFStringGetTypeID() ||
806 CFGetTypeID(cfRef) == CFNumberGetTypeID() ||
807 CFGetTypeID(cfRef) == CFBooleanGetTypeID() ) == false )
808 {
809 Remove(pKeys[i]);
810 --i;
811 --cfiCount;
812 delete[] pKeys;
813 delete[] pValues;
814 pKeys = new CFTypeRef[cfiCount];
815 pValues = new CFTypeRef[cfiCount];
816 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
817 }
818 }
819
820 delete[] pValues;
821 delete[] pKeys;
822 }
823 }
824
825 void wxCFArray::MakeValidXML()
826 {
827 {
828 for(CFIndex i = 0; i < GetCount(); ++i)
829 {
830 //must be an array, dictionary, string, bool, or int and cannot be null
831 //and dictionaries can only contain cfstring keys
832 CFTypeRef cfRef = (*this)[i];
833 if(!cfRef)
834 {
835 Remove(i);
836 --i;
837 }
838 else if (CFGetTypeID(cfRef) == CFArrayGetTypeID())
839 {
840 CFRetain(cfRef);
841 wxCFArray cfaCurrent(cfRef);
842 cfaCurrent.MakeMutable();
843 cfaCurrent.MakeValidXML();
844 Set(i, cfaCurrent);
845 }
846 else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID())
847 {
848 CFRetain(cfRef);
849 wxCFDictionary cfdCurrent(cfRef);
850 cfdCurrent.MakeMutable();
851 cfdCurrent.MakeValidXML();
852 Set(i, cfdCurrent);
853 }
854 else if( ( CFGetTypeID(cfRef) == CFStringGetTypeID() ||
855 CFGetTypeID(cfRef) == CFNumberGetTypeID() ||
856 CFGetTypeID(cfRef) == CFBooleanGetTypeID() ) == false )
857 {
858 Remove(i);
859 --i;
860 }
861 }
862 }
863 }
864
865 wxFileType* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
866 {
867 bool bInfoSuccess = false;
868
869 const wxArrayString& asExtensions = ftInfo.GetExtensions();
870 size_t dwFoundIndex = 0;
871 if(!asExtensions.GetCount())
872 {
873 wxLogDebug(wxT("Must have extension to associate with"));
874 }
875
876 //Find and write to Info.plist in main bundle (note that some other
877 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
878 //some apps (non-wx) use the 'plst' resource instead
879 CFBundleRef cfbMain = CFBundleGetMainBundle();
880 if(cfbMain)
881 {
882 UInt32 dwBundleType, dwBundleCreator;
883 CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator);
884
885 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
886 //which will give us the incorrect info.plist path
887 //otherwise it will be 'APPL', or in the case of a framework,
888 //'FMWK'
889 if(dwBundleType == 'APPL')
890 {
891
892 wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain));
893 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
894 wxString sInfoPath;
895 // sInfoPath << wxT("file://");
896 sInfoPath << cfurlBundleLoc.BuildWXString();
897 sInfoPath << wxT("Contents/Info.plist");
898
899 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
900 wxCFDictionary cfdInfo;
901 bool bInfoOpenSuccess = false;
902 wxFile indictfile;
903 if(indictfile.Open(sInfoPath, wxFile::read));
904 {
905 CFIndex cfiBufLen = (CFIndex) indictfile.Length();
906 const UInt8* pBuffer = new UInt8[cfiBufLen];
907 indictfile.Read((void*)pBuffer, cfiBufLen);
908 wxCFData cfdaInDict(pBuffer, cfiBufLen);
909 wxString sError;
910 bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError);
911 if(!bInfoOpenSuccess)
912 wxLogDebug(sError);
913 indictfile.Close();
914 }
915 if(bInfoOpenSuccess)
916 {
917 cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 );
918
919 wxCFArray cfaDocTypes( cfdInfo[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN );
920
921 bool bAddDocTypesArrayToDictionary = cfaDocTypes.IsOk() == false;
922 if(bAddDocTypesArrayToDictionary)
923 cfaDocTypes.Create();
924 else
925 cfaDocTypes.MakeMutable( cfaDocTypes.GetCount() + 1 );
926
927 bool bEntryFound = false;
928
929 //search for duplicate
930 CFIndex i;
931 for(i = 0; i < cfaDocTypes.GetCount(); ++i)
932 {
933 wxCFDictionary cfdDocTypeEntry( cfaDocTypes[i], wxCF_RETAIN );
934
935 //A lot of apps dont do to mime types for some reason
936 //so we go by extensions only
937 wxCFArray cfaExtensions( cfdDocTypeEntry[ wxCFString(wxT("CFBundleTypeExtensions")) ],
938 wxCF_RETAIN );
939
940 if(cfaExtensions.IsOk() == false)
941 continue;
942
943 for(CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt)
944 {
945 for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt)
946 {
947 if(asExtensions[iWXExt] ==
948 wxCFString(cfaExtensions[iExt], wxCF_RETAIN).BuildWXString())
949 {
950 bEntryFound = true;
951 dwFoundIndex = iWXExt;
952 break;
953 }
954 } //end of wxstring array
955
956 if(bEntryFound)
957 break;
958 } //end for cf array
959
960 if(bEntryFound)
961 break;
962 }//end for doctypes
963
964 wxCFDictionary cfdNewEntry;
965
966 if(!ftInfo.GetDescription().empty())
967 {
968 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeName")),
969 wxCFString(ftInfo.GetDescription()) );
970 }
971
972 if(!ftInfo.GetIconFile().empty())
973 {
974 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeIconFile")),
975 wxCFString(ftInfo.GetIconFile()) );
976 }
977
978
979 wxCFArray cfaOSTypes;
980 wxCFArray cfaExtensions;
981 wxCFArray cfaMimeTypes;
982
983
984 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
985 cfaOSTypes.Add( wxCFString(wxT("****")) );
986 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes );
987
988 if(ftInfo.GetExtensionsCount() != 0) //'*' for unrestricted
989 {
990 for(size_t iExtension = 0; iExtension < (size_t)ftInfo.GetExtensionsCount(); ++iExtension)
991 {
992 cfaExtensions.Add( wxCFString( asExtensions[iExtension] ) );
993 }
994
995 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions );
996 }
997
998 if(!ftInfo.GetMimeType().empty())
999 {
1000 cfaMimeTypes.Add( wxCFString(ftInfo.GetMimeType()) );
1001 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes );
1002 }
1003
1004 // Editor - can perform all actions
1005 // Viewer - all actions except manipulation/saving
1006 // None - can perform no actions
1007 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1008
1009 // Is application bundled?
1010 cfdNewEntry.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue );
1011
1012 if(bEntryFound)
1013 cfaDocTypes.Set(i, cfdNewEntry);
1014 else
1015 cfaDocTypes.Add(cfdNewEntry);
1016
1017 //
1018 // set the doc types array in the muted dictionary
1019 //
1020
1021 if(bAddDocTypesArrayToDictionary)
1022 cfdInfo.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes);
1023 else
1024 cfdInfo.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes);
1025
1026 cfdInfo.MakeValidXML();
1027
1028 wxFile outdictfile;
1029 if(outdictfile.Open(sInfoPath, wxFile::write))
1030 {
1031 wxCFData cfdaInfo(cfdInfo.WriteAsXML());
1032 if(cfdaInfo.IsOk())
1033 {
1034 if(outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) !=
1035 (wxFileOffset)cfdaInfo.GetCount())
1036 {
1037 wxLogDebug(wxT("error in writing to file"));
1038 }
1039 else
1040 {
1041 bInfoSuccess = true;
1042 //#if defined(__DARWIN__)
1043 // //force launch services to update its database for the finder
1044 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1045 // if(status != noErr)
1046 // {
1047 // wxLogDebug(wxT("LSRegisterURL Failed."));
1048 // }
1049 //#endif
1050 }
1051 outdictfile.Close();
1052 }
1053 else
1054 {
1055 outdictfile.Close();
1056 wxLogDebug(wxT("Could not read in new dictionary"));
1057 }
1058 }
1059 else
1060 {
1061 wxLogDebug(wxString(wxT("Could not open [")) +
1062 sInfoPath + wxT("] for writing."));
1063 }
1064 }
1065 else
1066 {
1067 wxLogDebug(wxT("No info dictionary in main bundle"));
1068 }
1069 }
1070 else
1071 {
1072 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1073 }
1074 }
1075 else
1076 {
1077 wxLogDebug(wxT("No main bundle"));
1078 }
1079
1080 #if defined(__DARWIN__)
1081 if(!bInfoSuccess)
1082 return NULL;
1083 #endif
1084 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1085 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1086 //the app's signature somehow...
1087
1088 OSType processType,
1089 creator;
1090 OSStatus status = MoreProcGetProcessTypeSignature(NULL,&processType, &creator);
1091
1092 if(status == noErr)
1093 {
1094 Str255 psCreatorName;
1095 FSSpec dummySpec;
1096 status = FindApplication(creator, false, psCreatorName, &dummySpec);
1097
1098 if(status == noErr)
1099 {
1100
1101 //get the file type if it exists -
1102 //if it really does then modify the database then save it,
1103 //otherwise we need to create a whole new entry
1104 wxFileType* pFileType = GetFileTypeFromExtension(asExtensions[dwFoundIndex]);
1105 if(pFileType)
1106 {
1107 ICMapEntry entry;
1108 ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
1109 pFileType->m_impl->m_lIndex, &entry);
1110
1111 entry.creatorAppName = psCreatorName;
1112 entry.fileCreator = creator;
1113
1114 status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
1115 pFileType->m_impl->m_lIndex, &entry);
1116
1117 //success
1118 if(status == noErr)
1119 {
1120 //kICAttrNoChange means we don't care about attributes such as
1121 //locking in the database
1122 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1123 // kICAttrNoChange, (Handle) m_hDatabase);
1124 // if(status == noErr)
1125 return pFileType;
1126 // else
1127 // {
1128 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1129 // }
1130 }
1131 else
1132 {
1133 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed.")));
1134 }
1135
1136 //failure - cleanup
1137 delete pFileType;
1138 }
1139 else
1140 {
1141 //TODO: Maybe force all 3 of these to be non-empty?
1142 Str255 psExtension;
1143 Str255 psMimeType;
1144 Str255 psDescription;
1145
1146 wxMacStringToPascal(ftInfo.GetExtensions()[0], psExtension);
1147 wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType);
1148 wxMacStringToPascal(ftInfo.GetDescription(), psDescription);
1149
1150 Str255 psPostCreatorName;
1151 wxMacStringToPascal(wxT(""), psPostCreatorName);
1152
1153
1154 //add the entry to the database
1155 ICMapEntry entry;
1156 entry.totalLength = sizeof(ICMapEntry);
1157 entry.fixedLength = kICMapFixedLength;
1158 entry.version = 0;
1159 entry.fileType = 0; //TODO: File type?
1160 entry.fileCreator = creator;
1161 entry.postCreator = 0;
1162 entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too?
1163 entry.extension = psExtension;
1164 entry.creatorAppName = psCreatorName;
1165 entry.postAppName = psPostCreatorName;
1166 entry.MIMEType = psMimeType;
1167 entry.entryName = psDescription;
1168
1169 status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry);
1170
1171 if(status == noErr)
1172 {
1173 //kICAttrNoChange means we don't care about attributes such as
1174 //locking in the database
1175 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1176 // kICAttrNoChange, (Handle) m_hDatabase);
1177
1178 //return the entry in the database if successful
1179 // if(status == noErr)
1180 return GetFileTypeFromExtension(ftInfo.GetMimeType());
1181 // else
1182 // {
1183 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1184 // }
1185 }
1186 else
1187 {
1188 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed.")));
1189 }
1190 }
1191 } //end if FindApplcation was successful
1192 else
1193 {
1194 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed.")));
1195 }
1196 } //end if it could obtain app's signature
1197 else
1198 {
1199 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed.")));
1200 }
1201 return NULL;
1202 }
1203
1204 bool
1205 wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType)
1206 {
1207 wxASSERT(pFileType);
1208 bool bInfoSuccess = false;
1209
1210 wxArrayString asExtensions;
1211 pFileType->GetExtensions(asExtensions);
1212
1213 if(!asExtensions.GetCount())
1214 {
1215 wxLogDebug(wxT("Must have extension to disassociate"));
1216 return FALSE;
1217 }
1218
1219 //Find and write to Info.plist in main bundle (note that some other
1220 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1221 //some apps (non-wx) use the 'plst' resource instead
1222 CFBundleRef cfbMain = CFBundleGetMainBundle();
1223 if(cfbMain)
1224 {
1225 UInt32 dwBundleType, dwBundleCreator;
1226 CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator);
1227
1228 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1229 //which will give us the incorrect info.plist path
1230 //otherwise it will be 'APPL', or in the case of a framework,
1231 //'FMWK'
1232 if(dwBundleType == 'APPL')
1233 {
1234
1235 wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain));
1236 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1237 wxString sInfoPath;
1238 // sInfoPath << wxT("file://");
1239 sInfoPath << cfurlBundleLoc.BuildWXString();
1240 sInfoPath << wxT("Contents/Info.plist");
1241
1242 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1243 wxCFDictionary cfdInfo;
1244 bool bInfoOpenSuccess = false;
1245 wxFile indictfile;
1246 if(indictfile.Open(sInfoPath, wxFile::read));
1247 {
1248 CFIndex cfiBufLen = (CFIndex) indictfile.Length();
1249 const UInt8* pBuffer = new UInt8[cfiBufLen];
1250 indictfile.Read((void*)pBuffer, cfiBufLen);
1251 wxCFData cfdaInDict(pBuffer, cfiBufLen);
1252 wxString sError;
1253 bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError);
1254 if(!bInfoOpenSuccess)
1255 wxLogDebug(sError);
1256 indictfile.Close();
1257 }
1258 if(bInfoOpenSuccess)
1259 {
1260 cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 );
1261
1262 wxCFArray cfaDocTypes( cfdInfo[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN );
1263
1264 if(cfaDocTypes.IsOk())
1265 {
1266 bool bEntryFound = false;
1267
1268 //search for duplicate
1269 CFIndex i;
1270 for(i = 0; i < cfaDocTypes.GetCount(); ++i)
1271 {
1272 wxCFDictionary cfdDocTypeEntry( cfaDocTypes[i], wxCF_RETAIN );
1273
1274 //A lot of apps dont do to mime types for some reason
1275 //so we go by extensions only
1276 wxCFArray cfaExtensions( cfdDocTypeEntry[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1277 wxCF_RETAIN );
1278
1279 if(cfaExtensions.IsOk() == false)
1280 continue;
1281
1282 for(CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt)
1283 {
1284 for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt)
1285 {
1286 if(asExtensions[iWXExt] ==
1287 wxCFString(cfaExtensions[iExt], wxCF_RETAIN).BuildWXString())
1288 {
1289 bEntryFound = true;
1290 cfaDocTypes.Remove(i);
1291 cfdInfo.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes );
1292 break;
1293 }
1294 } //end of wxstring array
1295
1296 if(bEntryFound)
1297 break;
1298 } //end for cf array
1299
1300 if(bEntryFound)
1301 break;
1302 }//end for doctypes
1303
1304 if(bEntryFound)
1305 {
1306 cfdInfo.MakeValidXML();
1307
1308 wxFile outdictfile;
1309 if(outdictfile.Open(sInfoPath, wxFile::write))
1310 {
1311 wxCFData cfdaInfo(cfdInfo.WriteAsXML());
1312 if(cfdaInfo.IsOk())
1313 {
1314 if(outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) !=
1315 (wxFileOffset)cfdaInfo.GetCount())
1316 {
1317 wxLogDebug(wxT("error in writing to file"));
1318 }
1319 else
1320 {
1321 bInfoSuccess = true;
1322 //#if defined(__DARWIN__)
1323 // //force launch services to update its database for the finder
1324 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1325 // if(status != noErr)
1326 // {
1327 // wxLogDebug(wxT("LSRegisterURL Failed."));
1328 // }
1329 //#endif
1330 }
1331 outdictfile.Close();
1332 }
1333 else
1334 {
1335 outdictfile.Close();
1336 wxLogDebug(wxT("Could not read in new dictionary"));
1337 }
1338 }
1339 else
1340 {
1341 wxLogDebug(wxString(wxT("Could not open [")) +
1342 sInfoPath + wxT("] for writing."));
1343 }
1344 }
1345 else
1346 {
1347 wxLogDebug(wxT("Entry not found to remove"));
1348 wxString sPrintOut;
1349 wxCFDictionary::PrintOutArray(sPrintOut, (CFArrayRef)(CFTypeRef)cfaDocTypes);
1350 wxLogDebug(sPrintOut);
1351 for(size_t i = 0; i < asExtensions.GetCount(); ++i)
1352 wxLogDebug(asExtensions[i]);
1353 }
1354 }
1355 else
1356 {
1357 wxLogDebug(wxT("No doc types array found"));
1358 wxString sPrintOut; cfdInfo.PrintOut(sPrintOut); wxLogDebug(sPrintOut);
1359 }
1360 }
1361 else
1362 {
1363 wxLogDebug(wxT("No info dictionary in main bundle"));
1364 }
1365 }
1366 else
1367 {
1368 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1369 }
1370 }
1371 else
1372 {
1373 wxLogDebug(wxT("No main bundle"));
1374 }
1375
1376 #if defined(__DARWIN__)
1377 if(!bInfoSuccess)
1378 return NULL;
1379 #endif
1380
1381 //this should be as easy as removing the entry from the database and then saving
1382 //the database
1383 OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
1384 pFileType->m_impl->m_lIndex);
1385
1386 if(status == noErr)
1387 {
1388 //kICAttrNoChange means we don't care about attributes such as
1389 //locking in the database
1390 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1391 // kICAttrNoChange, (Handle) m_hDatabase);
1392
1393 // if(status == noErr)
1394 return TRUE;
1395 // else
1396 // {
1397 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1398 // }
1399
1400 }
1401 else
1402 {
1403 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed.")));
1404 }
1405
1406 return FALSE;
1407 }
1408 /*
1409 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1410 kCFAllocatorDefault,
1411 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1412
1413 // CFShow(cfdInfo);
1414 if(cfwsInfo)
1415 {
1416 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1417 if(bOpened)
1418 {
1419 CFStringRef cfsError;
1420 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1421 cfwsInfo,
1422 kCFPropertyListXMLFormat_v1_0, //100
1423 &cfsError);
1424 if(cfsError && cfiWritten == 0)
1425 {
1426 wxLogDebug(wxCFString(cfsError).BuildWXString());
1427 wxString sMessage;
1428 cfdInfo.PrintOut(sMessage);
1429 wxLogDebug(sMessage);
1430 }
1431 else
1432 {
1433 bInfoSuccess = true;
1434 //#if defined(__DARWIN__)
1435 // //force launch services to update its database for the finder
1436 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1437 // if(status != noErr)
1438 // {
1439 // wxLogDebug(wxT("LSRegisterURL Failed."));
1440 // }
1441 //#endif
1442 }
1443
1444 CFWriteStreamClose(cfwsInfo);
1445
1446 */
1447 #endif //wxUSE_MIMETYPE