]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/mimetmac.cpp
patch 1306473
[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 420 //PHEW! Success!
7db33dd7 421 //Since a filename might have spaces in it, so surround it with quotes
7baa887c 422 if(cfsUnixPath)
7db33dd7 423 return wxString(wxT("'")) + wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding()) + wxString(wxT("'"));
7baa887c
RN
424 }
425 else
426 {
cb9c71ec
WS
427 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
428 __LINE__,
7baa887c 429 wxT("LSGetApplicationForInfo failed."),
cb9c71ec 430 (int)status));
7baa887c
RN
431 }
432 }
cb9c71ec 433
7baa887c
RN
434 return wxEmptyString;
435}
436
437#else //carbon/classic implementation
438
439wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
440{
03271fa7 441 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
cb9c71ec 442
c9e227f4 443 if(verb == wxT("open"))
7dc3cc31 444 {
c9e227f4 445 ICMapEntry entry;
cb9c71ec
WS
446 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
447 (Handle) m_manager->m_hDatabase,
c02af653 448 m_lIndex, &entry);
cb9c71ec 449
c9e227f4
RN
450 //The entry in the mimetype database only contains the app
451 //that's registered - it may not exist... we need to remap the creator
452 //type and find the right application
cb9c71ec 453
7baa887c
RN
454 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
455 // on here.
c9e227f4 456 Str255 outName;
7baa887c
RN
457 FSSpec outSpec;
458 if(FindApplication(entry.fileCreator, false, outName, &outSpec) != noErr)
c9e227f4 459 return wxEmptyString;
cb9c71ec 460
7baa887c
RN
461 Handle outPathHandle;
462 short outPathSize;
463 OSErr err = FSpGetFullPath(&outSpec, &outPathSize, &outPathHandle);
cb9c71ec 464
7baa887c
RN
465 if(err == noErr)
466 {
467 char* szPath = *outPathHandle;
468 wxString sClassicPath(szPath, wxConvLocal, outPathSize);
469#if defined(__DARWIN__)
470 //Classic Path --> Unix (OSX) Path
cb9c71ec
WS
471 CFURLRef finalURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
472 wxMacCFStringHolder(sClassicPath, wxLocale::GetSystemEncoding()),
7baa887c
RN
473 kCFURLHFSPathStyle,
474 false); //false == not a directory
475
476 //clean up memory from the classic path handle
477 DisposeHandle(outPathHandle);
cb9c71ec 478
7baa887c
RN
479 if(finalURL)
480 {
481 CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(finalURL, kCFURLPOSIXPathStyle);
482 CFRelease(finalURL);
cb9c71ec 483
7baa887c
RN
484 //PHEW! Success!
485 if(cfsUnixPath)
486 return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding());
487 }
488#else //classic HFS path acceptable
489 return sClassicPath;
490#endif
491 }
492 else
493 {
494 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus)err);
495 }
7dc3cc31 496 }
c9e227f4
RN
497 return wxEmptyString;
498}
7baa887c 499#endif //!DARWIN
c9e227f4 500
0be2eb2f
RN
501bool wxFileTypeImpl::GetDescription(wxString *desc) const
502{
503 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
cb9c71ec 504
0be2eb2f 505 ICMapEntry entry;
cb9c71ec
WS
506 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
507 (Handle) m_manager->m_hDatabase,
0be2eb2f 508 m_lIndex, &entry);
cb9c71ec 509
0be2eb2f
RN
510 *desc = wxMacMakeStringFromPascal(entry.entryName);
511 return true;
512}
513
c9e227f4
RN
514bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
515{
03271fa7 516 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
cb9c71ec 517
c9e227f4 518 ICMapEntry entry;
cb9c71ec
WS
519 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
520 (Handle) m_manager->m_hDatabase,
c02af653 521 m_lIndex, &entry);
cb9c71ec 522
c9e227f4
RN
523 //entry has period in it
524 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
525 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
526 return true;
527}
528
529bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
530{
03271fa7 531 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
cb9c71ec 532
c9e227f4 533 ICMapEntry entry;
cb9c71ec
WS
534 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
535 (Handle) m_manager->m_hDatabase,
c02af653 536 m_lIndex, &entry);
cb9c71ec 537
c9e227f4
RN
538 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
539 return true;
7dc3cc31
VS
540}
541
4d2976ad
VS
542bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
543{
544 wxString s;
cb9c71ec 545
4d2976ad
VS
546 if (GetMimeType(&s))
547 {
548 mimeTypes.Clear();
549 mimeTypes.Add(s);
cb9c71ec 550 return true;
4d2976ad 551 }
cb9c71ec
WS
552 else
553 return false;
4d2976ad
VS
554}
555
da0766ab 556bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
7dc3cc31 557{
03271fa7
RN
558 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
559
7dc3cc31 560 // no such file type or no value or incorrect icon entry
cb9c71ec 561 return false;
7dc3cc31
VS
562}
563
c9e227f4 564size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
e40298d5 565 const wxFileType::MessageParameters& params) const
007ae8d0 566{
03271fa7
RN
567 wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") );
568
569 wxString sCommand;
570 size_t ulCount = 0;
cb9c71ec 571
03271fa7
RN
572 if(GetOpenCommand(&sCommand, params))
573 {
574 verbs->Add(wxString(wxT("open")));
575 commands->Add(sCommand);
576 ++ulCount;
577 }
cb9c71ec 578
03271fa7 579 return ulCount;
f040060e
GD
580}
581
c9e227f4 582void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
f040060e 583{
c9e227f4 584 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
007ae8d0 585
5af840f1
RN
586 //some apps (non-wx) use the 'plst' resource instead
587/*
588 CFBundleRef cfbMain = CFBundleGetMainBundle();
589 wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
590 wxString sLog;
591 cfdInfo.PrintOut(sLog);
592 wxLogDebug(sLog);
593*/
594
c9e227f4
RN
595 //start internet config - log if there's an error
596 //the second param is the signature of the application, also known
597 //as resource ID 0. However, as per some recent discussions, we may not
cb9c71ec 598 //have a signature for this app, so a generic 'APPL' which is the executable
c9e227f4 599 //type will work for now
cb9c71ec
WS
600 OSStatus status = ICStart( (ICInstance*) &m_hIC, 'APPL');
601
c9e227f4 602 if(status != noErr)
7dc3cc31 603 {
7baa887c 604 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
c9e227f4 605 wxASSERT( false );
03271fa7 606 m_hIC = NULL;
c9e227f4 607 return;
7dc3cc31 608 }
cb9c71ec 609
c9e227f4
RN
610 ICAttr attr;
611 m_hDatabase = (void**) NewHandle(0);
612 status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase );
613
614 //the database file can be corrupt (on OSX its
615 //~/Library/Preferences/com.apple.internetconfig.plist)
616 //- bail if it is
617 if(status != noErr)
7dc3cc31 618 {
c9e227f4 619 ClearData();
7baa887c 620 wxLogDebug(wxT("Corrupt Mime Database!"));
c9e227f4 621 return;
7dc3cc31 622 }
c9e227f4
RN
623
624 //obtain the number of entries in the map
625 status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount );
626 wxASSERT( status == noErr );
7baa887c
RN
627 /*
628 //debug stuff
629 ICMapEntry entry;
630 long pos;
cb9c71ec 631
7baa887c
RN
632 for(long i = 1; i <= m_lCount; ++i)
633 {
634 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
cb9c71ec 635
7baa887c 636 if(status == noErr)
cb9c71ec 637 {
7baa887c
RN
638 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
639 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
640 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
cb9c71ec 641
7baa887c
RN
642 wxFileTypeImpl impl;
643 impl.Init(this, pos);
cb9c71ec 644
7baa887c
RN
645 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
646 {
647 wxString cmd;
cb9c71ec
WS
648 impl.GetOpenCommand (&cmd,
649 wxFileType::MessageParameters (wxT("http://www.google.com")));
7baa887c
RN
650
651 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
652 }
653 }
654 }
655 */
c9e227f4
RN
656}
657
658void wxMimeTypesManagerImpl::ClearData()
659{
660 if(m_hIC != NULL)
7dc3cc31 661 {
c9e227f4
RN
662 DisposeHandle((Handle)m_hDatabase);
663 //this can return an error, but we don't really care that much about it
664 ICStop( (ICInstance) m_hIC );
665 m_hIC = NULL;
7dc3cc31 666 }
c9e227f4
RN
667}
668
03271fa7
RN
669//
670// Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
671// A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
672//
673
c9e227f4
RN
674// extension -> file type
675wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
676{
677 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
cb9c71ec
WS
678
679 //low level functions - iterate through the database
c9e227f4 680 ICMapEntry entry;
c9e227f4 681 long pos;
cb9c71ec 682
c9e227f4 683 for(long i = 1; i <= m_lCount; ++i)
7dc3cc31 684 {
c9e227f4 685 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
cb9c71ec 686
c9e227f4 687 if(status == noErr)
cb9c71ec 688 {
c9e227f4
RN
689 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
690 if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it
691 {
03271fa7 692 wxFileType* pFileType = new wxFileType();
c9e227f4 693 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
03271fa7 694 return pFileType;
c9e227f4
RN
695 }
696 }
7dc3cc31 697 }
cb9c71ec
WS
698
699 return NULL;
7dc3cc31
VS
700}
701
702// MIME type -> extension -> file type
c9e227f4 703wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
7dc3cc31 704{
c9e227f4
RN
705 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
706
cb9c71ec 707 //low level functions - iterate through the database
c9e227f4 708 ICMapEntry entry;
c9e227f4 709 long pos;
cb9c71ec 710
c9e227f4
RN
711 for(long i = 1; i <= m_lCount; ++i)
712 {
713 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
714 wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) );
cb9c71ec 715
c9e227f4 716 if(status == noErr)
cb9c71ec 717 {
c9e227f4
RN
718 if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType)
719 {
03271fa7 720 wxFileType* pFileType = new wxFileType();
c9e227f4 721 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
03271fa7 722 return pFileType;
c9e227f4
RN
723 }
724 }
725 }
cb9c71ec 726
03271fa7 727 return NULL;
7dc3cc31
VS
728}
729
730size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
731{
c9e227f4 732 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
7dc3cc31 733
cb9c71ec 734 //low level functions - iterate through the database
c9e227f4 735 ICMapEntry entry;
c9e227f4 736 long pos;
cb9c71ec 737
7baa887c 738 long lStartCount = (long) mimetypes.GetCount();
cb9c71ec 739
c9e227f4
RN
740 for(long i = 1; i <= m_lCount; ++i)
741 {
742 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
743 if( status == noErr )
744 mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) );
745 }
cb9c71ec 746
7baa887c 747 return mimetypes.GetCount() - lStartCount;
7dc3cc31
VS
748}
749
7baa887c
RN
750
751pascal OSStatus MoreProcGetProcessTypeSignature(
752 const ProcessSerialNumberPtr pPSN,
753 OSType *pProcessType,
754 OSType *pCreator)
755{
cb9c71ec 756 OSStatus anErr = noErr;
7baa887c
RN
757 ProcessInfoRec infoRec;
758 ProcessSerialNumber localPSN;
cb9c71ec 759
7baa887c
RN
760 infoRec.processInfoLength = sizeof(ProcessInfoRec);
761 infoRec.processName = nil;
762 infoRec.processAppSpec = nil;
763
764 if ( pPSN == nil ) {
765 localPSN.highLongOfPSN = 0;
766 localPSN.lowLongOfPSN = kCurrentProcess;
767 } else {
768 localPSN = *pPSN;
769 }
cb9c71ec 770
7baa887c
RN
771 anErr = GetProcessInformation(&localPSN, &infoRec);
772 if (anErr == noErr)
773 {
774 *pProcessType = infoRec.processType;
775 *pCreator = infoRec.processSignature;
776 }
cb9c71ec 777
7baa887c
RN
778 return anErr;
779}//end MoreProcGetProcessTypeSignature
780
5af840f1
RN
781//
782//
783// TODO: clean this up, its messy
784//
785//
786//
787
788#include "wx/mac/corefoundation/cfstring.h"
789#include "wx/intl.h" //wxLocale for wxCFString
790
791#define wxCF_RELEASE true
792#define wxCF_RETAIN false
793
794// ----------------------------------------------------------------------------
795// wxCFDictionary
796// ----------------------------------------------------------------------------
797
798class wxCFDictionary
dd9b4b7f 799{
5af840f1
RN
800public:
801 wxCFDictionary(CFTypeRef ref, bool bRetain = wxCF_RELEASE)
802 {
803 m_cfmdRef = (CFMutableDictionaryRef) ref;
804 if(bRetain == wxCF_RETAIN && ref)
805 CFRetain(ref);
806 }
cb9c71ec 807
5af840f1
RN
808 wxCFDictionary(CFIndex cfiSize = 0)
809 {
810 CFDictionaryKeyCallBacks kcbs;
811 CFDictionaryValueCallBacks vcbs;
812 BuildKeyCallbacks(&kcbs);
813 BuildValueCallbacks(&vcbs);
814
815 m_cfmdRef = CFDictionaryCreateMutable(
816 kCFAllocatorDefault, cfiSize, &kcbs, &vcbs);
817
818 }
cb9c71ec
WS
819
820 ~wxCFDictionary()
821 { Clear(); }
822
5af840f1
RN
823 void Clear()
824 {if(m_cfmdRef) CFRelease(m_cfmdRef);}
cb9c71ec 825
5af840f1 826 static const void* RetainProc(CFAllocatorRef, const void* v)
cb9c71ec
WS
827 { return (const void*) CFRetain(v); }
828
5af840f1 829 static void ReleaseProc(CFAllocatorRef, const void* v)
cb9c71ec 830 { CFRelease(v); }
5af840f1
RN
831
832 void MakeMutable(CFIndex cfiSize = 0)
833 {
834 CFDictionaryRef oldref = (CFDictionaryRef) m_cfmdRef;
cb9c71ec 835
5af840f1
RN
836 m_cfmdRef = CFDictionaryCreateMutableCopy(
837 kCFAllocatorDefault,
cb9c71ec 838 cfiSize,
5af840f1 839 oldref);
cb9c71ec 840
5af840f1
RN
841 CFRelease(oldref);
842 }
cb9c71ec 843
5af840f1
RN
844 void BuildKeyCallbacks(CFDictionaryKeyCallBacks* pCbs)
845 {
846 pCbs->version = 0;
847 pCbs->retain = RetainProc;
848 pCbs->release = ReleaseProc;
849 pCbs->copyDescription = NULL;
850 pCbs->equal = NULL;
851 pCbs->hash = NULL;
852 }
853
854 void BuildValueCallbacks(CFDictionaryValueCallBacks* pCbs)
855 {
856 pCbs->version = 0;
857 pCbs->retain = RetainProc;
858 pCbs->release = ReleaseProc;
859 pCbs->copyDescription = NULL;
860 pCbs->equal = NULL;
861 }
862
863 operator CFTypeRef () const
cb9c71ec
WS
864 { return (CFTypeRef)m_cfmdRef; }
865
5af840f1 866 CFDictionaryRef GetCFDictionary() const
cb9c71ec 867 { return (CFDictionaryRef)m_cfmdRef; }
5af840f1
RN
868
869 CFMutableDictionaryRef GetCFMutableDictionary()
cb9c71ec 870 { return (CFMutableDictionaryRef) m_cfmdRef; }
5af840f1
RN
871
872 CFTypeRef operator [] (CFTypeRef cftEntry) const
cb9c71ec 873 {
5af840f1 874 wxASSERT(IsValid());
cb9c71ec
WS
875 return (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef)m_cfmdRef, cftEntry);
876 }
877
5af840f1 878 CFIndex GetCount() const
cb9c71ec 879 {
5af840f1 880 wxASSERT(IsValid());
cb9c71ec 881 return CFDictionaryGetCount((CFDictionaryRef)m_cfmdRef);
5af840f1 882 }
cb9c71ec 883
5af840f1
RN
884 void Add(CFTypeRef cftKey, CFTypeRef cftValue)
885 {
886 wxASSERT(IsValid());
887 wxASSERT(Exists(cftKey) == false);
cb9c71ec 888 CFDictionaryAddValue(m_cfmdRef, cftKey, cftValue);
5af840f1 889 }
cb9c71ec 890
5af840f1 891 void Remove(CFTypeRef cftKey)
cb9c71ec 892 {
5af840f1
RN
893 wxASSERT(IsValid());
894 wxASSERT(Exists(cftKey));
cb9c71ec 895 CFDictionaryRemoveValue(m_cfmdRef, cftKey);
5af840f1 896 }
cb9c71ec 897
5af840f1 898 void Set(CFTypeRef cftKey, CFTypeRef cftValue)
cb9c71ec 899 {
5af840f1
RN
900 wxASSERT(IsValid());
901 wxASSERT(Exists(cftKey));
cb9c71ec 902 CFDictionarySetValue(m_cfmdRef, cftKey, cftValue);
5af840f1 903 }
cb9c71ec 904
5af840f1
RN
905 bool Exists(CFTypeRef cftKey) const
906 {
907 wxASSERT(IsValid());
d0ee33f5 908 return CFDictionaryContainsKey((CFDictionaryRef)m_cfmdRef, cftKey);
5af840f1 909 }
cb9c71ec
WS
910
911 bool IsOk() const {return m_cfmdRef != NULL; }
5af840f1
RN
912
913 bool IsValid() const
914 {
915 return IsOk() && CFGetTypeID((CFTypeRef)m_cfmdRef) == CFDictionaryGetTypeID();
916 }
cb9c71ec 917
5af840f1
RN
918 void PrintOut(wxString& sMessage)
919 {
920 PrintOutDictionary(sMessage, m_cfmdRef);
921 }
922
923 static void PrintOutDictionary(wxString& sMessage, CFDictionaryRef cfdRef)
924 {
925 CFIndex cfiCount = CFDictionaryGetCount(cfdRef);
926 CFTypeRef* pKeys = new CFTypeRef[cfiCount];
927 CFTypeRef* pValues = new CFTypeRef[cfiCount];
928
929 CFDictionaryGetKeysAndValues(cfdRef, pKeys, pValues);
cb9c71ec 930
5af840f1
RN
931 for(CFIndex i = 0; i < cfiCount; ++i)
932 {
933 wxString sKey = wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys[i]))).AsString();
934 wxString sValue = wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues[i]))).AsString();
cb9c71ec
WS
935
936 sMessage <<
5af840f1
RN
937 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i,
938 sKey.c_str());
cb9c71ec 939
5af840f1 940 PrintOutType(sMessage, sKey, pKeys[i]);
cb9c71ec
WS
941
942 sMessage <<
943 wxString::Format(wxT("\n\t[Value : %s]"),
5af840f1 944 sValue.c_str());
cb9c71ec 945
5af840f1 946 PrintOutType(sMessage, sValue, pValues[i]);
cb9c71ec 947
5af840f1
RN
948 sMessage << wxT("\n");
949 }
cb9c71ec 950
5af840f1
RN
951 delete[] pKeys;
952 delete[] pValues;
953 }
cb9c71ec 954
5af840f1
RN
955 static void PrintOutArray(wxString& sMessage, CFArrayRef cfaRef)
956 {
cb9c71ec 957
5af840f1
RN
958 for(CFIndex i = 0; i < CFArrayGetCount(cfaRef); ++i)
959 {
960 wxString sValue = wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
961 CFArrayGetValueAtIndex(cfaRef, i)
962 ))).AsString();
cb9c71ec
WS
963
964 sMessage <<
5af840f1
RN
965 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i,
966 sValue.c_str());
cb9c71ec 967
5af840f1
RN
968 PrintOutType(sMessage, sValue, CFArrayGetValueAtIndex(cfaRef, i));
969 }
970 }
cb9c71ec 971
fbfb8bcc 972 static void PrintOutType(wxString& sMessage, const wxString& sValue, CFTypeRef cfRef)
5af840f1
RN
973 {
974 sMessage << wxT(" {");
cb9c71ec 975
5af840f1
RN
976 if(sValue == wxT("CFString"))
977 {
978 sMessage << wxMacCFStringHolder((CFStringRef)cfRef, false).AsString();
979 }
980 else if(sValue == wxT("CFNumber"))
981 {
982 int nOut;
983 CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &nOut);
984 sMessage << nOut;
985 }
986 else if(sValue == wxT("CFDictionary"))
987 {
988 PrintOutDictionary(sMessage, (CFDictionaryRef)cfRef);
989 }
990 else if(sValue == wxT("CFArray"))
991 {
992 PrintOutArray(sMessage, (CFArrayRef)cfRef);
993 }
994 else if(sValue == wxT("CFBoolean"))
995 {
996 sMessage << (cfRef == kCFBooleanTrue ? wxT("true") : wxT("false"));
997 }
998 else if(sValue == wxT("CFURL"))
999 {
1000 sMessage << wxMacCFStringHolder(CFURLCopyPath((CFURLRef) cfRef)).AsString();
1001 }
1002 else
1003 {
1004 sMessage << wxT("*****UNKNOWN TYPE******");
1005 }
cb9c71ec 1006
5af840f1
RN
1007 sMessage << wxT("} ");
1008 }
cb9c71ec 1009
5af840f1
RN
1010#if wxUSE_MIMETYPE
1011 void MakeValidXML();
1012#endif
cb9c71ec 1013
5af840f1
RN
1014 CFTypeRef WriteAsXML()
1015 {
1016 return CFPropertyListCreateXMLData(kCFAllocatorDefault, m_cfmdRef);
1017 }
cb9c71ec 1018
5af840f1
RN
1019 bool ReadAsXML(CFTypeRef cfData, wxString* pErrorMsg = NULL)
1020 {
1021 Clear();
1022 CFStringRef cfsError=NULL;
1023 m_cfmdRef = (CFMutableDictionaryRef) CFPropertyListCreateFromXMLData(
1024 kCFAllocatorDefault,
1025 (CFDataRef)cfData,
1026 kCFPropertyListMutableContainersAndLeaves,
cb9c71ec 1027 &cfsError );
5af840f1
RN
1028 if(cfsError)
1029 {
1030 if(pErrorMsg)
1031 *pErrorMsg = wxMacCFStringHolder(cfsError).AsString();
1032 else
1033 CFRelease(cfsError);
cb9c71ec 1034 }
5af840f1
RN
1035
1036 return m_cfmdRef != NULL;
1037 }
cb9c71ec 1038 private:
5af840f1
RN
1039 CFMutableDictionaryRef m_cfmdRef;
1040};
1041
1042// ----------------------------------------------------------------------------
1043// wxCFArray
1044// ----------------------------------------------------------------------------
1045
1046class wxCFArray
1047{
1048public:
1049 wxCFArray(CFTypeRef ref, bool bRetain = wxCF_RELEASE)
1050 {
1051 m_cfmaRef = (CFMutableArrayRef)ref;
1052 if(bRetain == wxCF_RETAIN && ref)
1053 CFRetain(ref);
1054 }
cb9c71ec 1055
5af840f1 1056 wxCFArray(CFIndex cfiSize = 0) : m_cfmaRef(NULL)
cb9c71ec
WS
1057 { Create(cfiSize); }
1058
1059 ~wxCFArray()
1060 { Clear(); }
1061
5af840f1
RN
1062 void MakeMutable(CFIndex cfiSize = 0)
1063 {
1064 wxASSERT(IsValid());
cb9c71ec 1065
5af840f1
RN
1066 CFMutableArrayRef oldref = m_cfmaRef;
1067 m_cfmaRef = CFArrayCreateMutableCopy(
1068 kCFAllocatorDefault,
cb9c71ec
WS
1069 cfiSize,
1070 (CFArrayRef)oldref);
5af840f1
RN
1071 CFRelease(oldref);
1072 }
cb9c71ec 1073
5af840f1
RN
1074 void BuildCallbacks(CFArrayCallBacks* pCbs)
1075 {
1076 pCbs->version = 0;
1077 pCbs->retain = RetainProc;
1078 pCbs->release = ReleaseProc;
1079 pCbs->copyDescription = NULL;
1080 pCbs->equal = NULL;
1081 }
1082
1083 void Create(CFIndex cfiSize = 0)
1084 {
1085 Clear();
1086 CFArrayCallBacks cb;
1087 BuildCallbacks(&cb);
cb9c71ec 1088
5af840f1
RN
1089 m_cfmaRef = CFArrayCreateMutable(kCFAllocatorDefault, cfiSize, &cb);
1090 }
cb9c71ec 1091
5af840f1
RN
1092 void Clear()
1093 {if(m_cfmaRef) CFRelease(m_cfmaRef);}
cb9c71ec 1094
5af840f1 1095 static const void* RetainProc(CFAllocatorRef, const void* v)
cb9c71ec
WS
1096 { return (const void*) CFRetain(v); }
1097
5af840f1 1098 static void ReleaseProc(CFAllocatorRef, const void* v)
cb9c71ec
WS
1099 { CFRelease(v); }
1100
5af840f1 1101 operator CFTypeRef () const
cb9c71ec
WS
1102 { return (CFTypeRef)m_cfmaRef; }
1103
5af840f1 1104 CFArrayRef GetCFArray() const
cb9c71ec 1105 { return (CFArrayRef)m_cfmaRef; }
5af840f1
RN
1106
1107 CFMutableArrayRef GetCFMutableArray()
cb9c71ec 1108 { return (CFMutableArrayRef) m_cfmaRef; }
5af840f1
RN
1109
1110 CFTypeRef operator [] (CFIndex cfiIndex) const
cb9c71ec 1111 {
5af840f1 1112 wxASSERT(IsValid());
cb9c71ec
WS
1113 return (CFTypeRef) CFArrayGetValueAtIndex((CFArrayRef)m_cfmaRef, cfiIndex);
1114 }
1115
5af840f1 1116 CFIndex GetCount()
cb9c71ec 1117 {
5af840f1 1118 wxASSERT(IsValid());
cb9c71ec 1119 return CFArrayGetCount((CFArrayRef)m_cfmaRef);
5af840f1 1120 }
cb9c71ec 1121
5af840f1 1122 void Add(CFTypeRef cftValue)
cb9c71ec 1123 {
5af840f1 1124 wxASSERT(IsValid());
cb9c71ec 1125 CFArrayAppendValue(m_cfmaRef, cftValue);
5af840f1 1126 }
cb9c71ec 1127
5af840f1 1128 void Remove(CFIndex cfiIndex)
cb9c71ec 1129 {
5af840f1
RN
1130 wxASSERT(IsValid());
1131 wxASSERT(cfiIndex < GetCount());
cb9c71ec 1132 CFArrayRemoveValueAtIndex(m_cfmaRef, cfiIndex);
5af840f1 1133 }
cb9c71ec 1134
5af840f1 1135 void Set(CFIndex cfiIndex, CFTypeRef cftValue)
cb9c71ec 1136 {
5af840f1
RN
1137 wxASSERT(IsValid());
1138 wxASSERT(cfiIndex < GetCount());
cb9c71ec 1139 CFArraySetValueAtIndex(m_cfmaRef, cfiIndex, cftValue);
5af840f1
RN
1140 }
1141
cb9c71ec 1142 bool IsOk() const {return m_cfmaRef != NULL; }
5af840f1
RN
1143
1144 bool IsValid() const
1145 {
1146 return IsOk() && CFGetTypeID((CFTypeRef)m_cfmaRef) == CFArrayGetTypeID();
1147 }
1148
1149#if wxUSE_MIMETYPE
1150 void MakeValidXML();
cb9c71ec
WS
1151#endif
1152
5af840f1
RN
1153private:
1154 CFMutableArrayRef m_cfmaRef;
1155};
1156
1157// ----------------------------------------------------------------------------
1158// wxCFString
1159// ----------------------------------------------------------------------------
1160
cb9c71ec 1161class wxCFString
5af840f1
RN
1162{
1163public:
1164 wxCFString(CFTypeRef ref, bool bRetain = wxCF_RELEASE) : m_Holder((CFStringRef)ref, bRetain == wxCF_RELEASE)
cb9c71ec
WS
1165 { }
1166
5af840f1 1167 wxCFString(const wxChar* szString) : m_Holder(wxString(szString), wxLocale::GetSystemEncoding())
cb9c71ec
WS
1168 { }
1169
5af840f1 1170 wxCFString(const wxString& sString) : m_Holder(sString, wxLocale::GetSystemEncoding())
cb9c71ec
WS
1171 { }
1172
5af840f1 1173 operator CFTypeRef() const
cb9c71ec
WS
1174 { return (CFTypeRef) ((CFStringRef) m_Holder); }
1175
1176 bool IsOk() { return ((CFTypeRef)(*this)) != NULL; }
1177
1178 wxString BuildWXString() {return m_Holder.AsString(); }
5af840f1
RN
1179
1180private:
1181 wxMacCFStringHolder m_Holder;
1182};
1183
1184// ----------------------------------------------------------------------------
1185// wxCFNumber
1186// ----------------------------------------------------------------------------
1187
1188class wxCFNumber
1189{
1190public:
cb9c71ec 1191 wxCFNumber(int nValue)
5af840f1 1192 {
cb9c71ec 1193 m_cfnRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &nValue);
5af840f1 1194 }
cb9c71ec 1195
5af840f1
RN
1196 wxCFNumber(CFTypeRef ref, bool bRetain = wxCF_RELEASE) : m_cfnRef((CFNumberRef)ref)
1197 {
1198 if(bRetain == wxCF_RETAIN && ref)
1199 CFRetain(ref);
1200 }
cb9c71ec 1201
5af840f1 1202 ~wxCFNumber()
cb9c71ec
WS
1203 {
1204 if(m_cfnRef)
1205 CFRelease(m_cfnRef);
1206 }
1207
1208
5af840f1 1209 operator CFTypeRef() const
cb9c71ec 1210 { return (CFTypeRef) m_cfnRef; }
5af840f1
RN
1211
1212 int GetValue()
cb9c71ec 1213 {
5af840f1 1214 int nOut;
01aefd22 1215 CFNumberGetValue( m_cfnRef,
cb9c71ec
WS
1216 kCFNumberIntType,
1217 &nOut
1218 );
1219
5af840f1
RN
1220 return nOut;
1221 }
1222
cb9c71ec 1223 bool IsOk() { return m_cfnRef != NULL; }
5af840f1
RN
1224
1225private:
1226 CFNumberRef m_cfnRef;
1227};
1228
1229// ----------------------------------------------------------------------------
1230// wxCFURL
1231// ----------------------------------------------------------------------------
1232
1233class wxCFURL
1234{
1235public:
cb9c71ec 1236 wxCFURL(CFTypeRef ref = NULL, bool bRetain = wxCF_RELEASE) : m_cfurlRef((CFURLRef)ref)
5af840f1
RN
1237 {
1238 if(bRetain == wxCF_RETAIN && ref)
1239 CFRetain(ref);
1240 }
1241 wxCFURL(const wxCFString& URLString, CFTypeRef BaseURL = NULL)
1242 {
1243 Create(URLString, BaseURL);
1244 }
cb9c71ec 1245
5af840f1
RN
1246 void Create(const wxCFString& URLString, CFTypeRef BaseURL = NULL)
1247 {
1248 m_cfurlRef = CFURLCreateWithString(
1249 kCFAllocatorDefault,
1250 (CFStringRef)(CFTypeRef)URLString,
1251 (CFURLRef) BaseURL);
1252 }
cb9c71ec 1253
5af840f1
RN
1254 ~wxCFURL() {if(m_cfurlRef) CFRelease(m_cfurlRef);}
1255
1256 wxString BuildWXString()
1257 {
1258 return wxCFString(CFURLCopyPath(m_cfurlRef)).BuildWXString();
1259 }
cb9c71ec 1260
5af840f1 1261 operator CFTypeRef() const
cb9c71ec
WS
1262 { return (CFTypeRef)m_cfurlRef; }
1263
1264 bool IsOk() { return m_cfurlRef != NULL; }
5af840f1
RN
1265private:
1266 CFURLRef m_cfurlRef;
1267};
1268
1269// ----------------------------------------------------------------------------
1270// wxCFData
1271// ----------------------------------------------------------------------------
1272
1273#define wxCFDATA_RELEASEBUFFER 1
1274#define wxCFDATA_RETAINBUFFER 0
1275
1276class wxCFData
1277{
1278public:
cb9c71ec 1279 wxCFData(CFTypeRef ref, bool bRetain = wxCF_RELEASE) : m_cfdaRef((CFDataRef)ref)
5af840f1
RN
1280 {
1281 if(bRetain == wxCF_RETAIN && ref)
1282 CFRetain(ref);
1283 }
1284 wxCFData(const UInt8* pBytes, CFIndex len, bool bKeep = wxCFDATA_RELEASEBUFFER)
1285 {
1286 if(bKeep == wxCFDATA_RELEASEBUFFER)
1287 {
1288 m_cfdaRef = CFDataCreateWithBytesNoCopy
1289 (kCFAllocatorDefault, pBytes, len, kCFAllocatorDefault);
1290 }
1291 else
1292 {
1293 m_cfdaRef = CFDataCreate(kCFAllocatorDefault, pBytes, len);
1294 }
1295 }
1296 ~wxCFData() {if(m_cfdaRef) CFRelease(m_cfdaRef);}
1297
1298 const UInt8* GetValue()
1299 {
1300 return CFDataGetBytePtr(m_cfdaRef);
1301 }
cb9c71ec 1302
5af840f1
RN
1303 CFIndex GetCount()
1304 {
1305 return CFDataGetLength(m_cfdaRef);
1306 }
cb9c71ec 1307
5af840f1 1308 operator CFTypeRef() const
cb9c71ec
WS
1309 { return (CFTypeRef)m_cfdaRef; }
1310
1311 bool IsOk() { return m_cfdaRef != NULL; }
5af840f1
RN
1312private:
1313 CFDataRef m_cfdaRef;
1314};
1315
1316void wxCFDictionary::MakeValidXML()
1317{
1318 CFIndex cfiCount = GetCount();
1319 CFTypeRef* pKeys = new CFTypeRef[cfiCount];
1320 CFTypeRef* pValues = new CFTypeRef[cfiCount];
1321
1322 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
1323
cb9c71ec 1324 //for plist xml format all dictionary keys must be cfstrings and no values in
5af840f1
RN
1325 //the dictionary or subkeys/values can be NULL
1326 //Also, CFURLs are not allowed
1327 for(CFIndex i = 0; i < cfiCount; ++i)
1328 {
1329 //must be an array, dictionary, string, bool, or int and cannot be null
1330 //and dictionaries can only contain cfstring keys
1331 CFTypeRef cfRef = pValues[i];
1332 if(!pKeys[i] ||
cb9c71ec 1333 CFGetTypeID(pKeys[i]) != CFStringGetTypeID() ||
5af840f1
RN
1334 !cfRef)
1335 {
1336 Remove(pKeys[i]);
1337 --i;
1338 --cfiCount;
1339 delete[] pKeys;
1340 delete[] pValues;
1341 pKeys = new CFTypeRef[cfiCount];
1342 pValues = new CFTypeRef[cfiCount];
1343 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
1344 }
1345 else if (CFGetTypeID(cfRef) == CFArrayGetTypeID())
1346 {
1347 CFRetain(cfRef);
1348 wxCFArray cfaCurrent(cfRef);
1349 cfaCurrent.MakeMutable();
1350 cfaCurrent.MakeValidXML();
1351 Set(pKeys[i], cfaCurrent);
1352 }
1353 else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID())
1354 {
1355 CFRetain(cfRef);
1356 wxCFDictionary cfdCurrent(cfRef);
1357 cfdCurrent.MakeMutable();
1358 cfdCurrent.MakeValidXML();
1359 Set(pKeys[i], cfdCurrent);
1360 }
d0ee33f5
WS
1361 else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() &&
1362 CFGetTypeID(cfRef) != CFNumberGetTypeID() &&
1363 CFGetTypeID(cfRef) != CFBooleanGetTypeID() )
5af840f1
RN
1364 {
1365 Remove(pKeys[i]);
1366 --i;
1367 --cfiCount;
1368 delete[] pKeys;
1369 delete[] pValues;
1370 pKeys = new CFTypeRef[cfiCount];
1371 pValues = new CFTypeRef[cfiCount];
1372 CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues);
cb9c71ec 1373 }
5af840f1
RN
1374 }
1375
1376 delete[] pValues;
1377 delete[] pKeys;
1378}
1379
1380void wxCFArray::MakeValidXML()
1381{
1382 for(CFIndex i = 0; i < GetCount(); ++i)
1383 {
1384 //must be an array, dictionary, string, bool, or int and cannot be null
1385 //and dictionaries can only contain cfstring keys
1386 CFTypeRef cfRef = (*this)[i];
1387 if(!cfRef)
1388 {
1389 Remove(i);
1390 --i;
1391 }
1392 else if (CFGetTypeID(cfRef) == CFArrayGetTypeID())
1393 {
1394 CFRetain(cfRef);
1395 wxCFArray cfaCurrent(cfRef);
1396 cfaCurrent.MakeMutable();
1397 cfaCurrent.MakeValidXML();
1398 Set(i, cfaCurrent);
1399 }
1400 else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID())
1401 {
1402 CFRetain(cfRef);
1403 wxCFDictionary cfdCurrent(cfRef);
1404 cfdCurrent.MakeMutable();
1405 cfdCurrent.MakeValidXML();
1406 Set(i, cfdCurrent);
1407 }
d0ee33f5
WS
1408 else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() &&
1409 CFGetTypeID(cfRef) != CFNumberGetTypeID() &&
1410 CFGetTypeID(cfRef) != CFBooleanGetTypeID() )
5af840f1
RN
1411 {
1412 Remove(i);
1413 --i;
1414 }
1415 }
1416}
1417
1418//
1419//
1420//
1421// END TODO
1422//
1423//
1424//
1425
1426wxFileType* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
1427{
1428 bool bInfoSuccess = false;
cb9c71ec
WS
1429
1430 const wxArrayString& asExtensions = ftInfo.GetExtensions();
5af840f1
RN
1431 size_t dwFoundIndex = 0;
1432 if(!asExtensions.GetCount())
1433 {
1434 wxLogDebug(wxT("Must have extension to associate with"));
1435 }
cb9c71ec 1436
5af840f1
RN
1437 //Find and write to Info.plist in main bundle (note that some other
1438 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1439 //some apps (non-wx) use the 'plst' resource instead
1440 CFBundleRef cfbMain = CFBundleGetMainBundle();
1441 if(cfbMain)
cb9c71ec 1442 {
5af840f1
RN
1443 UInt32 dwBundleType, dwBundleCreator;
1444 CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator);
cb9c71ec 1445
5af840f1
RN
1446 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1447 //which will give us the incorrect info.plist path
1448 //otherwise it will be 'APPL', or in the case of a framework,
1449 //'FMWK'
1450 if(dwBundleType == 'APPL')
1451 {
1452
1453 wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain));
1454// wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1455 wxString sInfoPath;
1456// sInfoPath << wxT("file://");
1457 sInfoPath << cfurlBundleLoc.BuildWXString();
1458 sInfoPath << wxT("Contents/Info.plist");
cb9c71ec 1459
5af840f1
RN
1460// wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1461 wxCFDictionary cfdInfo;
1462 bool bInfoOpenSuccess = false;
1463 wxFile indictfile;
01aefd22 1464 if(indictfile.Open(sInfoPath, wxFile::read))
5af840f1
RN
1465 {
1466 CFIndex cfiBufLen = (CFIndex) indictfile.Length();
1467 const UInt8* pBuffer = new UInt8[cfiBufLen];
1468 indictfile.Read((void*)pBuffer, cfiBufLen);
1469 wxCFData cfdaInDict(pBuffer, cfiBufLen);
1470 wxString sError;
cb9c71ec 1471 bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError);
5af840f1
RN
1472 if(!bInfoOpenSuccess)
1473 wxLogDebug(sError);
1474 indictfile.Close();
1475 }
1476 if(bInfoOpenSuccess)
1477 {
1478 cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 );
1479
1480 wxCFArray cfaDocTypes( cfdInfo[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN );
cb9c71ec 1481
5af840f1
RN
1482 bool bAddDocTypesArrayToDictionary = cfaDocTypes.IsOk() == false;
1483 if(bAddDocTypesArrayToDictionary)
1484 cfaDocTypes.Create();
1485 else
1486 cfaDocTypes.MakeMutable( cfaDocTypes.GetCount() + 1 );
cb9c71ec 1487
5af840f1 1488 bool bEntryFound = false;
cb9c71ec 1489
5af840f1
RN
1490 //search for duplicate
1491 CFIndex i;
1492 for(i = 0; i < cfaDocTypes.GetCount(); ++i)
1493 {
1494 wxCFDictionary cfdDocTypeEntry( cfaDocTypes[i], wxCF_RETAIN );
cb9c71ec 1495
5af840f1
RN
1496 //A lot of apps dont do to mime types for some reason
1497 //so we go by extensions only
cb9c71ec 1498 wxCFArray cfaExtensions( cfdDocTypeEntry[ wxCFString(wxT("CFBundleTypeExtensions")) ],
5af840f1 1499 wxCF_RETAIN );
cb9c71ec 1500
5af840f1
RN
1501 if(cfaExtensions.IsOk() == false)
1502 continue;
cb9c71ec 1503
5af840f1 1504 for(CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt)
cb9c71ec 1505 {
5af840f1
RN
1506 for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt)
1507 {
cb9c71ec 1508 if(asExtensions[iWXExt] ==
5af840f1
RN
1509 wxCFString(cfaExtensions[iExt], wxCF_RETAIN).BuildWXString())
1510 {
1511 bEntryFound = true;
1512 dwFoundIndex = iWXExt;
1513 break;
1514 }
cb9c71ec
WS
1515 } //end of wxstring array
1516
5af840f1
RN
1517 if(bEntryFound)
1518 break;
cb9c71ec
WS
1519 } //end for cf array
1520
5af840f1
RN
1521 if(bEntryFound)
1522 break;
1523 }//end for doctypes
cb9c71ec 1524
5af840f1
RN
1525 wxCFDictionary cfdNewEntry;
1526
1527 if(!ftInfo.GetDescription().empty())
1528 {
cb9c71ec 1529 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeName")),
5af840f1
RN
1530 wxCFString(ftInfo.GetDescription()) );
1531 }
cb9c71ec 1532
5af840f1
RN
1533 if(!ftInfo.GetIconFile().empty())
1534 {
cb9c71ec 1535 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeIconFile")),
5af840f1
RN
1536 wxCFString(ftInfo.GetIconFile()) );
1537 }
cb9c71ec
WS
1538
1539
5af840f1
RN
1540 wxCFArray cfaOSTypes;
1541 wxCFArray cfaExtensions;
1542 wxCFArray cfaMimeTypes;
cb9c71ec
WS
1543
1544
5af840f1
RN
1545 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1546 cfaOSTypes.Add( wxCFString(wxT("****")) );
1547 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes );
1548
1549 if(ftInfo.GetExtensionsCount() != 0) //'*' for unrestricted
1550 {
1551 for(size_t iExtension = 0; iExtension < (size_t)ftInfo.GetExtensionsCount(); ++iExtension)
1552 {
1553 cfaExtensions.Add( wxCFString( asExtensions[iExtension] ) );
1554 }
cb9c71ec 1555
5af840f1
RN
1556 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions );
1557 }
cb9c71ec 1558
5af840f1
RN
1559 if(!ftInfo.GetMimeType().empty())
1560 {
1561 cfaMimeTypes.Add( wxCFString(ftInfo.GetMimeType()) );
1562 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes );
1563 }
cb9c71ec 1564
5af840f1
RN
1565 // Editor - can perform all actions
1566 // Viewer - all actions except manipulation/saving
1567 // None - can perform no actions
1568 cfdNewEntry.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
cb9c71ec 1569
5af840f1
RN
1570 // Is application bundled?
1571 cfdNewEntry.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue );
1572
1573 if(bEntryFound)
1574 cfaDocTypes.Set(i, cfdNewEntry);
1575 else
1576 cfaDocTypes.Add(cfdNewEntry);
cb9c71ec 1577
5af840f1
RN
1578 //
1579 // set the doc types array in the muted dictionary
1580 //
cb9c71ec 1581
5af840f1
RN
1582 if(bAddDocTypesArrayToDictionary)
1583 cfdInfo.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes);
1584 else
1585 cfdInfo.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes);
cb9c71ec 1586
5af840f1
RN
1587 cfdInfo.MakeValidXML();
1588
1589 wxFile outdictfile;
1590 if(outdictfile.Open(sInfoPath, wxFile::write))
1591 {
1592 wxCFData cfdaInfo(cfdInfo.WriteAsXML());
1593 if(cfdaInfo.IsOk())
1594 {
cb9c71ec 1595 if(outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) !=
5af840f1
RN
1596 (wxFileOffset)cfdaInfo.GetCount())
1597 {
1598 wxLogDebug(wxT("error in writing to file"));
1599 }
1600 else
1601 {
1602 bInfoSuccess = true;
1603//#if defined(__DARWIN__)
1604// //force launch services to update its database for the finder
1605// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1606// if(status != noErr)
1607// {
1608// wxLogDebug(wxT("LSRegisterURL Failed."));
1609// }
1610//#endif
1611 }
1612 outdictfile.Close();
1613 }
1614 else
1615 {
1616 outdictfile.Close();
1617 wxLogDebug(wxT("Could not read in new dictionary"));
1618 }
1619 }
1620 else
1621 {
cb9c71ec 1622 wxLogDebug(wxString(wxT("Could not open [")) +
5af840f1
RN
1623 sInfoPath + wxT("] for writing."));
1624 }
1625 }
1626 else
1627 {
1628 wxLogDebug(wxT("No info dictionary in main bundle"));
1629 }
1630 }
cb9c71ec 1631 else
5af840f1
RN
1632 {
1633 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1634 }
1635 }
1636 else
1637 {
1638 wxLogDebug(wxT("No main bundle"));
1639 }
1640
1641#if defined(__DARWIN__)
1642 if(!bInfoSuccess)
1643 return NULL;
1644#endif
c9e227f4
RN
1645 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1646 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1647 //the app's signature somehow...
007ae8d0 1648
7baa887c
RN
1649 OSType processType,
1650 creator;
1651 OSStatus status = MoreProcGetProcessTypeSignature(NULL,&processType, &creator);
cb9c71ec 1652
7baa887c
RN
1653 if(status == noErr)
1654 {
1655 Str255 psCreatorName;
1656 FSSpec dummySpec;
1657 status = FindApplication(creator, false, psCreatorName, &dummySpec);
cb9c71ec 1658
7baa887c
RN
1659 if(status == noErr)
1660 {
1661
cb9c71ec 1662 //get the file type if it exists -
7baa887c
RN
1663 //if it really does then modify the database then save it,
1664 //otherwise we need to create a whole new entry
5af840f1 1665 wxFileType* pFileType = GetFileTypeFromExtension(asExtensions[dwFoundIndex]);
7baa887c
RN
1666 if(pFileType)
1667 {
1668 ICMapEntry entry;
cb9c71ec 1669 ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
7baa887c 1670 pFileType->m_impl->m_lIndex, &entry);
cb9c71ec
WS
1671
1672 memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255));
7baa887c 1673 entry.fileCreator = creator;
cb9c71ec
WS
1674
1675 status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
7baa887c
RN
1676 pFileType->m_impl->m_lIndex, &entry);
1677
1678 //success
1679 if(status == noErr)
5af840f1
RN
1680 {
1681 //kICAttrNoChange means we don't care about attributes such as
1682 //locking in the database
cb9c71ec 1683// status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
5af840f1
RN
1684// kICAttrNoChange, (Handle) m_hDatabase);
1685// if(status == noErr)
1686 return pFileType;
1687// else
1688// {
cb9c71ec 1689// wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
5af840f1
RN
1690 // }
1691 }
cb9c71ec
WS
1692 else
1693 {
1694 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed.")));
7baa887c 1695 }
cb9c71ec 1696
7baa887c
RN
1697 //failure - cleanup
1698 delete pFileType;
1699 }
1700 else
1701 {
1702 //TODO: Maybe force all 3 of these to be non-empty?
1703 Str255 psExtension;
1704 Str255 psMimeType;
1705 Str255 psDescription;
cb9c71ec
WS
1706
1707 wxMacStringToPascal(wxString(wxT(".")) + ftInfo.GetExtensions()[0], psExtension);
1708 wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType);
1709 wxMacStringToPascal(ftInfo.GetDescription(), psDescription);
7baa887c
RN
1710
1711 Str255 psPostCreatorName;
1712 wxMacStringToPascal(wxT(""), psPostCreatorName);
1713
1714
1715 //add the entry to the database
7baa887c
RN
1716 ICMapEntry entry;
1717 entry.totalLength = sizeof(ICMapEntry);
1718 entry.fixedLength = kICMapFixedLength;
1719 entry.version = 0;
1720 entry.fileType = 0; //TODO: File type?
1721 entry.fileCreator = creator;
1722 entry.postCreator = 0;
1723 entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too?
b94fa9f5 1724 PLstrcpy( entry.extension , psExtension ) ;
fcc7c995
RN
1725 memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255));
1726 memcpy(entry.postAppName, psPostCreatorName, sizeof(Str255));
1727 memcpy(entry.MIMEType, psMimeType, sizeof(Str255));
1728 memcpy(entry.entryName, psDescription, sizeof(Str255));
cb9c71ec 1729
7baa887c 1730 status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry);
cb9c71ec 1731
7baa887c
RN
1732 if(status == noErr)
1733 {
1734 //kICAttrNoChange means we don't care about attributes such as
1735 //locking in the database
cb9c71ec 1736 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
5af840f1 1737 // kICAttrNoChange, (Handle) m_hDatabase);
cb9c71ec 1738
7baa887c 1739 //return the entry in the database if successful
5af840f1
RN
1740 // if(status == noErr)
1741 return GetFileTypeFromExtension(ftInfo.GetMimeType());
cb9c71ec
WS
1742 // else
1743 // {
1744 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
5af840f1 1745 // }
7baa887c 1746 }
cb9c71ec
WS
1747 else
1748 {
1749 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed.")));
7baa887c
RN
1750 }
1751 }
1752 } //end if FindApplcation was successful
cb9c71ec
WS
1753 else
1754 {
1755 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed.")));
7baa887c
RN
1756 }
1757 } //end if it could obtain app's signature
cb9c71ec
WS
1758 else
1759 {
1760 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed.")));
7baa887c 1761 }
007ae8d0
GD
1762 return NULL;
1763}
f040060e
GD
1764
1765bool
7baa887c 1766wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType)
f040060e 1767{
5af840f1
RN
1768 wxASSERT(pFileType);
1769 bool bInfoSuccess = false;
cb9c71ec 1770
5af840f1 1771 wxArrayString asExtensions;
cb9c71ec
WS
1772 pFileType->GetExtensions(asExtensions);
1773
5af840f1
RN
1774 if(!asExtensions.GetCount())
1775 {
1776 wxLogDebug(wxT("Must have extension to disassociate"));
cb9c71ec 1777 return false;
5af840f1
RN
1778 }
1779
1780 //Find and write to Info.plist in main bundle (note that some other
1781 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1782 //some apps (non-wx) use the 'plst' resource instead
1783 CFBundleRef cfbMain = CFBundleGetMainBundle();
1784 if(cfbMain)
cb9c71ec 1785 {
5af840f1
RN
1786 UInt32 dwBundleType, dwBundleCreator;
1787 CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator);
cb9c71ec 1788
5af840f1
RN
1789 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1790 //which will give us the incorrect info.plist path
1791 //otherwise it will be 'APPL', or in the case of a framework,
1792 //'FMWK'
1793 if(dwBundleType == 'APPL')
1794 {
1795
1796 wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain));
1797// wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1798 wxString sInfoPath;
1799// sInfoPath << wxT("file://");
1800 sInfoPath << cfurlBundleLoc.BuildWXString();
1801 sInfoPath << wxT("Contents/Info.plist");
cb9c71ec 1802
5af840f1
RN
1803// wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1804 wxCFDictionary cfdInfo;
1805 bool bInfoOpenSuccess = false;
1806 wxFile indictfile;
01aefd22 1807 if(indictfile.Open(sInfoPath, wxFile::read))
5af840f1
RN
1808 {
1809 CFIndex cfiBufLen = (CFIndex) indictfile.Length();
1810 const UInt8* pBuffer = new UInt8[cfiBufLen];
1811 indictfile.Read((void*)pBuffer, cfiBufLen);
1812 wxCFData cfdaInDict(pBuffer, cfiBufLen);
1813 wxString sError;
cb9c71ec 1814 bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError);
5af840f1
RN
1815 if(!bInfoOpenSuccess)
1816 wxLogDebug(sError);
1817 indictfile.Close();
1818 }
1819 if(bInfoOpenSuccess)
1820 {
1821 cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 );
1822
1823 wxCFArray cfaDocTypes( cfdInfo[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN );
cb9c71ec 1824
5af840f1 1825 if(cfaDocTypes.IsOk())
cb9c71ec 1826 {
5af840f1 1827 bool bEntryFound = false;
cb9c71ec 1828
5af840f1
RN
1829 //search for duplicate
1830 CFIndex i;
1831 for(i = 0; i < cfaDocTypes.GetCount(); ++i)
1832 {
1833 wxCFDictionary cfdDocTypeEntry( cfaDocTypes[i], wxCF_RETAIN );
cb9c71ec 1834
5af840f1
RN
1835 //A lot of apps dont do to mime types for some reason
1836 //so we go by extensions only
cb9c71ec 1837 wxCFArray cfaExtensions( cfdDocTypeEntry[ wxCFString(wxT("CFBundleTypeExtensions")) ],
5af840f1 1838 wxCF_RETAIN );
cb9c71ec 1839
5af840f1
RN
1840 if(cfaExtensions.IsOk() == false)
1841 continue;
cb9c71ec 1842
5af840f1 1843 for(CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt)
cb9c71ec 1844 {
5af840f1
RN
1845 for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt)
1846 {
cb9c71ec 1847 if(asExtensions[iWXExt] ==
5af840f1
RN
1848 wxCFString(cfaExtensions[iExt], wxCF_RETAIN).BuildWXString())
1849 {
1850 bEntryFound = true;
1851 cfaDocTypes.Remove(i);
1852 cfdInfo.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes );
1853 break;
1854 }
cb9c71ec
WS
1855 } //end of wxstring array
1856
5af840f1
RN
1857 if(bEntryFound)
1858 break;
cb9c71ec
WS
1859 } //end for cf array
1860
5af840f1
RN
1861 if(bEntryFound)
1862 break;
1863 }//end for doctypes
cb9c71ec 1864
5af840f1
RN
1865 if(bEntryFound)
1866 {
1867 cfdInfo.MakeValidXML();
1868
1869 wxFile outdictfile;
1870 if(outdictfile.Open(sInfoPath, wxFile::write))
1871 {
1872 wxCFData cfdaInfo(cfdInfo.WriteAsXML());
1873 if(cfdaInfo.IsOk())
1874 {
cb9c71ec 1875 if(outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) !=
5af840f1
RN
1876 (wxFileOffset)cfdaInfo.GetCount())
1877 {
1878 wxLogDebug(wxT("error in writing to file"));
1879 }
1880 else
1881 {
1882 bInfoSuccess = true;
1883//#if defined(__DARWIN__)
1884// //force launch services to update its database for the finder
1885// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1886// if(status != noErr)
1887// {
1888// wxLogDebug(wxT("LSRegisterURL Failed."));
1889// }
1890//#endif
1891 }
1892 outdictfile.Close();
1893 }
1894 else
1895 {
1896 outdictfile.Close();
1897 wxLogDebug(wxT("Could not read in new dictionary"));
1898 }
1899 }
1900 else
1901 {
cb9c71ec 1902 wxLogDebug(wxString(wxT("Could not open [")) +
5af840f1
RN
1903 sInfoPath + wxT("] for writing."));
1904 }
1905 }
1906 else
1907 {
1908 wxLogDebug(wxT("Entry not found to remove"));
cb9c71ec
WS
1909 wxString sPrintOut;
1910 wxCFDictionary::PrintOutArray(sPrintOut, (CFArrayRef)(CFTypeRef)cfaDocTypes);
5af840f1
RN
1911 wxLogDebug(sPrintOut);
1912 for(size_t i = 0; i < asExtensions.GetCount(); ++i)
1913 wxLogDebug(asExtensions[i]);
1914 }
1915 }
1916 else
1917 {
1918 wxLogDebug(wxT("No doc types array found"));
1919 wxString sPrintOut; cfdInfo.PrintOut(sPrintOut); wxLogDebug(sPrintOut);
1920 }
1921 }
1922 else
1923 {
1924 wxLogDebug(wxT("No info dictionary in main bundle"));
1925 }
1926 }
cb9c71ec 1927 else
5af840f1
RN
1928 {
1929 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1930 }
1931 }
1932 else
1933 {
1934 wxLogDebug(wxT("No main bundle"));
1935 }
1936
1937#if defined(__DARWIN__)
1938 if(!bInfoSuccess)
cb9c71ec 1939 return false;
5af840f1
RN
1940#endif
1941
cb9c71ec 1942 //this should be as easy as removing the entry from the database and then saving
c9e227f4 1943 //the database
cb9c71ec 1944 OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase,
7baa887c 1945 pFileType->m_impl->m_lIndex);
cb9c71ec 1946
7baa887c
RN
1947 if(status == noErr)
1948 {
1949 //kICAttrNoChange means we don't care about attributes such as
1950 //locking in the database
cb9c71ec 1951// status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
5af840f1 1952// kICAttrNoChange, (Handle) m_hDatabase);
cb9c71ec 1953
5af840f1 1954// if(status == noErr)
cb9c71ec 1955 return true;
5af840f1 1956// else
cb9c71ec
WS
1957// {
1958// wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
5af840f1 1959// }
7baa887c
RN
1960
1961 }
1962 else
cb9c71ec
WS
1963 {
1964 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed.")));
7baa887c 1965 }
cb9c71ec
WS
1966
1967 return false;
f040060e 1968}
5af840f1
RN
1969/*
1970 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1971 kCFAllocatorDefault,
cb9c71ec
WS
1972 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1973
5af840f1
RN
1974// CFShow(cfdInfo);
1975 if(cfwsInfo)
1976 {
1977 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1978 if(bOpened)
cb9c71ec 1979 {
5af840f1
RN
1980 CFStringRef cfsError;
1981 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1982 cfwsInfo,
1983 kCFPropertyListXMLFormat_v1_0, //100
1984 &cfsError);
1985 if(cfsError && cfiWritten == 0)
1986 {
1987 wxLogDebug(wxCFString(cfsError).BuildWXString());
1988 wxString sMessage;
1989 cfdInfo.PrintOut(sMessage);
1990 wxLogDebug(sMessage);
1991 }
1992 else
1993 {
1994 bInfoSuccess = true;
1995//#if defined(__DARWIN__)
1996// //force launch services to update its database for the finder
1997// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1998// if(status != noErr)
1999// {
2000// wxLogDebug(wxT("LSRegisterURL Failed."));
2001// }
2002//#endif
2003 }
cb9c71ec 2004
5af840f1 2005 CFWriteStreamClose(cfwsInfo);
f040060e 2006
5af840f1 2007*/
d0ee33f5 2008#endif //wxUSE_MIMETYPE