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