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