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