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