--- /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<TEdgeAnchor, 2> 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<TEdgeAnchor*>(&(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<TAxis, 2> iAxes;
+ };
+
+CHuiAnchorLayout::TAxis& CHuiAnchorLayout::TAnchor::Axis(THuiAnchorAxis aAxis) const
+ {
+ // index is an enumeration and cannot go outside array boundaries.
+ return const_cast<CHuiAnchorLayout::TAxis&>(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<TAnchor> 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<TReal32, 2> tl;
+ TFixedArray<TReal32, 2> 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