+#include <InternetConfig.h> //For mime types
+
+
+/* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
+
+ /* IsRemoteVolume can be used to find out if the
+ volume referred to by vRefNum is a remote volume
+ located somewhere on a network. the volume's attribute
+ flags (copied from the GetVolParmsInfoBuffer structure)
+ are returned in the longword pointed to by vMAttrib. */
+OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) {
+ HParamBlockRec volPB;
+ GetVolParmsInfoBuffer volinfo;
+ OSErr err;
+ volPB.ioParam.ioVRefNum = vRefNum;
+ volPB.ioParam.ioNamePtr = NULL;
+ volPB.ioParam.ioBuffer = (Ptr) &volinfo;
+ volPB.ioParam.ioReqCount = sizeof(volinfo);
+ err = PBHGetVolParmsSync(&volPB);
+ if (err == noErr) {
+ *isRemote = (volinfo.vMServerAdr != 0);
+ *vMAttrib = volinfo.vMAttrib;
+ }
+ return err;
+}
+
+
+ /* BuildVolumeList fills the array pointed to by vols with
+ a list of the currently mounted volumes. If includeRemote
+ is true, then remote server volumes will be included in
+ the list. When remote server volumes are included in the
+ list, they will be added to the end of the list. On entry,
+ *count should contain the size of the array pointed to by
+ vols. On exit, *count will be set to the number of id numbers
+ placed in the array. If vMAttribMask is non-zero, then
+ only volumes with matching attributes are added to the
+ list of volumes. bits in the vMAttribMask should use the
+ same encoding as bits in the vMAttrib field of
+ the GetVolParmsInfoBuffer structure. */
+OSErr BuildVolumeList(Boolean includeRemote, short *vols,
+ long *count, long vMAttribMask) {
+ HParamBlockRec volPB;
+ Boolean isRemote;
+ OSErr err;
+ long nlocal, nremote;
+ long vMAttrib;
+
+ /* set up and check parameters */
+ volPB.volumeParam.ioNamePtr = NULL;
+ nlocal = nremote = 0;
+ if (*count == 0) return noErr;
+
+ /* iterate through volumes */
+ for (volPB.volumeParam.ioVolIndex = 1;
+ PBHGetVInfoSync(&volPB) == noErr;
+ volPB.volumeParam.ioVolIndex++) {
+
+ /* skip remote volumes, if necessary */
+ err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib);
+ if (err != noErr) goto bail;
+ if ( ( includeRemote || ! isRemote )
+ && (vMAttrib & vMAttribMask) == vMAttribMask ) {
+
+ /* add local volumes at the front, remote
+ volumes at the end */
+ if (isRemote)
+ vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum;
+ else {
+ if (nremote > 0)
+ BlockMoveData(vols+nlocal, vols+nlocal+1,
+ nremote*sizeof(short));
+ vols[nlocal++] = volPB.volumeParam.ioVRefNum;
+ }
+
+ /* list full? */
+ if ((nlocal + nremote) >= *count) break;
+ }
+ }
+bail:
+ *count = (nlocal + nremote);
+ return err;
+}
+
+
+ /* FindApplication iterates through mounted volumes
+ searching for an application with the given creator
+ type. If includeRemote is true, then remote volumes
+ will be searched (after local ones) for an application
+ with the creator type. */
+
+#define kMaxVols 20
+
+/* Hacked to output to appName */
+
+OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) {
+ short rRefNums[kMaxVols];
+ long i, volCount;
+ DTPBRec desktopPB;
+ OSErr err;
+
+ /* get a list of volumes - with desktop files */
+ volCount = kMaxVols;
+ err = BuildVolumeList(includeRemote, rRefNums, &volCount,
+ (1<<bHasDesktopMgr) );
+ if (err != noErr) return err;
+
+ /* iterate through the list */
+ for (i=0; i<volCount; i++) {
+
+ /* has a desktop file? */
+ desktopPB.ioCompletion = NULL;
+ desktopPB.ioVRefNum = rRefNums[i];
+ desktopPB.ioNamePtr = NULL;
+ desktopPB.ioIndex = 0;
+ err = PBDTGetPath(&desktopPB);
+ if (err != noErr) continue;
+
+ /* has the correct app?? */
+ desktopPB.ioFileCreator = appCreator;
+ desktopPB.ioNamePtr = appName;
+ err = PBDTGetAPPLSync(&desktopPB);
+ if (err != noErr) continue;
+
+ /* make a file spec referring to it */
+ err = FSMakeFSSpec(rRefNums[i],
+ desktopPB.ioAPPLParID, appName,
+ appSpec);
+ if (err != noErr) continue;
+
+ /* found it! */
+ return noErr;
+
+ }
+ return fnfErr;
+}
+
+/* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
+
+//yeah, duplicated code
+pascal OSErr FSpGetFullPath(const FSSpec *spec,
+ short *fullPathLength,
+ Handle *fullPath)
+{
+ OSErr result;
+ OSErr realResult;
+ FSSpec tempSpec;
+ CInfoPBRec pb;
+
+ *fullPathLength = 0;
+ *fullPath = NULL;
+
+
+ /* Default to noErr */
+ realResult = result = noErr;
+
+ /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
+/*
+ if ( spec->name[0] == 0 )
+ {
+ result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
+ }
+ else
+ {
+*/
+ /* Make a copy of the input FSSpec that can be modified */
+ BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
+/* }*/
+
+ if ( result == noErr )
+ {
+ if ( tempSpec.parID == fsRtParID )
+ {
+ /* The object is a volume */
+
+ /* Add a colon to make it a full pathname */
+ ++tempSpec.name[0];
+ tempSpec.name[tempSpec.name[0]] = ':';
+
+ /* We're done */
+ result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
+ }
+ else
+ {
+ /* The object isn't a volume */
+
+ /* Is the object a file or a directory? */
+ pb.dirInfo.ioNamePtr = tempSpec.name;
+ pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
+ pb.dirInfo.ioDrDirID = tempSpec.parID;
+ pb.dirInfo.ioFDirIndex = 0;
+ result = PBGetCatInfoSync(&pb);
+ /* Allow file/directory name at end of path to not exist. */
+ realResult = result;
+ if ( (result == noErr) || (result == fnfErr) )
+ {
+ /* if the object is a directory, append a colon so full pathname ends with colon */
+ if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
+ {
+ ++tempSpec.name[0];
+ tempSpec.name[tempSpec.name[0]] = ':';
+ }
+
+ /* Put the object name in first */
+ result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
+ if ( result == noErr )
+ {
+ /* Get the ancestor directory names */
+ pb.dirInfo.ioNamePtr = tempSpec.name;
+ pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
+ pb.dirInfo.ioDrParID = tempSpec.parID;
+ do /* loop until we have an error or find the root directory */
+ {
+ pb.dirInfo.ioFDirIndex = -1;
+ pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
+ result = PBGetCatInfoSync(&pb);
+ if ( result == noErr )
+ {
+ /* Append colon to directory name */
+ ++tempSpec.name[0];
+ tempSpec.name[tempSpec.name[0]] = ':';
+
+ /* Add directory name to beginning of fullPath */
+ (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]);
+ result = MemError();
+ }
+ } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
+ }
+ }
+ }
+ }
+
+ if ( result == noErr )
+ {
+ /* Return the length */
+ *fullPathLength = GetHandleSize(*fullPath);
+ result = realResult; /* return realResult in case it was fnfErr */
+ }
+ else
+ {
+ /* Dispose of the handle and return NULL and zero length */
+ if ( *fullPath != NULL )
+ {
+ DisposeHandle(*fullPath);
+ }
+ *fullPath = NULL;
+ *fullPathLength = 0;
+ }
+
+ return ( result );
+}
+
+//
+// On the mac there are two ways to open a file - one is through apple events and the
+// finder, another is through mime types.
+//
+// So, really there are two ways to implement wxFileType...
+//
+// Mime types are only available on OS 8.1+ through the InternetConfig API
+//
+// Much like the old-style file manager, it has 3 levels of flexibility for its methods -
+// Low - which means you have to iterate yourself through the mime database
+// Medium - which lets you sort of cache the database if you want to use lowlevel functions
+// High - which requires access to the database every time
+//
+// We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
+// and mid-level functions
+//
+// TODO: Should we call ICBegin/ICEnd? Then where?
+//
+
+// debug helper
+inline void wxLogMimeDebug(const wxChar* szMsg, OSStatus status)
+{
+ wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg, __LINE__, (int)status));
+}