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