]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mimetmac.cpp
fix some possible crashes due to uninitialized variables - thanks to Steve Hartwell...
[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::GetExtensions(wxArrayString& extensions)
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 //entry has period in it
495 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
496 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
497 return true;
498 }
499
500 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
501 {
502 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
503
504 ICMapEntry entry;
505 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
506 (Handle) m_manager->m_hDatabase,
507 m_lIndex, &entry);
508
509 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
510 return true;
511 }
512
513 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
514 {
515 wxString s;
516
517 if (GetMimeType(&s))
518 {
519 mimeTypes.Clear();
520 mimeTypes.Add(s);
521 return TRUE;
522 }
523 else
524 return FALSE;
525 }
526
527 bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
528 {
529 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
530
531 // no such file type or no value or incorrect icon entry
532 return FALSE;
533 }
534
535 bool wxFileTypeImpl::GetDescription(wxString *desc) const
536 {
537 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
538
539 ICMapEntry entry;
540 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
541 (Handle) m_manager->m_hDatabase,
542 m_lIndex, &entry);
543
544 *desc = wxString((char*)entry.entryName, wxConvLocal);
545 return true;
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 wxFileType *
758 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
759 {
760 #if 1
761 wxFAIL_MSG(wxT("Associate not ready for production use"));
762 return NULL;
763 #else
764 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
765 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
766 //the app's signature somehow...
767
768 OSType processType,
769 creator;
770 OSStatus status = MoreProcGetProcessTypeSignature(NULL,&processType, &creator);
771
772 if(status == noErr)
773 {
774 Str255 psCreatorName;
775 FSSpec dummySpec;
776 status = FindApplication(creator, false, psCreatorName, &dummySpec);
777
778 if(status == noErr)
779 {
780
781 //get the file type if it exists -
782 //if it really does then modify the database then save it,
783 //otherwise we need to create a whole new entry
784 wxFileType* pFileType = GetFileTypeFromMimeType(ftInfo.GetMimeType());
785 if(pFileType)
786 {
787 ICMapEntry entry;
788 ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
789 pFileType->m_impl->m_lIndex, &entry);
790
791 entry.creatorAppName = psCreatorName;
792 entry.fileCreator = creator;
793
794 status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
795 pFileType->m_impl->m_lIndex, &entry);
796
797 //success
798 if(status == noErr)
799 return pFileType;
800 else
801 {
802 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed.")));
803 }
804
805 //failure - cleanup
806 delete pFileType;
807 }
808 else
809 {
810 //TODO: Maybe force all 3 of these to be non-empty?
811 Str255 psExtension;
812 Str255 psMimeType;
813 Str255 psDescription;
814
815 wxMacStringToPascal(ftInfo.GetExtensions()[0], psExtension);
816 wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType);
817 wxMacStringToPascal(ftInfo.GetDescription(), psDescription);
818
819 Str255 psPostCreatorName;
820 wxMacStringToPascal(wxT(""), psPostCreatorName);
821
822
823 //add the entry to the database
824 //TODO: Does this work?
825 ICMapEntry entry;
826 entry.totalLength = sizeof(ICMapEntry);
827 entry.fixedLength = kICMapFixedLength;
828 entry.version = 0;
829 entry.fileType = 0; //TODO: File type?
830 entry.fileCreator = creator;
831 entry.postCreator = 0;
832 entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too?
833 entry.extension = psExtension;
834 entry.creatorAppName = psCreatorName;
835 entry.postAppName = psPostCreatorName;
836 entry.MIMEType = psMimeType;
837 entry.entryName = psDescription;
838
839 status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry);
840
841 if(status == noErr)
842 {
843 //kICAttrNoChange means we don't care about attributes such as
844 //locking in the database
845 status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
846 kICAttrNoChange, (Handle) m_hDatabase);
847
848 //return the entry in the database if successful
849 if(status == noErr)
850 return GetFileTypeFromMimeType(ftInfo.GetMimeType());
851 else
852 {
853 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
854 }
855 }
856 else
857 {
858 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed.")));
859 }
860 }
861 } //end if FindApplcation was successful
862 else
863 {
864 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed.")));
865 }
866 } //end if it could obtain app's signature
867 else
868 {
869 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed.")));
870 }
871 return NULL;
872 #endif
873 }
874
875 bool
876 wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType)
877 {
878 #if 1
879 wxFAIL_MSG(wxT("Unassociate not ready for production use"));
880 return FALSE;
881 #else
882 //this should be as easy as removing the entry from the database and then saving
883 //the database
884 OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
885 pFileType->m_impl->m_lIndex);
886
887 if(status == noErr)
888 {
889 //kICAttrNoChange means we don't care about attributes such as
890 //locking in the database
891 status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
892 kICAttrNoChange, (Handle) m_hDatabase);
893
894 if(status == noErr)
895 return TRUE;
896 else
897 {
898 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
899 }
900
901 }
902 else
903 {
904 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed.")));
905 }
906
907 return FALSE;
908 #endif
909 }
910
911 #endif //wxUSE_MIMETYPE