lafagnosticuifoundation/cone/src/coecontrolarray.cpp
changeset 0 2f259fa3e83a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lafagnosticuifoundation/cone/src/coecontrolarray.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,672 @@
+// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <coecontrolarray.h>	// class CCoeControlArray
+#include <coecntrl.h>			// class CCoeControl
+#include "coepanic.h"			// Cone panic codes
+#include <e32std.h>				// class EUser
+
+/**
+Flags used internally.
+*/
+enum TFlags
+	{
+	/** Controls are owned externally. This means that the destructor
+	shouldn't delete the individual controls. 
+	*/
+	EControlsOwnedExternally = 0x01,
+	/** When the array is locked any attempt to add or remove elements will result in KErrLocked.
+	*/
+	EArrayLocked = 0x02
+	};
+
+LOCAL_C void CleanupComponentControl(TAny* aCleanupItem)	
+	{
+	CCoeControl* control = reinterpret_cast<CCoeControl*>(aCleanupItem);
+	if (control)
+		{
+		control->RemoveFromParent();
+		delete control;
+		}
+	}
+
+LOCAL_C void CleanupExternallyOwnedComponentControl(TAny* aCleanupItem)	
+	{
+	CCoeControl* control = reinterpret_cast<CCoeControl*>(aCleanupItem);
+	if (control)
+		{
+		control->RemoveFromParent();
+		}
+	}
+
+//
+// class CCoeControlArray::TCursor
+//
+
+/** Updates the cursor so that it points to the previous element in the array.
+If the current element is the first one then this function does nothing and returns EFalse.
+
+@return EFalse if the current element was the first one in the array, ETrue if it wasn't and the function
+actually did something.
+*/
+EXPORT_C TBool CCoeControlArray::TCursor::Prev()
+	{
+	if(iIndex <= 0)
+		return EFalse;
+	
+	iIndex--;
+	UpdateMemento();
+	return ETrue;
+	}
+	
+/** Updates the cursor so that it points to the next element in the array.
+If the current element is the last one then this function returns EFalse.
+
+@return EFalse if the current element was the last one in the array, ETrue otherwise 
+*/
+EXPORT_C TBool CCoeControlArray::TCursor::Next()
+	{
+	const TInt count = iArray->Count();
+	if(iIndex >= count-1) //If last element or beyond move to just off the end of the array
+		{
+		iIndex = count;
+		iMemento = TCoeControlWithId(KCoeNoControlId);
+		return EFalse;
+		}	
+	iIndex++;
+	UpdateMemento();
+	return ETrue;
+	}
+
+/** Checks if the cursor is valid. Use this to replace the comparison with NULL that you would
+do with pointers. This function is typically used on the cursors returned by the find operations.
+@return ETrue if the cursor points to a control, EFalse otherwise
+*/
+EXPORT_C TBool CCoeControlArray::TCursor::IsValid() const
+	{
+	UpdateIndex();
+	return (iIndex != KErrNotFound);
+	}
+
+/** Checks if the cursors are equal. Cursors are equal if they point to the same control in the
+same array else they are different.
+@param aCursor The other cursor.
+@return ETrue if the cursors point to the same position in the same array, EFalse 
+otherwise.
+*/
+EXPORT_C TBool CCoeControlArray::TCursor::operator ==(const TCursor& aCursor) const
+	{
+	UpdateIndex();
+	aCursor.UpdateIndex();
+	
+	ASSERT(IsValid());
+	ASSERT(aCursor.IsValid());
+	
+	return (iIndex == aCursor.iIndex && iArray == aCursor.iArray);
+	}
+
+/** Checks if the cursors are different. Cursors are equal if they point to the same control in the
+same array else they are different.
+@param aCursor The other cursor.
+@return EFalse if the cursors point to the same position in the same array, EFalse 
+otherwise.
+*/
+EXPORT_C TBool CCoeControlArray::TCursor::operator !=(const TCursor& aCursor) const
+	{
+	UpdateIndex();	
+	aCursor.UpdateIndex();
+	
+	ASSERT(IsValid());
+	ASSERT(aCursor.IsValid());
+	
+	return !(iIndex == aCursor.iIndex && iArray == aCursor.iArray);
+	}
+
+/** Constructor.
+@param aArray The array
+@param aIndex The index into the array
+*/
+CCoeControlArray::TCursor::TCursor(const CCoeControlArray& aArray, TInt aIndex) : 
+	iArray(&aArray), iIndex(aIndex), iMemento(KCoeNoControlId)
+	{
+	if(aIndex >= 0 && aIndex < aArray.Count())
+		iMemento = TCoeControlWithId(aArray.At(aIndex));
+	}
+
+/** Gets the index of the cursor.
+@return The index i.e. the position in the array
+*/
+TInt CCoeControlArray::TCursor::Index() const
+	{
+	UpdateIndex();
+	return iIndex;
+	}
+
+/** Gets the control of the cursor.
+@return The control.
+*/
+EXPORT_C CCoeControl* CCoeControlArray::TCursor::Ctrl() const
+	{
+	UpdateIndex();
+	if(iIndex == KErrNotFound || iIndex >= iArray->Count())
+		return NULL;
+	else
+		return iArray->At(iIndex).iControl;
+	}
+
+/** This function is used by any operation that requires an update of the memento.
+*/
+void CCoeControlArray::TCursor::UpdateMemento() const
+	{
+	if(iIndex >= 0 && iIndex < iArray->Count())
+		iMemento = iArray->At(iIndex);
+	else
+		{
+		iIndex = KErrNotFound;
+		iMemento = TCoeControlWithId(KErrNotFound);
+		}
+	}
+
+/** This function is used by any operation that requires an update of the index.
+*/
+void CCoeControlArray::TCursor::UpdateIndex() const
+	{
+	if(!iMemento.iControl)	// If we don't know what control this cursor should point to
+		{
+		// This is either an invalid cursor (if iIndex == -1) 
+		// or one that points to the element just beyond the end of the array (iIndex == iArray->Count())
+		
+		// If the iIndex is pointing anywhere but at CCoeControlArray::End() it has gone bad
+		if(iIndex != KErrNotFound && iIndex != iArray->Count())
+			{
+			iIndex = KErrNotFound;
+			iMemento = TCoeControlWithId(KErrNotFound);
+			}
+	
+		return;
+		}
+	
+	if(iIndex >= 0 && iIndex < iArray->Count())	// If the index is within the valid range
+		{
+		// Check that the control at iIndex is the same as the memento
+			
+		const TCoeControlWithId controlWithId = iArray->At(iIndex);
+		if(iMemento.iControl != controlWithId.iControl)	// If not, update the index to match the memento
+			{
+			const TInt newIndex = iArray->Find(iMemento.iControl).iIndex;
+			if(newIndex != KErrNotFound)
+				iIndex = newIndex;
+			else
+				{
+				// If the memento control is no longer in the array, try update the memento instead
+				UpdateMemento();
+				}
+			}
+		}
+	else
+		{
+		const TInt newIndex = iArray->Find(iMemento.iControl).iIndex;
+		if(newIndex != KErrNotFound)
+			iIndex = newIndex;
+		else
+			iMemento = TCoeControlWithId(KErrNotFound);	
+		}
+	}
+
+
+//
+// class CCoeControlArray
+//
+
+/** Creates a new CCoeControlArray.
+@param aOwner The control that owns the new array
+@return A new CCoeControlArray instance
+*/
+EXPORT_C CCoeControlArray* CCoeControlArray::NewL(CCoeControl& aOwner)
+	{
+	CCoeControlArray* self = new (ELeave) CCoeControlArray(aOwner);
+	return self;
+	}
+
+/** Constructor.
+*/
+TCoeControlWithId::TCoeControlWithId(TInt aControlId, CCoeControl* aControl) : iControl(aControl), iId(aControlId)
+	{
+	}
+	
+/** The destructor will delete the controls in the array only if the EControlsOwnedExternally flag is not set.
+*/
+EXPORT_C CCoeControlArray::~CCoeControlArray()
+	{
+	if(!ControlsOwnedExternally())
+		ResetAndDestroy();
+	
+	iControls.Close();
+ 	}
+
+const TInt KControlArrayGranularity = 1;
+
+/** Constructor 
+@param aOwner The control that owns the new array
+*/
+EXPORT_C CCoeControlArray::CCoeControlArray(CCoeControl& aOwner)
+ : iOwner(aOwner), iControls(KControlArrayGranularity, _FOFF(TCoeControlWithId, iId))
+	{
+	}
+
+/** Gets the number of elements in the array.
+@return The number of elements in the array
+*/
+EXPORT_C TInt CCoeControlArray::Count() const
+	{
+	return iControls.Count();
+	}
+
+/** Removes all the controls from the array but doesn't delete them.
+*/
+EXPORT_C void CCoeControlArray::Reset()
+	{
+	iControls.Reset();
+	}
+
+/** Removes all the controls from the array and deletes them.
+*/
+EXPORT_C void CCoeControlArray::ResetAndDestroy()
+	{
+	for(TInt i = iControls.Count()-1; i >= 0; i--)	// remove from the end, so we don't need to move any items
+		{
+		delete iControls[i].iControl;
+		// The array must be shrunk immediately to avoid Kern-Exec 3 when calling CCoeControl::Components() 
+		// or CCoeControl::ComponentControl(TInt aIndex) inside MCoeFocusObserver::HandleDestructionOfFocusedItem
+ 		iControls.Remove(i);
+		}
+
+	iControls.Reset();
+	}
+
+/** Sorts the controls using their index as the key.
+*/
+EXPORT_C void CCoeControlArray::SortById()
+	{
+	iControls.SortSigned();
+	}
+
+/**This function provides a pluggable implementation to sort the array of controls.
+@param aOrder The user defined static method which implements the algorithm for sorting.
+*/
+EXPORT_C void CCoeControlArray::Sort(TLinearOrder< TCoeControlWithId > aOrder)
+	{
+	iControls.Sort(aOrder);
+	}
+/** This function checks if the controls are owned by the array or not. If the controls are owned by the array they
+will be deleted when the array is destroyed else they will not.
+
+@return ETrue if the array does NOT own the controls, EFalse if the array owns the controls.
+*/
+EXPORT_C TBool CCoeControlArray::ControlsOwnedExternally() const
+	{
+	return (iFlags&EControlsOwnedExternally);
+	}
+
+/** Is used to set whether the array owns the controls or not. If the controls are owned by the array they
+will be deleted when the array is destroyed else they will not.
+
+@param aOwnedExternally ETrue if the controls are owned externally, EFalse if
+they are owned by the array.
+*/
+EXPORT_C void CCoeControlArray::SetControlsOwnedExternally(TBool aOwnedExternally)
+	{
+	if(aOwnedExternally)
+		{
+		iFlags |= EControlsOwnedExternally;
+		}
+	else 
+		{
+		iFlags &= ~EControlsOwnedExternally;
+		}
+	}
+
+/** Checks whether the array is locked or not. If an array is locked any attempt to add or remove controls
+will fail with KErrLocked.
+
+@return ETrue if the array is locked, EFalse otherwise
+*/
+EXPORT_C TBool CCoeControlArray::IsArrayLocked() const
+	{
+	return (iFlags&EArrayLocked);
+	}
+
+/** Locks the array. If an array is locked any attempt to add or remove controls
+will fail with KErrLocked.
+*/
+EXPORT_C void CCoeControlArray::SetArrayLocked()
+	{
+	iFlags |= EArrayLocked;
+	}
+	
+/** Gets a cursor that points to the first element of the array. Note that if the array is empty
+this is actually equivalent to a call to End().
+
+@return A cursor that points to the first control in the array.
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::Begin() const
+	{
+	return TCursor(*this, 0);
+	}
+
+/** Gets a cursor to the position right after the last element in the array. To get the last element
+use Prev(). This cursor is useful as argument to the insertion function to add the new control at the end of the array.
+
+@return A cursor that points right after the last element
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::End() const
+	{
+	return TCursor(*this, Count());
+	}
+
+/** Gets a cursor to the control, if the control is found in the array. Use
+the TCursor::IsValid function to check that the search found something or not.
+
+@param aControl The control to find.
+@return A cursor to the control. This may be an invalid cursor if we didn't 
+find the requested control. Use TCursor::IsValid() to check if the cursor is valid.
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::Find(const CCoeControl* aControl) const
+	{
+	const TInt numControls = iControls.Count();
+	for(TInt index = 0; index < numControls; index++)
+		{
+		if(iControls[index].iControl == aControl)
+			return TCursor(*this, index);
+		}
+	
+	return TCursor(*this, KCoeNoControlId);
+	}
+	
+/** Gets a cursor to the control, if the control with the given id is found in the array.
+Use the TCusror::IsValid function to check that the search found something or not.
+
+@param aControlId The id of the control to find.
+@return A cursor to the control. This may be an invalid cursor if we didn't 
+find the requested control. Use TCursor::IsValid() to check if the cursor is valid.
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::Find(TInt aControlId) const
+	{
+	const TInt index = iControls.Find(TCoeControlWithId(aControlId));
+	return TCursor(*this, index);
+	}
+
+/** Appends a control at the end of the array.
+
+@param aControl The control to add
+@param aControlId The id for the new control
+@return A cursor to the added control
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::AppendLC(CCoeControl* aControl, TInt aControlId)
+	{
+	TCursor cursor = End();
+	InsertLC(cursor, aControl, aControlId);
+	return cursor;
+	}
+	
+/** Inserts a control after the control with the given id.
+
+Each array has an owner (an instance of the CCoeControl class) which is the container of all the child controls. 
+Each control has a parent and for the child controls this parent must be the container. This function will automatically 
+update the parent of aControl.
+
+The function will also result in the CCoeControl::HandleControlArrayEventL method being called
+on the owner of the array. The event being generated is EControlAdded.
+
+@param aInsertAfterId The id of the control after which we want to insert the new control. If a control with this 
+id can't be found in the array the function will leave with KErrNotFound.
+@param aControl The new control we want to add.
+@param aControlId The id of the new control.
+@return A cursor to the added control
+@leave KErrLocked if the array has been locked using the CCoeControlArray::SetArrayLocked() function.
+@leave KErrNotFound if the array doesn't contain a control identified by aInsertAfterId. 
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::InsertAfterLC(TInt aInsertAfterId, CCoeControl* aControl, TInt aControlId)
+	{
+	const TInt index = IndexById(aInsertAfterId);
+	if(index == KErrNotFound)
+		{
+		if(!(iFlags & EControlsOwnedExternally))
+			delete aControl;
+		User::Leave(KErrNotFound);
+		}
+	TCursor cursor(*this, index);
+	cursor.Next();	// insert before the next item
+	return(InsertLC(cursor, aControl, aControlId));
+	}
+
+/** Inserts a control at the given position. 
+
+Each array has an owner (an instance of the CCoeControl class) which is the container of all the child controls. 
+Each control has a parent and for the child controls this parent must be the container. This function will automatically 
+update the parent of aControl.
+
+The function will also result in the CCoeControl::HandleControlArrayEventL method being called
+on the owner of the array. The event being generated is EControlAdded.
+
+@param aInsertAt The position at which we want to insert the new control.
+@param aControl The new control we want to add.
+@param aControlId The id of the new control.
+@return A cursor to the added control
+@leave KErrLocked if the array has been locked using the CCoeControlArray::SetArrayLocked() function.
+*/
+EXPORT_C CCoeControlArray::TCursor CCoeControlArray::InsertLC(TCursor& aInsertAt, CCoeControl* aControl, TInt aControlId)
+	{
+ 	__ASSERT_DEBUG(this, Panic(ECoePanicNonExistentArray));	
+	
+	const TBool externallyOwned = iFlags & EControlsOwnedExternally;
+	
+	// Set parent before pushing the CleanupComponenetControl, so that we can 
+	// look for external ownership on parent if something leaves
+	const TInt err = aControl->SetParent(&iOwner);
+	if(err != KErrNone)
+		{
+		if(!externallyOwned)
+			delete aControl;
+		User::Leave(err);
+		}
+	
+	CleanupStack::PushL(TCleanupItem((externallyOwned ? 
+										CleanupExternallyOwnedComponentControl : 
+										CleanupComponentControl),
+									 aControl));
+
+	if(IsArrayLocked())
+		User::Leave(KErrLocked);
+
+#ifdef _DEBUG	
+	__ASSERT_DEBUG(aControlId == KCoeNoControlId || 
+			iControls.Find(TCoeControlWithId(aControlId)) == KErrNotFound, Panic(ECoePanicDuplicateControlId));
+#endif	
+
+	// To preserve the insertion order of controls without assigned ID
+	if(aControlId == KCoeNoControlId)	// If the control has not been given an ID...
+		{
+		// ...loop through all controls and find the largest negative ID (i.e. the one closest to zero)
+		TInt autoId = KMinTInt32;
+		const TInt count = iControls.Count();
+		for(TInt i = 0; i < count; i++)
+			{
+			const TInt controlId = iControls[i].iId;
+			if(controlId < 0 && autoId <= controlId)
+				{
+				autoId = controlId+1;
+				}
+			}
+			
+		aControlId = autoId;	// Give the new control the next larger negative ID
+		}
+		
+	iControls.InsertL(TCoeControlWithId(aControlId, aControl), aInsertAt.Index());
+	iOwner.HandleControlArrayEventL(EControlAdded, this, aControl, aControlId);	
+	
+	const TCursor newItemCursor = aInsertAt; 
+	aInsertAt.Next();	// Move the cursor forward to presserve its position in the array
+	
+	return newItemCursor;
+	}
+	
+/** Removes a control but does NOT delete the control itself. The ownership of the control is 
+transferred to the caller.
+
+The function will also result in the CCoeControl::HandleControlArrayEventL method being called
+on the owner of the array. The event being generated is EControlRemoved.
+
+@param aControl The control to remove
+@return KErrNone if the control was removed successfully, KErrNotFound if the control could not be found.
+*/
+EXPORT_C TInt CCoeControlArray::Remove(const CCoeControl* aControl)
+	{
+	for(TInt i = 0; i < iControls.Count(); i++)
+		{
+		const TCoeControlWithId controlWithId = iControls[i];
+		if(controlWithId.iControl == aControl)
+			{
+			iControls.Remove(i);
+			// EControlRemoved event must never cause leave
+			TRAP_IGNORE(iOwner.HandleControlArrayEventL(EControlRemoved, this, controlWithId.iControl, controlWithId.iId));	
+			return KErrNone;	
+			}
+		}
+		
+	return KErrNotFound;
+	}
+
+/** Removes a control but does NOT delete the control itself. The ownership of the control is 
+transferred to the caller.
+
+The function will also result in the CCoeControl::HandleControlArrayEventL method being called
+on the owner of the array. The event being generated is EControlRemoved.
+
+@param aRemoveAt The position of the control to remove
+@return A pointer to the control that has been removed. This can be used by the caller to delete the control.
+*/
+EXPORT_C CCoeControl* CCoeControlArray::Remove(TCursor aRemoveAt)
+	{
+	const TCoeControlWithId controlWithId = iControls[aRemoveAt.Index()];
+	iControls.Remove(aRemoveAt.Index());
+	// EControlRemoved event must never cause leave
+	TRAP_IGNORE(iOwner.HandleControlArrayEventL(EControlRemoved, this, controlWithId.iControl, controlWithId.iId));	
+
+	return controlWithId.iControl;	
+	}
+
+/** Removes a control but does NOT delete the control itself. The ownership of the control is 
+transferred to the caller.
+
+The function will also result in the CCoeControl::HandleControlArrayEventL method being called
+on the owner of the array. The event being generated is EControlRemoved.
+
+@param aControlId The id of the control to remove
+@return A pointer to the control that has been removed. This can be used by the caller to delete the control. The function
+returns NULL if no control with the given aControlId has been found.
+*/	
+EXPORT_C CCoeControl* CCoeControlArray::RemoveById(TInt aControlId)
+	{
+	const TInt index = IndexById(aControlId);
+	if(index != KErrNotFound)
+		{
+		const TCoeControlWithId controlWithId = iControls[index];
+		iControls.Remove(index);
+		// EControlRemoved event must never cause leave
+		TRAP_IGNORE(iOwner.HandleControlArrayEventL(EControlRemoved, this, controlWithId.iControl, controlWithId.iId));	
+		return controlWithId.iControl;
+		}
+		
+	return NULL;	
+	}
+	
+/** Gets the element at the given index.
+@param aIndex The index of the control
+@return The control and its id
+*/
+EXPORT_C TCoeControlWithId CCoeControlArray::At(TInt aIndex)
+	{
+	return iControls[aIndex];	
+	}
+
+/** Gets the element at the given index.
+@param aIndex The index of the control
+@return The control and its id
+*/
+EXPORT_C const TCoeControlWithId CCoeControlArray::At(TInt aIndex) const
+	{
+	return iControls[aIndex];	
+	}
+	
+/** Gets the id of the control.
+@param aControl The control
+@return The id of the control or KErrNotFound if the control can't be found.
+*/
+EXPORT_C TInt CCoeControlArray::Id(const CCoeControl& aControl) const
+	{
+	for(TInt i = 0; i < iControls.Count(); i++)
+		{
+		if(iControls[i].iControl == &aControl)
+			return iControls[i].iId;
+		}
+		
+	return KErrNotFound;
+	}
+
+/** Replaces a control in the array with another control.
+
+@param aOriginalControl The control that must be replaced
+@param aNewControl The new control
+@return A standard error code
+*/
+EXPORT_C TInt CCoeControlArray::Replace(CCoeControl* aOriginalControl, CCoeControl* aNewControl)
+	{
+ 	__ASSERT_DEBUG(this, Panic(ECoePanicNonExistentArray));		
+	for(TInt i = 0; i < iControls.Count(); i++)
+		{
+		if(iControls[i].iControl == aOriginalControl)
+			{
+			iControls[i].iControl = aNewControl;
+			return KErrNone;
+			}
+		}
+		
+	return KErrNotFound;
+	}
+
+/** Gets the control with the given id or NULL if there is no control with the given id.
+@param aControlId The id of the control
+@return The control with the given id or null.
+*/
+EXPORT_C CCoeControl* CCoeControlArray::CtrlById(TInt aControlId) const
+	{
+	const TInt index = IndexById(aControlId);
+	return (index != KErrNotFound ? iControls[index].iControl : NULL);
+	}
+
+/** Gets the index of the control with the given id or KErrNotFound if there is no control that has
+that id.
+@param aControlId The id of the control
+@return The index of the control with the given id or KErrNotFound.
+*/
+TInt CCoeControlArray::IndexById(TInt aControlId) const
+	{
+	if(aControlId == KErrNotFound)
+		return KErrNotFound;
+	
+	const TInt index = iControls.Find(TCoeControlWithId(aControlId));
+	return index;
+	}