]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mimetmac.cpp
8ff9ad041650bef0dbb7ce26c6128d1b59e99453
[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 return FALSE;
326 }
327
328 bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index)
329 {
330 return FALSE;
331 }
332
333 bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
334 const wxFileType::MessageParameters& params) const
335 {
336 wxString cmd = GetCommand(wxT("open"));
337
338 *openCmd = wxFileType::ExpandCommand(cmd, params);
339
340 return !openCmd->empty();
341 }
342
343 bool
344 wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
345 const wxFileType::MessageParameters& params)
346 const
347 {
348 wxString cmd = GetCommand(wxT("print"));
349
350 *printCmd = wxFileType::ExpandCommand(cmd, params);
351
352 return !printCmd->empty();
353 }
354
355 //
356 // Internet Config vs. Launch Services
357 //
358 // From OS 8 on there was internet config...
359 // However, OSX and its finder does not use info
360 // from Internet Config at all - the Internet Config
361 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
362 // OR REGISTERED THROUGH INTERNET CONFIG
363 //
364 // Therefore on OSX in order for the open command to be useful
365 // we need to go straight to launch services
366 //
367
368 #if defined(__DARWIN__)
369
370 //on darwin, use launch services
371 #include "LaunchServices.h"
372
373 wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
374 {
375 wxASSERT(m_manager);
376
377 if(!m_manager)
378 return wxEmptyString;
379
380 if(verb == wxT("open"))
381 {
382 ICMapEntry entry;
383 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
384 (Handle) m_manager->m_hDatabase,
385 m_lIndex, &entry);
386
387 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
388 sCurrentExtension = sCurrentExtension.Right(sCurrentExtension.Length()-1 );
389
390 //type, creator, ext, roles, outapp (FSRef), outappurl
391 CFURLRef cfurlAppPath;
392 OSStatus status = LSGetApplicationForInfo (kLSUnknownType,
393 kLSUnknownCreator,
394 wxMacCFStringHolder(sCurrentExtension, wxLocale::GetSystemEncoding()),
395 kLSRolesAll,
396 NULL,
397 &cfurlAppPath);
398
399 if(status == noErr)
400 {
401 CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(cfurlAppPath, kCFURLPOSIXPathStyle);
402 CFRelease(cfurlAppPath);
403
404 //PHEW! Success!
405 if(cfsUnixPath)
406 return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding());
407 }
408 else
409 {
410 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
411 __LINE__,
412 wxT("LSGetApplicationForInfo failed."),
413 (int)status));
414 }
415 }
416
417 return wxEmptyString;
418 }
419
420 #else //carbon/classic implementation
421
422 wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
423 {
424 wxASSERT(m_manager);
425
426 if(!m_manager)
427 return wxEmptyString;
428
429 if(verb == wxT("open"))
430 {
431 ICMapEntry entry;
432 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
433 (Handle) m_manager->m_hDatabase,
434 m_lIndex, &entry);
435
436 //The entry in the mimetype database only contains the app
437 //that's registered - it may not exist... we need to remap the creator
438 //type and find the right application
439
440 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
441 // on here.
442 Str255 outName;
443 FSSpec outSpec;
444 if(FindApplication(entry.fileCreator, false, outName, &outSpec) != noErr)
445 return wxEmptyString;
446
447 Handle outPathHandle;
448 short outPathSize;
449 OSErr err = FSpGetFullPath(&outSpec, &outPathSize, &outPathHandle);
450
451 if(err == noErr)
452 {
453 char* szPath = *outPathHandle;
454 wxString sClassicPath(szPath, wxConvLocal, outPathSize);
455 #if defined(__DARWIN__)
456 //Classic Path --> Unix (OSX) Path
457 CFURLRef finalURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
458 wxMacCFStringHolder(sClassicPath, wxLocale::GetSystemEncoding()),
459 kCFURLHFSPathStyle,
460 false); //false == not a directory
461
462 //clean up memory from the classic path handle
463 DisposeHandle(outPathHandle);
464
465 if(finalURL)
466 {
467 CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(finalURL, kCFURLPOSIXPathStyle);
468 CFRelease(finalURL);
469
470 //PHEW! Success!
471 if(cfsUnixPath)
472 return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding());
473 }
474 #else //classic HFS path acceptable
475 return sClassicPath;
476 #endif
477 }
478 else
479 {
480 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus)err);
481 }
482 }
483 return wxEmptyString;
484 }
485 #endif //!DARWIN
486
487 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
488 {
489 if(!m_manager)
490 return false;
491
492 ICMapEntry entry;
493 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
494 (Handle) m_manager->m_hDatabase,
495 m_lIndex, &entry);
496
497 //entry has period in it
498 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
499 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
500 return true;
501 }
502
503 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
504 {
505 if(!m_manager)
506 return false;
507
508 ICMapEntry entry;
509 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
510 (Handle) m_manager->m_hDatabase,
511 m_lIndex, &entry);
512
513 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
514 return true;
515 }
516
517 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
518 {
519 wxString s;
520
521 if (GetMimeType(&s))
522 {
523 mimeTypes.Clear();
524 mimeTypes.Add(s);
525 return TRUE;
526 }
527 else
528 return FALSE;
529 }
530
531 bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
532 {
533 // no such file type or no value or incorrect icon entry
534 return FALSE;
535 }
536
537 bool wxFileTypeImpl::GetDescription(wxString *desc) const
538 {
539 if(!m_manager)
540 return false;
541
542 ICMapEntry entry;
543 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
544 (Handle) m_manager->m_hDatabase,
545 m_lIndex, &entry);
546
547 *desc = wxString((char*)entry.entryName, wxConvLocal);
548 return true;
549 }
550
551 size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
552 const wxFileType::MessageParameters& params) const
553 {
554 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
555 return 0;
556 }
557
558 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
559 {
560 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
561
562 //start internet config - log if there's an error
563 //the second param is the signature of the application, also known
564 //as resource ID 0. However, as per some recent discussions, we may not
565 //have a signature for this app, so a generic 'APPL' which is the executable
566 //type will work for now
567 OSStatus status = ICStart( (ICInstance*) &m_hIC, 'APPL');
568
569 if(status != noErr)
570 {
571 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
572 wxASSERT( false );
573 return;
574 }
575
576 ICAttr attr;
577 m_hDatabase = (void**) NewHandle(0);
578 status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase );
579
580 //the database file can be corrupt (on OSX its
581 //~/Library/Preferences/com.apple.internetconfig.plist)
582 //- bail if it is
583 if(status != noErr)
584 {
585 ClearData();
586 wxLogDebug(wxT("Corrupt Mime Database!"));
587 return;
588 }
589
590 //obtain the number of entries in the map
591 status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount );
592 wxASSERT( status == noErr );
593 /*
594 //debug stuff
595 ICMapEntry entry;
596 long pos;
597
598 for(long i = 1; i <= m_lCount; ++i)
599 {
600 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
601
602 if(status == noErr)
603 {
604 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
605 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
606 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
607
608 wxFileTypeImpl impl;
609 impl.Init(this, pos);
610
611 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
612 {
613 wxString cmd;
614 impl.GetOpenCommand (&cmd,
615 wxFileType::MessageParameters (wxT("http://www.google.com"),
616 _T("")));
617
618 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
619 }
620 }
621 }
622 */
623 }
624
625 void wxMimeTypesManagerImpl::ClearData()
626 {
627 if(m_hIC != NULL)
628 {
629 DisposeHandle((Handle)m_hDatabase);
630 //this can return an error, but we don't really care that much about it
631 ICStop( (ICInstance) m_hIC );
632 m_hIC = NULL;
633 }
634 }
635
636 // extension -> file type
637 wxFileType* 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 wxFileType* pFileType;
644 long pos;
645
646 for(long i = 1; i <= m_lCount; ++i)
647 {
648 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
649
650 if(status == noErr)
651 {
652 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
653 if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it
654 {
655 pFileType = new wxFileType();
656 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
657 break;
658 }
659 }
660 }
661
662 return pFileType;
663 }
664
665 // MIME type -> extension -> file type
666 wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
667 {
668 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
669
670 //low level functions - iterate through the database
671 ICMapEntry entry;
672 wxFileType* pFileType;
673
674 long pos;
675
676 for(long i = 1; i <= m_lCount; ++i)
677 {
678 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
679 wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) );
680
681 if(status == noErr)
682 {
683 if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType)
684 {
685 pFileType = new wxFileType();
686 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
687 break;
688 }
689 }
690 }
691
692 return pFileType;
693 }
694
695 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
696 {
697 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
698
699 //low level functions - iterate through the database
700 ICMapEntry entry;
701
702 long pos;
703 long lStartCount = (long) mimetypes.GetCount();
704
705 for(long i = 1; i <= m_lCount; ++i)
706 {
707 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
708 if( status == noErr )
709 mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) );
710 }
711
712 return mimetypes.GetCount() - lStartCount;
713 }
714
715
716 pascal OSStatus MoreProcGetProcessTypeSignature(
717 const ProcessSerialNumberPtr pPSN,
718 OSType *pProcessType,
719 OSType *pCreator)
720 {
721 OSStatus anErr = noErr;
722 ProcessInfoRec infoRec;
723 ProcessSerialNumber localPSN;
724
725 infoRec.processInfoLength = sizeof(ProcessInfoRec);
726 infoRec.processName = nil;
727 infoRec.processAppSpec = nil;
728
729 if ( pPSN == nil ) {
730 localPSN.highLongOfPSN = 0;
731 localPSN.lowLongOfPSN = kCurrentProcess;
732 } else {
733 localPSN = *pPSN;
734 }
735
736 anErr = GetProcessInformation(&localPSN, &infoRec);
737 if (anErr == noErr)
738 {
739 *pProcessType = infoRec.processType;
740 *pCreator = infoRec.processSignature;
741 }
742
743 return anErr;
744 }//end MoreProcGetProcessTypeSignature
745
746 wxFileType *
747 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
748 {
749 #if 1
750 wxFAIL_MSG(wxT("Associate not ready for production use"));
751 return NULL;
752 #else
753 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
754 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
755 //the app's signature somehow...
756
757 OSType processType,
758 creator;
759 OSStatus status = MoreProcGetProcessTypeSignature(NULL,&processType, &creator);
760
761 if(status == noErr)
762 {
763 Str255 psCreatorName;
764 FSSpec dummySpec;
765 status = FindApplication(creator, false, psCreatorName, &dummySpec);
766
767 if(status == noErr)
768 {
769
770 //get the file type if it exists -
771 //if it really does then modify the database then save it,
772 //otherwise we need to create a whole new entry
773 wxFileType* pFileType = GetFileTypeFromMimeType(ftInfo.GetMimeType());
774 if(pFileType)
775 {
776 ICMapEntry entry;
777 ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
778 pFileType->m_impl->m_lIndex, &entry);
779
780 entry.creatorAppName = psCreatorName;
781 entry.fileCreator = creator;
782
783 status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
784 pFileType->m_impl->m_lIndex, &entry);
785
786 //success
787 if(status == noErr)
788 return pFileType;
789 else
790 {
791 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed.")));
792 }
793
794 //failure - cleanup
795 delete pFileType;
796 }
797 else
798 {
799 //TODO: Maybe force all 3 of these to be non-empty?
800 Str255 psExtension;
801 Str255 psMimeType;
802 Str255 psDescription;
803
804 wxMacStringToPascal(ftInfo.GetExtensions()[0], psExtension);
805 wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType);
806 wxMacStringToPascal(ftInfo.GetDescription(), psDescription);
807
808 Str255 psPostCreatorName;
809 wxMacStringToPascal(wxT(""), psPostCreatorName);
810
811
812 //add the entry to the database
813 //TODO: Does this work?
814 ICMapEntry entry;
815 entry.totalLength = sizeof(ICMapEntry);
816 entry.fixedLength = kICMapFixedLength;
817 entry.version = 0;
818 entry.fileType = 0; //TODO: File type?
819 entry.fileCreator = creator;
820 entry.postCreator = 0;
821 entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too?
822 entry.extension = psExtension;
823 entry.creatorAppName = psCreatorName;
824 entry.postAppName = psPostCreatorName;
825 entry.MIMEType = psMimeType;
826 entry.entryName = psDescription;
827
828 status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry);
829
830 if(status == noErr)
831 {
832 //kICAttrNoChange means we don't care about attributes such as
833 //locking in the database
834 status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
835 kICAttrNoChange, (Handle) m_hDatabase);
836
837 //return the entry in the database if successful
838 if(status == noErr)
839 return GetFileTypeFromMimeType(ftInfo.GetMimeType());
840 else
841 {
842 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
843 }
844 }
845 else
846 {
847 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed.")));
848 }
849 }
850 } //end if FindApplcation was successful
851 else
852 {
853 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed.")));
854 }
855 } //end if it could obtain app's signature
856 else
857 {
858 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed.")));
859 }
860 return NULL;
861 #endif
862 }
863
864 bool
865 wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType)
866 {
867 #if 1
868 wxFAIL_MSG(wxT("Unassociate not ready for production use"));
869 return FALSE;
870 #else
871 //this should be as easy as removing the entry from the database and then saving
872 //the database
873 OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
874 pFileType->m_impl->m_lIndex);
875
876 if(status == noErr)
877 {
878 //kICAttrNoChange means we don't care about attributes such as
879 //locking in the database
880 status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
881 kICAttrNoChange, (Handle) m_hDatabase);
882
883 if(status == noErr)
884 return TRUE;
885 else
886 {
887 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
888 }
889
890 }
891 else
892 {
893 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed.")));
894 }
895
896 return FALSE;
897 #endif
898 }
899
900 #endif //wxUSE_MIMETYPE