//************************************************************************************** // Filename: CFAliasArray.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 "CFAliasArray.h" #include "CFObjDel.h" CFAliasArray::CFAliasArray(void) { mArray = ::CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); //mArray may return NULL, we do not throw, //but all functions in this class must be prepared for NULL and check for it mDataType = ::CFDataGetTypeID(); } CFAliasArray::CFAliasArray(CFStringRef inKey, CFStringRef inPrefsIdentifier) : mArray(NULL) { mDataType = ::CFDataGetTypeID(); LoadArrayFromPrefs(inKey, inPrefsIdentifier); } CFAliasArray::~CFAliasArray(void) { if(mArray != NULL) { ::CFRelease(mArray); mArray = NULL; } } CFIndex CFAliasArray::GetCount() const { if(mArray == NULL) return 0; return ::CFArrayGetCount(mArray); } void CFAliasArray::AddItem(const FSRef *inRef) { 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) == mDataType) ) { CFDataRef dataRef = (CFDataRef)theItem; CFIndex theLen = ::CFDataGetLength(dataRef); AliasHandle newAliasH = (AliasHandle)::NewHandleClear(theLen); if(newAliasH != NULL) { const UInt8 * dataPtr = ::CFDataGetBytePtr(dataRef); ::HLock( (Handle)newAliasH ); ::BlockMoveData(dataPtr, *newAliasH, theLen); ::HUnlock( (Handle)newAliasH ); FSRef newRef; Boolean wasChanged = false; err = ::FSResolveAliasWithMountFlags( NULL, newAliasH, &newRef, &wasChanged, kResolveAliasFileNoUI); ::DisposeHandle( (Handle)newAliasH ); if(err == noErr) { err = ::FSCompareFSRefs( inRef, &newRef ); if(err == noErr) {//objects equal, do not add it to our list return; } } } } } //add item AliasHandle aliasH = NULL; err = ::FSNewAlias( NULL, inRef, &aliasH); if(aliasH == NULL) return; if( err == noErr ) { ::HLock( (Handle)aliasH ); CFDataRef theDataRef = ::CFDataCreate( kCFAllocatorDefault, (const UInt8 *)*aliasH, ::GetHandleSize((Handle)aliasH) ); ::DisposeHandle( (Handle)aliasH ); if(theDataRef != NULL) { ::CFArrayAppendValue( mArray, (const void *)theDataRef ); } } } //zero-based index void CFAliasArray::RemoveItemAt(CFIndex inIndex) { if(mArray == NULL) return; ::CFArrayRemoveValueAtIndex( mArray, inIndex ); } void CFAliasArray::RemoveAllItems() { if(mArray == NULL) return; ::CFArrayRemoveAllValues(mArray); } //index is zero-based //returns true if valid outRef set, false otherwise Boolean CFAliasArray::FetchItemAt(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) == mDataType) ) { CFDataRef dataRef = (CFDataRef)theItem; CFIndex theLen = ::CFDataGetLength(dataRef); AliasHandle newAliasH = (AliasHandle)::NewHandleClear(theLen); if(newAliasH != NULL) { const UInt8 * dataPtr = ::CFDataGetBytePtr(dataRef); if(dataPtr != NULL) { ::HLock( (Handle)newAliasH ); ::BlockMoveData(dataPtr, *newAliasH, theLen); ::HUnlock( (Handle)newAliasH ); Boolean wasChanged = false; OSErr err = ::FSResolveAliasWithMountFlags( NULL, newAliasH, &outRef, &wasChanged, kResolveAliasFileNoUI); ::DisposeHandle( (Handle)newAliasH ); return (err == noErr); } } } } return false; } //you may need to call CFPreferencesAppSynchronize before calling this function void CFAliasArray::LoadArrayFromPrefs(CFStringRef inKey, CFStringRef inPrefsIdentifier) { if(mArray != NULL) { ::CFRelease(mArray); mArray = NULL; } if( (inKey == NULL) || (inPrefsIdentifier == NULL) ) { mArray = ::CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); return; } CFPropertyListRef resultRef = ::CFPreferencesCopyAppValue( inKey, inPrefsIdentifier ); if(resultRef != NULL) { CFObjDel refDel(resultRef); if( ::CFGetTypeID(resultRef) == ::CFArrayGetTypeID() ) {//we need a mutable copy of this array mArray = ::CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, (CFArrayRef)resultRef); } } else {//unable to read or not created yet mArray = ::CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); } } //you may need to call CFPreferencesAppSynchronize after calling this function void CFAliasArray::SaveArrayToPrefs(CFStringRef inKey, CFStringRef inPrefsIdentifier) const { if(mArray == NULL) return; ::CFPreferencesSetAppValue( inKey, (CFPropertyListRef)mArray, inPrefsIdentifier ); }