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