//************************************************************************************** // Filename: CFDictArray.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 CFDictionaryRefs // Design goals: // - not to throw // - load and save to preferences file via CFPreferences // - the object does own the array and deletes it // //************************************************************************************** // Revision History: // Monday, August 19, 2002 - Original //************************************************************************************** #include "CFDictArray.h" #include "CFObjDel.h" //#include "AMacHandle.h" #include "CMUtils.h" #include "CFAliasArray.h" #include "ACFNumber.h" CFDictArray::CFDictArray(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 } CFDictArray::CFDictArray(CFStringRef inKey, CFStringRef inPrefsIdentifier) : mArray(NULL) { LoadArrayFromPrefs(inKey, inPrefsIdentifier); } CFDictArray::CFDictArray(CFMutableArrayRef inArray, bool inDoRetain) : mArray(inArray) { if( (mArray != NULL) && (inDoRetain) ) ::CFRetain(inArray); } CFDictArray::CFDictArray(const CFDictArray& inArray) { mArray = ::CFArrayCreateMutableCopy ( kCFAllocatorDefault, 0, inArray.mArray ); } CFDictArray::~CFDictArray(void) { if(mArray != NULL) { ::CFRelease(mArray); mArray = NULL; } } CFDictArray& CFDictArray::operator=(const CFDictArray& inArray) { if ( this != &inArray ) { ::CFRelease(mArray); mArray = ::CFArrayCreateMutableCopy ( kCFAllocatorDefault, 0, inArray.mArray ); } return *this; } CFIndex CFDictArray::GetCount() const { if(mArray == NULL) return 0; return ::CFArrayGetCount(mArray); } void CFDictArray::AddItem(CFDictionaryRef inItem) { if( (mArray == NULL) || (inItem == NULL) ) return; ::CFArrayAppendValue( mArray, (const void *)inItem );//the dict is retained } void CFDictArray::InsertItemAt(CFDictionaryRef inItem, CFIndex inIndex) { if( (mArray == NULL) || (inItem == NULL) ) return; ::CFArrayInsertValueAtIndex(mArray, inIndex, (const void *)inItem);//the dict is retained } //zero-based index void CFDictArray::RemoveItemAt(CFIndex inIndex) { if(mArray == NULL) return; ::CFArrayRemoveValueAtIndex( mArray, inIndex ); } void CFDictArray::RemoveAllItems() { if(mArray == NULL) return; ::CFArrayRemoveAllValues(mArray); } CFIndex CFDictArray::MoveOneItem(CFIndex fromIndex, CFIndex toIndex) { if(mArray == NULL) return 0; CFIndex theCount = ::CFArrayGetCount(mArray); if( (fromIndex < 0) || (fromIndex > (theCount-1)) ) return 0;//request out of range if(toIndex < 0) toIndex = 0; else if (toIndex > theCount) toIndex = theCount; const void *ourItem = ::CFArrayGetValueAtIndex(mArray, fromIndex); ::CFArrayInsertValueAtIndex(mArray, toIndex, ourItem); if( ::CFArrayGetCount(mArray) == (theCount+1) ) {//insert succeeded CFIndex removalIndex = fromIndex; if(toIndex <= fromIndex)//inserted before old item, so old item is now shifted one position down removalIndex = fromIndex + 1; else toIndex--;//when old item is deleted in front, new index is decreased ::CFArrayRemoveValueAtIndex(mArray, removalIndex); if (toIndex == theCount) toIndex--;//should not happen if there is no error in logic, but let's make sure toIndex is valid return toIndex; } return fromIndex; } //on input, we get sorted list of indexes to move //on output we get the first index of moved block CFIndex CFDictArray::MoveItems(CFArrayRef inIndexList, CFIndex toIndex) { if((mArray == NULL) || (inIndexList == NULL)) return 0; CFIndex arrayCount = ::CFArrayGetCount(mArray); CFMutableArrayRef tempList = ::CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); CFObjDel listDel(tempList); //first get all items to move and put them in temp array CFIndex moveCount = ::CFArrayGetCount(inIndexList); for(CFIndex i = 0; i < moveCount; i++) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( inIndexList, i); if(::CFNumberGetTypeID() == ::CFGetTypeID(theItem)) { ACFNumber theIndex((CFNumberRef)theItem); CFIndex arrayIndex = (long)theIndex; if((arrayIndex >= 0) && (arrayIndex < arrayCount) ) {//valid index const void *ourItem = ::CFArrayGetValueAtIndex(mArray, arrayIndex); ::CFArrayAppendValue( tempList, ourItem ); } } } //now we have all items in our tem array //we may remove them from original array //we need to calculate the new insertion index while doing it for(CFIndex i = (moveCount-1); i >= 0 ; i--) { CFTypeRef theItem = ::CFArrayGetValueAtIndex( inIndexList, i); if(::CFNumberGetTypeID() == ::CFGetTypeID(theItem)) { ACFNumber theIndex((CFNumberRef)theItem); CFIndex arrayIndex = (long)theIndex; if((arrayIndex >= 0) && (arrayIndex < arrayCount) ) {//valid index ::CFArrayRemoveValueAtIndex(mArray, arrayIndex); if(arrayIndex < toIndex) {//removing item before new insertion point toIndex--; } } } } CFIndex storedItemsCount = ::CFArrayGetCount(tempList); for(CFIndex i = 0; i < storedItemsCount; i++) { const void *ourItem = ::CFArrayGetValueAtIndex(tempList, i); ::CFArrayInsertValueAtIndex(mArray, toIndex + i, ourItem); } return toIndex; } //index is zero-based //do not release the result dictionary - retain it if you plan to keep it CFDictionaryRef CFDictArray::FetchItemAt(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()) ) { return (CFDictionaryRef)theItem; } } return NULL; } void CFDictArray::SetItemAt(CFDictionaryRef inItem, CFIndex inIndex) { if(mArray == NULL) return; CFIndex theCount = ::CFArrayGetCount(mArray); if( (inIndex >= 0) && (inIndex < theCount) ) { ::CFArraySetValueAtIndex( mArray, inIndex, (const void *)inItem); } } //you may need to call CFPreferencesAppSynchronize before calling this function void CFDictArray::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 CFDictArray::SaveArrayToPrefs(CFStringRef inKey, CFStringRef inPrefsIdentifier) const { if(mArray == NULL) return; ::CFPreferencesSetAppValue( inKey, (CFPropertyListRef)mArray, inPrefsIdentifier ); } #pragma mark - //TODO: support other data types for dictionary CFStringRef CFDictArray::GetItemStringForKey(CFIndex inIndex, CFStringRef inKey) { CFDictionaryRef theDictItem = FetchItemAt(inIndex); if(theDictItem == NULL) return NULL; CFTypeRef theResult = ::CFDictionaryGetValue( theDictItem, inKey ); if((theResult != NULL) && (::CFStringGetTypeID() == ::CFGetTypeID(theResult)) ) { return (CFStringRef)theResult; } return NULL; } void CFDictArray::SetItemStringForKey(CFIndex inIndex, CFStringRef inKey, CFStringRef inString) { CFMutableDictionaryRef theDictItem = (CFMutableDictionaryRef)FetchItemAt(inIndex); if(theDictItem == NULL) return; ::CFDictionarySetValue(theDictItem, inKey, inString); } Boolean CFDictArray::GetItemBooleanForKey(CFIndex inIndex, CFStringRef inKey, Boolean defaultValue) { CFDictionaryRef theDictItem = FetchItemAt(inIndex); if(theDictItem == NULL) return defaultValue; CFTypeRef theResult = ::CFDictionaryGetValue( theDictItem, inKey ); if((theResult != NULL) && (::CFBooleanGetTypeID() == ::CFGetTypeID(theResult)) ) { return ::CFBooleanGetValue( (CFBooleanRef)theResult ); } return defaultValue; } void CFDictArray::SetItemBooleanForKey(CFIndex inIndex, CFStringRef inKey, Boolean inValue) { CFMutableDictionaryRef theDictItem = (CFMutableDictionaryRef)FetchItemAt(inIndex); if(theDictItem == NULL) return; ::CFDictionarySetValue(theDictItem, inKey, inValue ? kCFBooleanTrue : kCFBooleanFalse); }