diff -r 000000000000 -r 15bf7259bb7c uiacceltk/hitchcock/coretoolkit/src/HuiAnchorLayout.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uiacceltk/hitchcock/coretoolkit/src/HuiAnchorLayout.cpp Tue Feb 02 07:56:43 2010 +0200 @@ -0,0 +1,946 @@ +/* +* Copyright (c) 2006-2007 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: Implementation for CHuiAnchorLayout. Anchor layouts +* allow specifying the placement of child visuals using +* anchors, that can be relative or absolute coordinates and +* attached to a specific edge of the layout. +* +*/ + + + +#include "uiacceltk/HuiAnchorLayout.h" // Class definition +#include "uiacceltk/HuiControl.h" +#include "uiacceltk/HuiPanic.h" +#include "uiacceltk/HuiUtil.h" + +// +// Private structures +// + +/* + * Anchor that defines an edge's position and/or size. + */ +struct CHuiAnchorLayout::TEdgeAnchor + { + THuiAnchorProximity iEdgeProximity; + THuiMetric iOffset; + THuiAnchorProximity iAttachmentProximity; + TInt iAttachmentOrdinal; + }; + +/* + * an axis can contain up to two edges + */ +struct CHuiAnchorLayout::TAxis + { +public: + inline TInt Edge(THuiAnchorProximity aEdgeProximity, TEdgeAnchor*& aEdge) const; + inline TInt AddEdge(THuiAnchorProximity aEdgeProximity, TEdgeAnchor*& aEdge); + +public: + /** array of edges */ + TFixedArray iEdges; + }; + +inline TInt CHuiAnchorLayout::TAxis::Edge(THuiAnchorProximity aEdgeProximity, TEdgeAnchor*& aEdge) const + { + TInt error = KErrNotFound; + TInt count = iEdges.Count(); + for(TInt ii = 0; ii < count && error == KErrNotFound ; ii++) + { + if(iEdges[ii].iEdgeProximity == aEdgeProximity) + { + aEdge = const_cast(&(iEdges[ii])); // uses the const operator[] as this is a const method + error = KErrNone; + } + } + return error; + } + +TInt CHuiAnchorLayout::TAxis::AddEdge(THuiAnchorProximity aEdgeProximity, TEdgeAnchor*& aEdge) + { + TInt error = Edge(aEdgeProximity, aEdge); + if(error == KErrNotFound) + { + // we haven't found the one we're looking for, so now look for an empty slot + error = Edge(EHuiAnchorProximityNone, aEdge); + if(error == KErrNotFound) + { + // couldn't find an empty slot, so actually we consider that to be an overflow + error = KErrOverflow; + } + } + return error; + } + + +/* + * axes for a given anchor. + * + */ +struct CHuiAnchorLayout::TAnchor + { +public: + TAxis& Axis(THuiAnchorAxis aAxis) const; + +public: + /** + * array of axes indexed by THuiAnchorAxis, each one contains the edges for a given axis + */ + TFixedArray iAxes; + }; + +CHuiAnchorLayout::TAxis& CHuiAnchorLayout::TAnchor::Axis(THuiAnchorAxis aAxis) const + { + // index is an enumeration and cannot go outside array boundaries. + return const_cast(iAxes[aAxis]); + } + + +/* + * Internal private data struct. + */ +struct CHuiAnchorLayout::THuiAnchorLayoutPrivateData + { +public: + THuiAnchorLayoutPrivateData(); + +public: + /* + * Anchors for a given axis, indexed by ordinal. + * @note that if an ordinal is used that is outside the current size of the + * array, then empty items will be filled in up to the ordinal needed. However, + * in general ordinals will correspond to indexes of visuals within the layout, + * and therefore we shouldn't expect unreasonable growth of anchors. + */ + RArray iAnchors; + }; + +CHuiAnchorLayout::THuiAnchorLayoutPrivateData::THuiAnchorLayoutPrivateData() + { + } + + + +// +// Methods for compatibility with deprecated APIs +// + +THuiAnchorType CHuiAnchorLayout::ConvertDeprecatedAnchorTypeToType(THuiAnchor aAnchor) const + { + THuiAnchorType type; + switch(aAnchor) + { + case EHuiAnchorTopLeft: + type = EHuiAnchorTypeTopLeft; + break; + case EHuiAnchorBottomRight: + type = EHuiAnchorTypeBottomRight; + break; + default: + type = EHuiAnchorTypeNone; + break; + } + return type; + } + +THuiAnchorAttachmentOrigin CHuiAnchorLayout::ConvertDeprecatedOrginToAttachmentOrigin(THuiAnchorOrigin aOrigin) const + { + THuiAnchorAttachmentOrigin origin; + switch(aOrigin) + { + case EHuiAnchorOriginLeft: + origin = EHuiAnchorAttachmentOriginLeft; + break; + case EHuiAnchorOriginHCenter: + origin = EHuiAnchorAttachmentOriginHCenter; + break; + case EHuiAnchorOriginRight: + origin = EHuiAnchorAttachmentOriginRight; + break; + case EHuiAnchorOriginTop: + origin = EHuiAnchorAttachmentOriginTop; + break; + case EHuiAnchorOriginVCenter: + origin = EHuiAnchorAttachmentOriginVCenter; + break; + case EHuiAnchorOriginBottom: + origin = EHuiAnchorAttachmentOriginBottom; + break; + default: + origin = EHuiAnchorAttachmentOriginNone; + break; + } + return origin; + } + +THuiAnchorAttachmentOrigin CHuiAnchorLayout::ConvertDeprecatedOriginsToAttachmentOrigin(THuiAnchorOrigin aHorizOrigin, THuiAnchorOrigin aVertOrigin) const + { + THuiAnchorAttachmentOrigin newHorizAttachmentOrigin = ConvertDeprecatedOrginToAttachmentOrigin(aHorizOrigin); + THuiAnchorAttachmentOrigin newVertAttachmentOrigin = ConvertDeprecatedOrginToAttachmentOrigin(aVertOrigin); + THuiAnchorAttachmentOrigin origin = THuiAnchorAttachmentOrigin(newHorizAttachmentOrigin | newVertAttachmentOrigin); + return origin; + } + +THuiMetric CHuiAnchorLayout::ConvertDeprecatedAnchorMetricAndTargetToHuiMetric(THuiAnchorMetric aMetric, TReal32 aTarget) const + { + THuiUnit unit; + switch(aMetric) + { + case EHuiAnchorMetricAbsolute: + unit = EHuiUnitPixel; + break; + case EHuiAnchorMetricRelativeToSize: + unit = EHuiUnitRelativeToMySize; + break; + default: + unit = EHuiUnitPixel; + break; + } + THuiMetric metric(aTarget, unit); + return metric; + } + +THuiXYMetric CHuiAnchorLayout::ConvertDeprecatedAnchorMetricsAndOffsetToHuiMetric( + THuiAnchorMetric aHorizMetric, + THuiAnchorMetric aVertMetric, + const THuiTimedPoint& aOffset) const + { + TReal32 horizTarget = aOffset.iX.Target(); + TReal32 vertTarget = aOffset.iY.Target(); + + THuiMetric horizMetric = ConvertDeprecatedAnchorMetricAndTargetToHuiMetric(aHorizMetric, horizTarget); + THuiMetric vertMetric = ConvertDeprecatedAnchorMetricAndTargetToHuiMetric(aVertMetric, vertTarget); + + THuiXYMetric metric(horizMetric, vertMetric); + return metric; + } + +// +// Methods for dealing with internal data types +// + +THuiAnchorProximity CHuiAnchorLayout::ConvertTypeToProximity(THuiAnchorType aType, THuiAnchorAxis& aAxis) const + { + THuiAnchorProximity proximity; + + THuiAnchorProximity proximityH = THuiAnchorProximity((aType & EHuiAnchorBitmaskHorizontal) >> EHuiAnchorBitmaskShiftHorizontal); + THuiAnchorProximity proximityV = THuiAnchorProximity((aType & EHuiAnchorBitmaskVertical) >> EHuiAnchorBitmaskShiftVertical); + if(proximityH) + { + proximity = proximityH; + aAxis = EHuiAnchorAxisHorizontal; + } + else if(proximityV) + { + proximity = proximityV; + aAxis = EHuiAnchorAxisVertical; + } + else + { + proximity = EHuiAnchorProximityNone; + } + return proximity; + } + +THuiAnchorProximity CHuiAnchorLayout::ConvertAttachmentOriginToProximity(THuiAnchorAttachmentOrigin aAttachmentOrigin, THuiAnchorAxis& aAxis) const + { + THuiAnchorType type = THuiAnchorType(aAttachmentOrigin); + return ConvertTypeToProximity(type, aAxis); + } + +void CHuiAnchorLayout::ConvertCornerToEdges(THuiAnchorType aCorner, THuiAnchorType& aEdgeH, THuiAnchorType& aEdgeV) const + { + aEdgeH = THuiAnchorType(aCorner & EHuiAnchorBitmaskHorizontal); + aEdgeV = THuiAnchorType(aCorner & EHuiAnchorBitmaskVertical); + } + +void CHuiAnchorLayout::ConvertCornerAttachmentOriginToEdgeAttachmentOrigins(THuiAnchorAttachmentOrigin aCornerOrigin, THuiAnchorAttachmentOrigin& aAttachmentOriginH, THuiAnchorAttachmentOrigin& aAttachmentOriginV) const + { + aAttachmentOriginH = THuiAnchorAttachmentOrigin(aCornerOrigin & EHuiAnchorBitmaskHorizontal); + aAttachmentOriginV = THuiAnchorAttachmentOrigin(aCornerOrigin & EHuiAnchorBitmaskVertical); + } + + +// +// Methods +// + +EXPORT_C CHuiAnchorLayout* CHuiAnchorLayout::AddNewL(CHuiControl& aOwnerControl, + CHuiLayout* aParentLayout) + { + CHuiAnchorLayout* layout = STATIC_CAST(CHuiAnchorLayout*, + aOwnerControl.AppendLayoutL(EHuiLayoutTypeAnchor, aParentLayout)); + return layout; + } + + +CHuiAnchorLayout::CHuiAnchorLayout(MHuiVisualOwner& aOwner) + : CHuiLayout(aOwner) + { + } + + +void CHuiAnchorLayout::ConstructL() + { + CHuiLayout::ConstructL(); + iHuiAnchorLayoutPrivateData = new (ELeave) THuiAnchorLayoutPrivateData; + } + + +EXPORT_C CHuiAnchorLayout::~CHuiAnchorLayout() + { + Reset(); + delete iHuiAnchorLayoutPrivateData; + } + + +EXPORT_C void CHuiAnchorLayout::Reset() + { + if ( iHuiAnchorLayoutPrivateData ) // a Fix for OOM situations, tried to dereference NULL pointer + { + for(TInt ii = EHuiAnchorAxisHorizontal; ii <= EHuiAnchorAxisVertical; ii++) + { + iHuiAnchorLayoutPrivateData->iAnchors.Reset(); + } + } + } + + +EXPORT_C void CHuiAnchorLayout::SetSize(const THuiRealSize& aSize, TInt aTransitionTime) + { + CHuiLayout::SetSize(aSize, aTransitionTime); + if(!(Flags() & EHuiVisualFlagFreezeLayout)) + { + UpdateChildrenLayout(aTransitionTime); + } + } + + +EXPORT_C TBool CHuiAnchorLayout::ChildSize(TInt aOrdinal, TSize& aSize) + { + TBool result(EFalse); + THuiRealRect rect; + TInt childRectStatus(THuiLayoutChildRectUpdateNotNeeded); + childRectStatus = ChildRect(aOrdinal, rect); + if(childRectStatus != THuiLayoutChildRectNotImplemented) + { + result = (childRectStatus & THuiLayoutChildRectSizeUpdateNeeded); + if(result) + { + THuiRealPoint size(rect.Width(), rect.Height()); + aSize = LocalPointInPixels(size, EHuiReferenceStateTarget).AsSize(); + } + } + return result; + } + +EXPORT_C TBool CHuiAnchorLayout::ChildPos(TInt aOrdinal, TPoint& aPos) + { + TBool result(EFalse); + THuiRealRect rect; + TInt childRectStatus(THuiLayoutChildRectUpdateNotNeeded); + childRectStatus = ChildRect(aOrdinal, rect); + if(childRectStatus != THuiLayoutChildRectNotImplemented) + { + result = (childRectStatus & THuiLayoutChildRectPosUpdateNeeded); + if(result) + { + aPos = LocalPointInPixels(rect.iTl, EHuiReferenceStateTarget); + } + } + return result; + } + +EXPORT_C TInt CHuiAnchorLayout::ChildRect(TInt aOrdinal, THuiRealRect& aRect) + { + TInt result(THuiLayoutChildRectUpdateNotNeeded); + + // split inner area (in pixels) into separate axes + TFixedArray tl; + TFixedArray br; + + // evaluate the anchor along each axis in turn + if(aOrdinal < iHuiAnchorLayoutPrivateData->iAnchors.Count()) + { + TAnchor& anchor = iHuiAnchorLayoutPrivateData->iAnchors[aOrdinal]; + for(TInt ii = EHuiAnchorAxisHorizontal; ii <= EHuiAnchorAxisVertical; ii++) + { + THuiAnchorAxis axisId = THuiAnchorAxis(ii); + TAxis& axis = anchor.Axis(axisId); + // index is an enumeration and cannot go outside array boundaries + result |= EvaluateAxis(axisId, axis, tl[axisId], br[axisId]); + } + aRect.iTl.iX = tl[EHuiAnchorAxisHorizontal]; + aRect.iTl.iY = tl[EHuiAnchorAxisVertical]; + aRect.iBr.iX = br[EHuiAnchorAxisHorizontal]; + aRect.iBr.iY = br[EHuiAnchorAxisVertical]; + } + + if(result == THuiLayoutChildRectUpdateNotNeeded) + { + // No anchor defined at all. Use the default anchor. + THuiRealPoint topLeft = InnerTopLeft(); + THuiRealSize inner = InnerSize(); + aRect.iTl = topLeft; + aRect.iBr.iX = inner.iWidth; + aRect.iBr.iY = inner.iHeight; + result = THuiLayoutChildRectLayoutUpdateNeeded; + } + + return result; + } + + +/* + * the values from the paddings are in parent base units, whilst the anchor edges have + * their own units, so we must first convert everything to real pixels, then calculate + * the pixel positions, then convert everything back into base units of this layout visual + */ +TInt CHuiAnchorLayout::EvaluateAxis(THuiAnchorAxis aAxisId, const TAxis& aAxis, TReal32& aNear, TReal32& aFar) const + { + TInt result(THuiLayoutChildRectUpdateNotNeeded); + // @todo use cached value if optimization is needed + + // @todo refactorize with CHuiLayout::InnerSize + THuiRealRect paddingRectPx = PaddingInPixels(EHuiReferenceStateTarget); + THuiRealPoint sizePointPx = LocalPointInPixels(Size().RealTarget(), EHuiReferenceStateTarget); + THuiRealPoint innerPaddingPointPx = MetricToPixels(InnerPadding(), EHuiReferenceStateTarget); + + // switch to near and far along this axis + TReal32 nearPx = (aAxisId == EHuiAnchorAxisHorizontal) ? paddingRectPx.iTl.iX : paddingRectPx.iTl.iY; + TReal32 farPaddingPx = (aAxisId == EHuiAnchorAxisHorizontal) ? paddingRectPx.iBr.iX : paddingRectPx.iBr.iY; + TReal32 sizePx = (aAxisId == EHuiAnchorAxisHorizontal) ? sizePointPx.iX : sizePointPx.iY; + TReal32 farPx = sizePx - farPaddingPx; + TReal32 innerPaddingPx= (aAxisId == EHuiAnchorAxisHorizontal) ? innerPaddingPointPx.iX : innerPaddingPointPx.iY; + + // these will be the calculated values, which we build up one edge at a time + TReal32 nearCalculatedPx(nearPx); + TReal32 farCalculatedPx(farPx); + + // we iterate over the edges, but we don't know what order + // they were defined, so have to build up the answer as we go + TInt count = aAxis.iEdges.Count(); + for(TInt ii = 0; ii < count; ii++) + { + const TEdgeAnchor& edge = aAxis.iEdges[ii]; + if(edge.iEdgeProximity != EHuiAnchorProximityNone) + { + // now convert this edge anchor's metric offset into pixels + TReal32 offsetPx = EdgeOffsetInPixels(aAxisId, edge); + result |= EvaluateEdgeAnchorInPixels(aAxisId, edge, innerPaddingPx , offsetPx, nearPx, nearCalculatedPx, farPx, farCalculatedPx); + } + } + + // Get metric reference, for converting between base units of this layout (from the child visual's perspective) and pixels. + // We must calculate metric reference in both directions, but we only need it along this axis + THuiRealPoint childMetricRefSizePx = MetricReferenceForLayoutInPixels(BaseUnit().Abs()); // always target reference + TReal32 childMetricRefPx = (aAxisId == EHuiAnchorAxisHorizontal) ? childMetricRefSizePx.iX :childMetricRefSizePx.iY; + + // Convert the result back into child relative coordinates + THuiMetric nearCalculated = (aAxisId == EHuiAnchorAxisHorizontal) ? BaseUnit().iX :BaseUnit().iY; + THuiMetric farCalculated(nearCalculated); + ConvertPixelsToMetricLength(nearCalculated, nearCalculatedPx, childMetricRefPx); + ConvertPixelsToMetricLength(farCalculated, farCalculatedPx, childMetricRefPx); + + // back to point and size + aNear = nearCalculated.iMagnitude; + aFar = farCalculated.iMagnitude; + + return result; + } + +TReal32 CHuiAnchorLayout::EdgeOffsetInPixels(THuiAnchorAxis aAxisId, const TEdgeAnchor& aEdge) const + { + THuiRealPoint offsetRefSizePx = MetricReferenceInPixels(THuiXYMetric(aEdge.iOffset), EHuiReferenceStateTarget); + TReal32 offsetRefPx = (aAxisId == EHuiAnchorAxisHorizontal) ? offsetRefSizePx.iX :offsetRefSizePx.iY; + TReal32 offsetPx(0); + ConvertMetricLengthToPixels(offsetPx, aEdge.iOffset, offsetRefPx); + return offsetPx; + } + +TInt CHuiAnchorLayout::EvaluateEdgeAnchorInPixels( + THuiAnchorAxis aAxisId, + const TEdgeAnchor& aEdge, + TReal32 aInnerPaddingPx, + TReal32 aOffsetPx, + TReal32 aNearPx, TReal32& aNearCalculatedPx, + TReal32 aFarPx, TReal32& aFarCalculatedPx) const + { + TInt result(THuiLayoutChildRectUpdateNotNeeded); + + // first calculate the offset caused if there is an attached anchor + TReal32 attachOriginNearCalculatedPx(aNearPx); + TReal32 attachOriginFarCalculatedPx(aFarPx); + result |= EvaluateEdgeAttachmentInPixels(aAxisId, aEdge, aInnerPaddingPx, aNearPx, attachOriginNearCalculatedPx, aFarPx, attachOriginFarCalculatedPx); + + TReal32 origDimensionPx = attachOriginFarCalculatedPx - attachOriginNearCalculatedPx; + TReal32 nearOriginTransform = attachOriginNearCalculatedPx - aNearPx; + TReal32 farOriginTransform = attachOriginFarCalculatedPx - aFarPx; + + // Transform to origin + switch(aEdge.iAttachmentProximity) + { + case EHuiAnchorProximitySize: + // @todo not yet implemented + return(EFalse); + case EHuiAnchorProximityNear: + nearOriginTransform = 0; + farOriginTransform -= origDimensionPx; + break; + case EHuiAnchorProximityCenter: + nearOriginTransform += origDimensionPx / 2; + farOriginTransform -= origDimensionPx / 2; + break; + case EHuiAnchorProximityFar: + nearOriginTransform += origDimensionPx; + farOriginTransform = 0; + break; + default: + break; + } + + // Apply edge offset. + switch(aEdge.iEdgeProximity) + { + case EHuiAnchorProximitySize: + // @todo not yet implemented + return(EFalse); + case EHuiAnchorProximityNear: + aNearCalculatedPx = attachOriginNearCalculatedPx + aOffsetPx + nearOriginTransform; + result |= THuiLayoutChildRectPosUpdateNeeded; + break; + case EHuiAnchorProximityCenter: + // @todo not yet implemented + return(EFalse); + case EHuiAnchorProximityFar: + aFarCalculatedPx = attachOriginFarCalculatedPx + aOffsetPx + farOriginTransform; + result |= THuiLayoutChildRectSizeUpdateNeeded; + break; + default: + break; + } + + return result; + } + +TInt CHuiAnchorLayout::EvaluateEdgeAttachmentInPixels( + THuiAnchorAxis aAxisId, + const TEdgeAnchor& aEdge, + TReal32 aInnerPaddingPx, + TReal32 aNearPx, TReal32& aNearCalculatedPx, + TReal32 aFarPx, TReal32& aFarCalculatedPx) const + { + TInt result(THuiLayoutChildRectUpdateNotNeeded); + + // if there is an attachment, follow that + if(aEdge.iAttachmentOrdinal != EHuiAnchorAttachToParent) + { + // retrieve the edge to which we are attached + const TAnchor* nextAnchor = Anchor(aEdge.iAttachmentOrdinal); + if(nextAnchor) + { + // @todo note that with size attachments, the axis id might change here + TAxis& nextAxis = nextAnchor->Axis(aAxisId); + TEdgeAnchor* nextEdge = NULL; + TInt error = nextAxis.Edge(aEdge.iAttachmentProximity, nextEdge); + if(error == KErrNone) + { + // now need to calculate near and far for the attached edge + // @todo in the case of size attachments, need to evaluate both next edges + TReal32 nextOffsetPx = EdgeOffsetInPixels(aAxisId, *nextEdge); + TReal32 attachOriginNearCalculatedPx(aNearPx); + TReal32 attachOriginFarCalculatedPx(aFarPx); + result |= EvaluateEdgeAnchorInPixels(aAxisId, *nextEdge, aInnerPaddingPx , nextOffsetPx, aNearPx, attachOriginNearCalculatedPx, aFarPx, attachOriginFarCalculatedPx); + + switch(aEdge.iAttachmentProximity) + { + case EHuiAnchorProximitySize: + // @todo not yet implemented + return(EFalse); + case EHuiAnchorProximityNear: + aNearCalculatedPx = attachOriginNearCalculatedPx + aInnerPaddingPx; + break; + case EHuiAnchorProximityCenter: + aNearCalculatedPx = (attachOriginNearCalculatedPx + attachOriginFarCalculatedPx + aInnerPaddingPx) / 2; + aFarCalculatedPx = aNearCalculatedPx; + break; + case EHuiAnchorProximityFar: + aFarCalculatedPx = attachOriginFarCalculatedPx - aInnerPaddingPx; + break; + default: + break; + } + } + } + } + return result; + } + +const CHuiAnchorLayout::TAnchor* CHuiAnchorLayout::Anchor(TInt aOrdinal) const + { + TAnchor* anchor(NULL); + if(aOrdinal < iHuiAnchorLayoutPrivateData->iAnchors.Count()) + { + anchor = &(iHuiAnchorLayoutPrivateData->iAnchors[aOrdinal]); + } + return anchor; + } + +EXPORT_C TInt CHuiAnchorLayout::SetRelativeAnchorRect + (TInt aOrdinal, + THuiAnchorOrigin aTlHorizOrigin, + THuiAnchorOrigin aTlVertOrigin, + const THuiRealPoint& aTopLeftOffset, + THuiAnchorOrigin aBrHorizOrigin, + THuiAnchorOrigin aBrVertOrigin, + const THuiRealPoint& aBottomRightOffset) + { + // Create top left anchor first + TInt error = SetAnchor( + EHuiAnchorTopLeft, + aOrdinal, + aTlHorizOrigin, + aTlVertOrigin, + EHuiAnchorMetricRelativeToSize, + EHuiAnchorMetricRelativeToSize, + THuiTimedPoint(aTopLeftOffset.iX, aTopLeftOffset.iY)); + + // if TL is OK, create BR anchor + if ( error == KErrNone ) + { + error = SetAnchor( + EHuiAnchorBottomRight, + aOrdinal, + aBrHorizOrigin, + aBrVertOrigin, + EHuiAnchorMetricRelativeToSize, + EHuiAnchorMetricRelativeToSize, + THuiTimedPoint(aBottomRightOffset.iX, aBottomRightOffset.iY)); + + // If BR fails try to roll back by removing the TL anchor. + if ( error != KErrNone ) + { + RemoveAnchor( EHuiAnchorTopLeft, aOrdinal ); + } + } + return error; + } + +EXPORT_C TInt CHuiAnchorLayout::SetAnchor(THuiAnchor aAnchor, TInt aOrdinal, + THuiAnchorOrigin aHorizOrigin, + THuiAnchorOrigin aVertOrigin, + THuiAnchorMetric aHorizMetric, + THuiAnchorMetric aVertMetric, + const THuiTimedPoint& aOffset) + { + // convert deprecated API call into new format + THuiAnchorType newType = ConvertDeprecatedAnchorTypeToType(aAnchor); + THuiAnchorAttachmentOrigin newAttachmentOrigin = ConvertDeprecatedOriginsToAttachmentOrigin(aHorizOrigin, aVertOrigin); + THuiXYMetric newOffset = ConvertDeprecatedAnchorMetricsAndOffsetToHuiMetric(aHorizMetric, aVertMetric, aOffset); + return Attach(aOrdinal, newType, newOffset, newAttachmentOrigin, EHuiAnchorAttachToParent); + } + + +EXPORT_C void CHuiAnchorLayout::RemoveAnchor(THuiAnchor aAnchor, TInt aOrdinal) + { + // convert deprecated API call into new format + THuiAnchorType newType = ConvertDeprecatedAnchorTypeToType(aAnchor); + Detach(aOrdinal, newType); + } + +EXPORT_C TInt CHuiAnchorLayout::Attach( + TInt aOrdinal, + THuiAnchorType aType, + const THuiMetric& aOffset, + THuiAnchorAttachmentOrigin aAttachmentOrigin, + TInt aAttachmentOrdinal) + { + TInt error = KErrNone; + + // ensure that it's an edge + THuiAnchorType edgeH; + THuiAnchorType edgeV; + ConvertCornerToEdges(aType, edgeH, edgeV); + TBool bothAxes = (edgeH != EHuiAnchorTypeNone) && (edgeV != EHuiAnchorTypeNone); + if(bothAxes) + { + return KErrArgument; + } + + THuiAnchorAxis axisId(EHuiAnchorAxisHorizontal); + THuiAnchorProximity edgeProximity = ConvertTypeToProximity(aType, axisId); + THuiAnchorProximity attachmentProximity = ConvertAttachmentOriginToProximity(aAttachmentOrigin, axisId); + + // size currently not supported, center only supported for attachment proximity + TBool sizeProx = (edgeProximity == EHuiAnchorProximitySize) || (attachmentProximity == EHuiAnchorProximitySize); + TBool centerProx = (edgeProximity == EHuiAnchorProximityCenter); + if(sizeProx || centerProx) + { + return KErrArgument; + } + + // avoid issue of Anchor() returning pointer to const + TAnchor* anchor(NULL); + TInt count = iHuiAnchorLayoutPrivateData->iAnchors.Count(); + if(aOrdinal < count) + { + anchor = &(iHuiAnchorLayoutPrivateData->iAnchors[aOrdinal]); + } + else + { + // Create a new anchor. Fill any gaps. + for(TInt ii = count; ii <= aOrdinal; ii++) + { + TInt appendError = iHuiAnchorLayoutPrivateData->iAnchors.Append(TAnchor()); + if ( appendError != KErrNone ) + { + return appendError; + } + anchor = &(iHuiAnchorLayoutPrivateData->iAnchors[ii]); + Mem::FillZ(anchor, sizeof(*anchor)); + } + } + + TEdgeAnchor* edge(NULL); + TAxis& axis = anchor->Axis(axisId); + error = axis.AddEdge(edgeProximity, edge); + if(error == KErrNone) + { + edge->iEdgeProximity = edgeProximity; + edge->iOffset = aOffset; + edge->iAttachmentProximity = attachmentProximity; + edge->iAttachmentOrdinal = aAttachmentOrdinal; + + // ensure that the attachment doesn't attach an edge to itself, + // however we don't need to do anything additional here, as this will + // be checked automatically when we check for cycles. + + // ensure that adding the attachment will not cause a circular dependency... + if(CheckForCycles(axisId, edgeProximity, aOrdinal, edge)) + { + edge->iEdgeProximity = EHuiAnchorProximityNone; + error = KErrArgument; + } + } + + return error; + } + +EXPORT_C TInt CHuiAnchorLayout::Attach( + TInt aOrdinal, + THuiAnchorType aType, + const THuiXYMetric& aOffset, + THuiAnchorAttachmentOrigin aAttachmentOrigin, + TInt aAttachmentOrdinal) + { + // ensure that it's a corner + THuiAnchorType typeH; + THuiAnchorType typeV; + ConvertCornerToEdges(aType, typeH, typeV); + TBool bothAxes = (typeH != EHuiAnchorTypeNone) && (typeV != EHuiAnchorTypeNone); + if(!bothAxes) + { + return KErrArgument; + } + + // split the corner into two edges + THuiAnchorAttachmentOrigin attachmentOriginH; + THuiAnchorAttachmentOrigin attachmentOriginV; + ConvertCornerAttachmentOriginToEdgeAttachmentOrigins(aAttachmentOrigin, attachmentOriginH, attachmentOriginV); + + TInt error = Attach(aOrdinal, typeH, aOffset.iX, attachmentOriginH, aAttachmentOrdinal); + + // if H is OK, create V + if ( error == KErrNone ) + { + error = Attach(aOrdinal, typeV, aOffset.iY, attachmentOriginV, aAttachmentOrdinal); + // If V fails try to roll back by removing H + if ( error != KErrNone ) + { + Detach(aOrdinal, typeH); + } + } + + return error; + } + +EXPORT_C TInt CHuiAnchorLayout::Attach( + TInt aOrdinal, + const THuiBoxMetric& aOffset, + THuiAnchorAttachmentOrigin aAttachmentOrigin, + TInt aAttachmentOrdinal) + { + // first detach all other edges + Detach(aOrdinal); + + // if no attachment origin is specified, interpret this as meaning "attach each edge to its neighbour's corresponding edge" + THuiAnchorAttachmentOrigin leftAttachmentOrigin = (aAttachmentOrigin == EHuiAnchorAttachmentOriginNone) ? EHuiAnchorAttachmentOriginLeft : aAttachmentOrigin; + THuiAnchorAttachmentOrigin rightAttachmentOrigin = (aAttachmentOrigin == EHuiAnchorAttachmentOriginNone) ? EHuiAnchorAttachmentOriginRight : aAttachmentOrigin; + THuiAnchorAttachmentOrigin topAttachmentOrigin = (aAttachmentOrigin == EHuiAnchorAttachmentOriginNone) ? EHuiAnchorAttachmentOriginTop : aAttachmentOrigin; + THuiAnchorAttachmentOrigin bottomAttachmentOrigin = (aAttachmentOrigin == EHuiAnchorAttachmentOriginNone) ? EHuiAnchorAttachmentOriginBottom : aAttachmentOrigin; + + // just attach all four edges separately + TInt error = Attach(aOrdinal, EHuiAnchorTypeLeft, aOffset.iLeft, leftAttachmentOrigin, aAttachmentOrdinal); + if ( error == KErrNone ) + { + error = Attach(aOrdinal, EHuiAnchorTypeRight, aOffset.iRight, rightAttachmentOrigin, aAttachmentOrdinal); + if ( error == KErrNone ) + { + error = Attach(aOrdinal, EHuiAnchorTypeTop, aOffset.iTop, topAttachmentOrigin, aAttachmentOrdinal); + if ( error == KErrNone ) + { + error = Attach(aOrdinal, EHuiAnchorTypeBottom, aOffset.iBottom, bottomAttachmentOrigin, aAttachmentOrdinal); + // if any of them fail, roll back the ones we've already added, unwinding as we go + if ( error != KErrNone ) + { + DetachEdge(aOrdinal, EHuiAnchorTypeBottom); + } + } + if ( error != KErrNone ) + { + DetachEdge(aOrdinal, EHuiAnchorTypeTop); + } + } + if ( error != KErrNone ) + { + DetachEdge(aOrdinal, EHuiAnchorTypeRight); + } + } + if ( error != KErrNone ) + { + DetachEdge(aOrdinal, EHuiAnchorTypeLeft); + } + return error; + } + +EXPORT_C void CHuiAnchorLayout::Detach(TInt aOrdinal) + { + if(aOrdinal < iHuiAnchorLayoutPrivateData->iAnchors.Count()) + { + TAnchor& anchor = iHuiAnchorLayoutPrivateData->iAnchors[aOrdinal]; + for(TInt axisId = EHuiAnchorAxisHorizontal; axisId <= EHuiAnchorAxisVertical; axisId++) + { + TAxis& axis = anchor.Axis(THuiAnchorAxis(axisId)); + TInt count = axis.iEdges.Count(); + for(TInt ii = 0; ii < count; ii++) + { + TEdgeAnchor& edge = axis.iEdges[ii]; + edge.iEdgeProximity = EHuiAnchorProximityNone; + edge.iOffset = THuiMetric(); + edge.iAttachmentProximity = EHuiAnchorProximityNone; + edge.iAttachmentOrdinal = EHuiAnchorAttachToParent; + } + } + } + } + +EXPORT_C void CHuiAnchorLayout::Detach(TInt aOrdinal, THuiAnchorType aType) + { + // split into edges + THuiAnchorType typeH; + THuiAnchorType typeV; + ConvertCornerToEdges(aType, typeH, typeV); + if(typeH != EHuiAnchorTypeNone) + { + DetachEdge(aOrdinal, typeH); + } + if(typeV != EHuiAnchorTypeNone) + { + DetachEdge(aOrdinal, typeV); + } + } + +void CHuiAnchorLayout::DetachEdge(TInt aOrdinal, THuiAnchorType aType) + { + // Try to find an existing edge anchor entry for this + TEdgeAnchor* edge(NULL); + THuiAnchorAxis axisId(EHuiAnchorAxisHorizontal); + THuiAnchorProximity edgeProximity = ConvertTypeToProximity(aType, axisId); + + // avoid issue of Anchor() returning pointer to const + TAnchor* anchor(NULL); + if(aOrdinal < iHuiAnchorLayoutPrivateData->iAnchors.Count()) + { + anchor = &(iHuiAnchorLayoutPrivateData->iAnchors[aOrdinal]); + TAxis& axis = anchor->Axis(axisId); + TInt error = axis.Edge(edgeProximity, edge); + if(error == KErrNone) + { + edge->iEdgeProximity = EHuiAnchorProximityNone; + edge->iOffset = THuiMetric(); + edge->iAttachmentProximity = EHuiAnchorProximityNone; + edge->iAttachmentOrdinal = EHuiAnchorAttachToParent; + } + } + } + +/* + * The model of anchors attached to other anchors should represent a directed + * acyclic graph (DAG), so check that insertion of a new attachment (/edge) does not + * cause a cycle. + * + * @note This method assumes that all previous insertion attempts have maintained + * the DAG property. + * @note This method is implemented using a depth first recursive tree search. + * @note Because there can be only one attachment (which is along a specfic axis) starting + * at each edge, it is not necessary to keep a visited list. + */ +TBool CHuiAnchorLayout::CheckForCycles( + THuiAnchorAxis aStartAxisId, + THuiAnchorProximity aStartAnchorProximity, + TInt aStartOrdinal, + const TEdgeAnchor* aEdge) const + { + TInt nextOrdinal = aEdge->iAttachmentOrdinal; + if(nextOrdinal == EHuiAnchorAttachToParent) + { + // we've found the end of a path, as this corner attaches to the parent. + return EFalse; + } + + const TAnchor* nextAnchor = Anchor(nextOrdinal); + if(!nextAnchor) + { + // anchor not specified, so we've found the end of a path + return EFalse; + } + + // @todo note that with size attachments, the axis might change here + TAxis& nextAxis = nextAnchor->Axis(aStartAxisId); + TEdgeAnchor* nextEdge = NULL; + TInt error = nextAxis.Edge(aEdge->iAttachmentProximity, nextEdge); + + // @todo, when implementing size attachments, need to evaluate both edges at this point + // in order to determine the dependencies for this edge + if(error != KErrNone) + { + return EFalse; + } + + // so we now know the next proximity, and the next anchor. + if(aStartAnchorProximity == nextEdge->iEdgeProximity && aStartOrdinal == nextOrdinal) + { + // we have found a cycle! + // note that this will also detect an attempt to attach to the same edge on same ordinal + return ETrue; + } + else + { + return CheckForCycles(aStartAxisId, aStartAnchorProximity, aStartOrdinal, nextEdge); + } + } + +// end of file