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