//************************************************************************************** // Filename: CFAliasAndNameArray.cp // Part of Contextual Menu Workshop by Abracode Inc. // http://free.abracode.com/cmworkshop/ // Copyright © 2002-2003 Abracode, Inc. All rights reserved. // // Description: CFArray-based list of aliases // Design goals: // - not to throw // - unique aliases (not to allow aliases pointing to the same object) // - load and save aliases to preferences file via CFPreferences // - access items (Add & Fetch) via FSRef // - the object does own the array and deletes it // //************************************************************************************** // Revision History: // Monday, August 19, 2002 - Original //************************************************************************************** #include "CFAliasAndNameArray.h" #include "CFObjDel.h" #include "AMacHandle.h" #include "CMUtils.h" #include "CFAliasArray.h" #include "ACFNumber.h" CFAliasAndNameArray::CFAliasAndNameArray(CFStringRef inKey, CFStringRef inPrefsIdentifier, CFIndex theVer) { mArray = NULL; if(theVer == 1) { LoadArrayFromPrefsVersion1(inKey, inPrefsIdentifier); } else if(theVer == 2) { LoadArrayFromPrefs(inKey, inPrefsIdentifier); } } CFAliasAndNameArray::CFAliasAndNameArray(const CFAliasAndNameArray& inArray) { mArray = ::CFArrayCreateMutableCopy ( kCFAllocatorDefault, 0, inArray.mArray ); } CFAliasAndNameArray& CFAliasAndNameArray::operator=(const CFAliasAndNameArray& inArray) { if ( this != &inArray ) { ::CFRelease(mArray); mArray = ::CFArrayCreateMutableCopy ( kCFAllocatorDefault, 0, inArray.mArray ); } return *this; } //if inName is NULL, the name is obtained from file void CFAliasAndNameArray::AddPair(const FSRef *inRef, CFStringRef inName) { if(mArray == NULL) return; //check if item exists in our list - do not add duplicates OSStatus err = noErr; CFIndex theCount = ::CFArrayGetCount(mArray); for(CFIndex i = 0; i < theCount; i++) { CFTypeRef theItem = ::CFArrayGetValueAtIndex(mArray, i); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFDictionaryRef theDict = (CFDictionaryRef)theItem; CFTypeRef theResult = ::CFDictionaryGetValue( theDict, CFSTR("ALIAS_DATA") ); if( (theResult != NULL) && (::CFDataGetTypeID() == ::CFGetTypeID(theResult)) ) { CFDataRef dataRef = (CFDataRef)theResult; CFIndex theLen = ::CFDataGetLength(dataRef); AMacHandle newAliasH(theLen); if(newAliasH != NULL) { const UInt8 * dataPtr = ::CFDataGetBytePtr(dataRef); newAliasH.Lock(); ::BlockMoveData(dataPtr, *newAliasH, theLen); newAliasH.Unlock(); FSRef newRef; Boolean wasChanged = false; err = ::FSResolveAliasWithMountFlags( NULL, newAliasH.Get(), &newRef, &wasChanged, kResolveAliasFileNoUI); if(err == noErr) { err = ::FSCompareFSRefs( inRef, &newRef ); if(err == noErr) {//objects equal, do not add it to our list return; } } } } } } CFObjDel newStrDel; if(inName == NULL) { inName = CMUtils::CreateCFStringNameFromFSRef(inRef); newStrDel.Adopt(inName); } if(inName == NULL) inName = CFSTR("Unknown Name"); //add item AliasHandle aliasH = NULL; err = ::FSNewAlias( NULL, inRef, &aliasH); if(aliasH == NULL) return; AHandle addAliasH((Handle)aliasH);//take ownership if( err == noErr ) { CFMutableDictionaryRef theDict = ::CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(theDict == NULL) return; CFObjDel newDictDel(theDict); addAliasH.Lock(); CFDataRef theDataRef = ::CFDataCreate( kCFAllocatorDefault, (const UInt8 *)*addAliasH, addAliasH.GetSize() ); if(theDataRef != NULL) { CFObjDel newDataDel(theDataRef); ::CFDictionaryAddValue( theDict, CFSTR("NAME"), inName);//the string is retained ::CFDictionaryAddValue( theDict, CFSTR("ALIAS_DATA"), theDataRef);//the data is retained ::CFArrayAppendValue( mArray, (const void *)theDict );//the dict is retained } } } //allow duplicates here because it is for the editor, user may want it for some reason void CFAliasAndNameArray::InsertPairAt(const FSRef *inRef, CFStringRef inName, CFIndex inIndex) { if(mArray == NULL) return; CFObjDel newStrDel; if(inName == NULL) { inName = CMUtils::CreateCFStringNameFromFSRef(inRef); newStrDel.Adopt(inName); } if(inName == NULL) inName = CFSTR("Unknown Name"); //add item AliasHandle aliasH = NULL; OSStatus err = ::FSNewAlias( NULL, inRef, &aliasH); if((err != noErr) || (aliasH == NULL)) return; AHandle addAliasH((Handle)aliasH);//take ownership if( err == noErr ) { CFMutableDictionaryRef theDict = ::CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(theDict == NULL) return; CFObjDel newDictDel(theDict); addAliasH.Lock(); CFDataRef theDataRef = ::CFDataCreate( kCFAllocatorDefault, (const UInt8 *)*addAliasH, addAliasH.GetSize() ); if(theDataRef != NULL) { CFObjDel newDataDel(theDataRef); ::CFDictionaryAddValue( theDict, CFSTR("NAME"), inName);//the string is retained ::CFDictionaryAddValue( theDict, CFSTR("ALIAS_DATA"), theDataRef);//the data is retained ::CFArrayInsertValueAtIndex(mArray, inIndex, (const void *)theDict);//the dict is retained } } } //index is zero-based //returns true if valid outRef set, false otherwise Boolean CFAliasAndNameArray::FetchFSRefAt(CFIndex inIndex, FSRef &outRef) const { if(mArray == NULL) return false; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( mArray, inIndex); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFDictionaryRef theDict = (CFDictionaryRef)theItem; CFTypeRef theResult = ::CFDictionaryGetValue( theDict, CFSTR("ALIAS_DATA") ); if( (theResult != NULL) && (::CFDataGetTypeID() == ::CFGetTypeID(theResult)) ) { CFDataRef dataRef = (CFDataRef)theResult; CFIndex theLen = ::CFDataGetLength(dataRef); AMacHandle newAliasH(theLen); if(newAliasH != NULL) { const UInt8 * dataPtr = ::CFDataGetBytePtr(dataRef); if(dataPtr != NULL) { newAliasH.Lock(); ::BlockMoveData(dataPtr, *newAliasH, theLen); newAliasH.Unlock(); Boolean wasChanged = false; OSErr err = ::FSResolveAliasWithMountFlags( NULL, newAliasH.Get(), &outRef, &wasChanged, kResolveAliasFileNoUI); return (err == noErr); } } } } } return false; } Boolean CFAliasAndNameArray::FetchFSRefWithMountingAt(CFIndex inIndex, FSRef &outRef) const { if(mArray == NULL) return false; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( mArray, inIndex); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFDictionaryRef theDict = (CFDictionaryRef)theItem; CFTypeRef theResult = ::CFDictionaryGetValue( theDict, CFSTR("ALIAS_DATA") ); if( (theResult != NULL) && (::CFDataGetTypeID() == ::CFGetTypeID(theResult)) ) { CFDataRef dataRef = (CFDataRef)theResult; CFIndex theLen = ::CFDataGetLength(dataRef); AMacHandle newAliasH(theLen); if(newAliasH != NULL) { const UInt8 * dataPtr = ::CFDataGetBytePtr(dataRef); if(dataPtr != NULL) { newAliasH.Lock(); ::BlockMoveData(dataPtr, *newAliasH, theLen); newAliasH.Unlock(); Boolean wasChanged = false; OSErr err = ::FSResolveAlias( NULL, newAliasH.Get(), &outRef, &wasChanged); return (err == noErr); } } } } } return false; } //do not release the result string - retain it if you plan to keep it CFStringRef CFAliasAndNameArray::FetchNameAt(CFIndex inIndex) const { if(mArray == NULL) return NULL; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( mArray, inIndex); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFDictionaryRef theDict = (CFDictionaryRef)theItem; CFTypeRef theResult = ::CFDictionaryGetValue( theDict, CFSTR("NAME") ); if((theResult != NULL) && (::CFStringGetTypeID() == ::CFGetTypeID(theResult)) ) { return (CFStringRef)theResult; } } } return NULL; } void CFAliasAndNameArray::SetNameAt(CFStringRef inName, CFIndex inIndex) { if(mArray == NULL) return; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( mArray, inIndex); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFMutableDictionaryRef theDict = (CFMutableDictionaryRef)theItem; ::CFDictionarySetValue(theDict, CFSTR("NAME"), inName); } } } void CFAliasAndNameArray::SetFSRefAt(const FSRef &inRef, CFIndex inIndex) { if(mArray == NULL) return; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( mArray, inIndex); if( (theItem != NULL) && (::CFGetTypeID(theItem) == ::CFDictionaryGetTypeID()) ) { CFMutableDictionaryRef theDict = (CFMutableDictionaryRef)theItem; AliasHandle aliasH = NULL; OSErr err = ::FSNewAlias( NULL, &inRef, &aliasH); if(aliasH == NULL) return; AHandle addAliasH((Handle)aliasH);//take ownership if( err == noErr ) { addAliasH.Lock(); CFDataRef theDataRef = ::CFDataCreate( kCFAllocatorDefault, (const UInt8 *)*addAliasH, addAliasH.GetSize() ); if(theDataRef != NULL) { CFObjDel newDataDel(theDataRef); ::CFDictionarySetValue( theDict, CFSTR("ALIAS_DATA"), theDataRef );//the data is retained } } } } } void CFAliasAndNameArray::LoadArrayFromPrefsVersion1(CFStringRef inKey, CFStringRef inPrefsIdentifier) { if(mArray != NULL) { ::CFRelease(mArray); mArray = NULL; } mArray = ::CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); if( (inKey == NULL) || (inPrefsIdentifier == NULL) ) { return; } //create old style array, get item one by one and add to our new-style array CFAliasArray oldArray(inKey, inPrefsIdentifier); CFIndex oldCount = oldArray.GetCount(); FSRef fileRef; for(CFIndex i = 0; i < oldCount; i++) { if( oldArray.FetchItemAt(i, fileRef) ) {//only items which properly resolve are added which cuts the dead wood AddPair(&fileRef, NULL); } } }