]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/mimetmac.cpp
Applied patch [ 1184295 ] wxDateTimePickerCtrl Create() fix for wxDP_ALLOWNONE
[wxWidgets.git] / src / mac / carbon / mimetmac.cpp
CommitLineData
7dc3cc31
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: mac/mimetype.cpp
c9e227f4
RN
3// Purpose: Mac Carbon implementation for wx mime-related classes
4// Author: Ryan Norton
7dc3cc31 5// Modified by:
c9e227f4 6// Created: 04/16/2005
7dc3cc31 7// RCS-ID: $Id$
c9e227f4
RN
8// Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
9// Licence: wxWindows licence
7dc3cc31
VS
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
7dc3cc31
VS
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
7dc3cc31
VS
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
4b8f9d46
RN
31#if wxUSE_MIMETYPE
32
7dc3cc31
VS
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"
c9e227f4 40#include "wx/mac/private.h" //wxMacMakeStringFromPascal
7dc3cc31
VS
41
42// other standard headers
43#include <ctype.h>
c9e227f4
RN
44#include <InternetConfig.h> //For mime types
45
7dc3cc31 46
c9e227f4
RN
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. */
54OSErr 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. */
83OSErr 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 }
122bail:
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
7baa887c 138OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) {
c9e227f4
RN
139 short rRefNums[kMaxVols];
140 long i, volCount;
141 DTPBRec desktopPB;
142 OSErr err;
c9e227f4
RN
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
7baa887c
RN
182//yeah, duplicated code
183pascal 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
315inline 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
321class WXDLLEXPORT wxIcon;
322
323bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt)
324{
325 return FALSE;
326}
327
328bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index)
329{
330 return FALSE;
331}
332
333bool 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
343bool
344wxFileTypeImpl::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
c9e227f4 373wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
7dc3cc31 374{
7baa887c
RN
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
422wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
423{
424 wxASSERT(m_manager);
425
c9e227f4
RN
426 if(!m_manager)
427 return wxEmptyString;
428
429 if(verb == wxT("open"))
7dc3cc31 430 {
c9e227f4 431 ICMapEntry entry;
c02af653
RN
432 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
433 (Handle) m_manager->m_hDatabase,
434 m_lIndex, &entry);
c9e227f4 435
c9e227f4
RN
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
7baa887c
RN
439
440 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
441 // on here.
c9e227f4 442 Str255 outName;
7baa887c
RN
443 FSSpec outSpec;
444 if(FindApplication(entry.fileCreator, false, outName, &outSpec) != noErr)
c9e227f4
RN
445 return wxEmptyString;
446
7baa887c
RN
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 }
7dc3cc31 482 }
c9e227f4
RN
483 return wxEmptyString;
484}
7baa887c 485#endif //!DARWIN
c9e227f4
RN
486
487bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
488{
489 if(!m_manager)
490 return false;
491
492 ICMapEntry entry;
c02af653
RN
493 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
494 (Handle) m_manager->m_hDatabase,
495 m_lIndex, &entry);
c9e227f4
RN
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
503bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
504{
505 if(!m_manager)
506 return false;
507
508 ICMapEntry entry;
c02af653
RN
509 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
510 (Handle) m_manager->m_hDatabase,
511 m_lIndex, &entry);
c9e227f4
RN
512
513 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
514 return true;
7dc3cc31
VS
515}
516
4d2976ad
VS
517bool 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
da0766ab 531bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
7dc3cc31
VS
532{
533 // no such file type or no value or incorrect icon entry
534 return FALSE;
535}
536
537bool wxFileTypeImpl::GetDescription(wxString *desc) const
538{
c9e227f4
RN
539 if(!m_manager)
540 return false;
541
542 ICMapEntry entry;
c02af653
RN
543 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
544 (Handle) m_manager->m_hDatabase,
545 m_lIndex, &entry);
c9e227f4
RN
546
547 *desc = wxString((char*)entry.entryName, wxConvLocal);
548 return true;
7dc3cc31
VS
549}
550
c9e227f4 551size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
e40298d5 552 const wxFileType::MessageParameters& params) const
007ae8d0 553{
0fe3b231 554 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
f040060e
GD
555 return 0;
556}
557
c9e227f4 558void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
f040060e 559{
c9e227f4 560 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
007ae8d0 561
c9e227f4
RN
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)
7dc3cc31 570 {
7baa887c 571 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
c9e227f4
RN
572 wxASSERT( false );
573 return;
7dc3cc31 574 }
c9e227f4
RN
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)
7dc3cc31 584 {
c9e227f4 585 ClearData();
7baa887c 586 wxLogDebug(wxT("Corrupt Mime Database!"));
c9e227f4 587 return;
7dc3cc31 588 }
c9e227f4
RN
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 );
7baa887c
RN
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 */
c9e227f4
RN
623}
624
625void wxMimeTypesManagerImpl::ClearData()
626{
627 if(m_hIC != NULL)
7dc3cc31 628 {
c9e227f4
RN
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;
7dc3cc31 633 }
c9e227f4
RN
634}
635
636// extension -> file type
637wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
638{
639 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
640
c9e227f4
RN
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)
7dc3cc31 647 {
c9e227f4
RN
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 }
7dc3cc31 660 }
c9e227f4
RN
661
662 return pFileType;
7dc3cc31
VS
663}
664
665// MIME type -> extension -> file type
c9e227f4 666wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
7dc3cc31 667{
c9e227f4
RN
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;
7dc3cc31
VS
693}
694
695size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
696{
c9e227f4 697 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
7dc3cc31 698
c9e227f4
RN
699 //low level functions - iterate through the database
700 ICMapEntry entry;
701
702 long pos;
7baa887c 703 long lStartCount = (long) mimetypes.GetCount();
c9e227f4
RN
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
7baa887c 712 return mimetypes.GetCount() - lStartCount;
7dc3cc31
VS
713}
714
7baa887c
RN
715
716pascal 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
007ae8d0
GD
746wxFileType *
747wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
748{
7baa887c
RN
749#if 1
750 wxFAIL_MSG(wxT("Associate not ready for production use"));
751 return NULL;
752#else
c9e227f4
RN
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...
007ae8d0 756
7baa887c
RN
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 }
007ae8d0 860 return NULL;
7baa887c 861#endif
007ae8d0 862}
f040060e
GD
863
864bool
7baa887c 865wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType)
f040060e 866{
7baa887c
RN
867#if 1
868 wxFAIL_MSG(wxT("Unassociate not ready for production use"));
869 return FALSE;
870#else
c9e227f4
RN
871 //this should be as easy as removing the entry from the database and then saving
872 //the database
7baa887c
RN
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
f040060e 896 return FALSE;
7baa887c 897#endif
f040060e
GD
898}
899
4b8f9d46 900#endif //wxUSE_MIMETYPE