com.adobe.xfa.layout.BoxModelLayout Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual and
* technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or copyright
* law. Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained from
* Adobe Systems Incorporated.
*/
package com.adobe.xfa.layout;
import com.adobe.xfa.Element;
import com.adobe.xfa.EnumAttr;
import com.adobe.xfa.Measurement;
import com.adobe.xfa.Node;
import com.adobe.xfa.XFA;
import com.adobe.xfa.content.Content;
import com.adobe.xfa.template.TemplateResolver;
import com.adobe.xfa.template.containers.AreaContainer;
import com.adobe.xfa.template.containers.Container;
import com.adobe.xfa.template.containers.ContentArea;
import com.adobe.xfa.template.containers.Draw;
import com.adobe.xfa.template.containers.ExclGroup;
import com.adobe.xfa.template.containers.Field;
import com.adobe.xfa.template.containers.PageArea;
import com.adobe.xfa.template.containers.Rotate;
import com.adobe.xfa.template.containers.Subform;
import com.adobe.xfa.template.formatting.Margin;
import com.adobe.xfa.template.ui.UI;
import com.adobe.xfa.ut.Angle;
import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.Margins;
import com.adobe.xfa.ut.ObjectHolder;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.UnitSpan;
import java.util.List;
/**
* A class to access and manipulate
* the axis-aligned rectangular layout of form objects.
* Form objects that subscribe to this box model paradigm are
* <draw>, <field>, <subform>, <contentArea>,
* <area> and <pageArea>.
*
* A box model defines a number of rectangular regions.
* All regions are relative to an offset from the local coordinate
* space, which has its origin at the top left corner of the
* nominal extent; this is the local box model origin.
*
* All regions have a set of margins. A region may also have
* an optional border that may be inset from its extents.
*
* A box model is characterized by the following:
*
* - a nominal extent region corresponding to the bounding rectangle of
* the form object.
*
- an (optional) interior caption region for the presentation of the
* caption. Caption regions do not have borders.
*
- an interior content region for the presentation of content
* objects. This is the remainder of the nominal extent not occupied
* by either margins or caption.
*
- a rotation angle -- in multiples of 90°.
*
*
* The nominal extent is used for graphical placement. However, the
* actual rendering of the form object may include graphic elements
* that draw outside of the nominal extent; this is the visual
* extent.
*
* Box model instances should be created using the static
* {@link #newBoxModel(Element, LayoutEnv, boolean) BoxModelLayout.newBoxModel(...)}
* function.
*/
public class BoxModelLayout /* extends Obj */ {
/**
* Nominal extent data.
* @exclude from published api.
*/
protected Rect m_oNE = Rect.ZERO;
/**
* @exclude from published api.
*/
protected boolean m_bGrowableH;
/**
* @exclude from published api.
*/
protected boolean m_bGrowableW;
/**
* @exclude from published api.
*/
protected boolean m_bInfiniteMaxH;
/**
* @exclude from published api.
*/
protected boolean m_bInfiniteMaxW;
/**
* @exclude from published api.
*/
protected UnitSpan m_oMinW = UnitSpan.ZERO;
/**
* @exclude from published api.
*/
protected UnitSpan m_oMaxW = UnitSpan.ZERO;
/**
* @exclude from published api.
*/
protected UnitSpan m_oMinH = UnitSpan.ZERO;
/**
* @exclude from published api.
*/
protected UnitSpan m_oMaxH = UnitSpan.ZERO;
/**
* @exclude from published api.
*/
protected Margins m_oNEMargins = new Margins();
/**
* @exclude from published api.
*/
protected Margins m_oCapMargins = new Margins();
/**
* @exclude from published api.
*/
protected Margins m_oContMargins = new Margins();
/**
* @exclude from published api.
*/
protected Margins m_oBorderMargins = new Margins();
/**
* @exclude from published api.
*/
protected Margins m_oContentBorderMargins = new Margins();
/**
* @exclude from published api.
*/
protected UnitSpan m_oMaxEdgeThickness = UnitSpan.ZERO;
/**
* The anchor point indicates a known reference point
* within the box model space and is not necessarily the top-left
* corner of the box-model nominal extent.
* The top-left value is returned by calling .getXY()
* @exclude from published api.
*/
protected CoordPair m_oAnchorPoint = CoordPair.ZERO_ZERO;
/**
* @exclude from published api.
*/
protected int m_oAnchorType;
/**
* The start point for text, adjusted by any rotation.
* @exclude from published api.
*/
protected CoordPair m_oRotatedContentTopLeft = CoordPair.ZERO_ZERO;
/**
* @exclude from published api.
*/
protected CoordPair m_oRotatedCaptionTopLeft = CoordPair.ZERO_ZERO;
/**
* @exclude from published api.
*/
protected boolean m_bWasRotated;
/**
* @exclude from published api.
*/
protected Angle m_oRotationAngle = Angle.ZERO;
// Enummerations of eBMType
/**
* For a node that can use the base box model implementation.
*
* @exclude from published api.
*/
public static final int eBaseLayout = 0;
/**
* For a node that is a button; it requires a
* box model implementation that makes the caption
* extent cover the entire margined nominal extent.
*
* @exclude from published api.
*/
public static final int eButtonLayout = 1;
/**
* For a node that is a check button; it requires a box model
* implementation that makes the caption extent cover the
* entire margined nominal extent except what's covered by
* the checkbox/radiio button.
*
* @exclude from published api.
*/
public static final int eCheckButtonLayout = 2;
/**
* For a node that has flowing textual contents; it requires
* a box model implementation that can format them.
*
* @exclude from published api.
*/
public static final int eTextContentLayout = 3;
/**
* For a node that is a barcode; it requires a box model
* implementation that formats the barcode data and
* any text label.
*
* @exclude from published api.
*/
public static final int eBarcodeLayout = 4;
/**
* For a node that is a paperMetaData barcode, including
* PDF417, DataMatrix, QRCode, etc.
* The boxmodel gives us a BMP image stored in ImageData.
*
* @exclude from published api.
*/
public static final int ePMDBarcodeLayout = 5;
/**
* For a node that is an image; it requries a box model
* implementation that separates the nominal extent
* into a content region for the image data and
* a caption region for the optional caption text data.
*
* @exclude from published api.
*/
public static final int eImageLayout = 6;
/**
* For a node that is a pageArea; it requires a box model
* implementation that resolves the correct pagesizes
*
* @exclude from published api.
*/
public static final int ePageLayout = 7;
/**
* For a node that is a exclusion group group;
* it requires a specialized box model implementation.
*
* @exclude from published api.
*/
public static final int eExclGroupLayout = 8;
/**
* Gets the box model type required for the node.
*
* @param oNode the form node that the box model applies to.
* @param oEnv the layout environment.
* @return a new box model layout type.
*
* @exclude from published api.
*/
public static int getBoxModelType(Element oNode, LayoutEnv oEnv) {
int eType = eBaseLayout;
if (oNode != null) {
if (oNode instanceof Field) {
UI oUI = (UI) oNode.peekElement(XFA.UITAG, true, 0);
Element oCurrentUI = oUI.getUIElement(false);
int oCurrentUITag = oCurrentUI.getClassTag();
if (oCurrentUITag == XFA.CHECKBUTTONTAG)
eType = eCheckButtonLayout;
else if (oCurrentUITag == XFA.BUTTONTAG)
eType = eButtonLayout;
else if (oCurrentUITag == XFA.BARCODETAG) {
// Javaport: TODO
// String sType = oCurrentUI.getAttribute(XFA.TYPETAG).toString();
// // isNotSupported returns true if the barcode is not supported by the hardware
// // nor the software but still needs to be rendered.
// // The name of this method is not very clear...
// boolean bBarcodeLayout = oEnv.getBarcodeResolver().isSoftwareSupported(sType) ||
// oEnv.getBarcodeResolver().isHardwareSupported(sType) ||
// oEnv.getBarcodeResolver().isNotSupported(sType);
// //
// // PunchCard controlled barcode
// //
// if (oEnv.getBarcodeResolver().isSpecial(sType)) {
// eType = ePMDBarcodeLayout;
// }
// else if (bBarcodeLayout) {
// eType = eBarcodeLayout;
// }
}
else if (oCurrentUITag == XFA.IMAGEEDITTAG)
eType = eImageLayout;
else {
eType = eTextContentLayout;
Element oValue = oNode.peekElement(XFA.VALUETAG, false, 0);
if (oValue != null) {
//Ignore images
Node oContent = oValue.getOneOfChild(true, true);
int oContentTag = oContent.getClassTag();
if (oContentTag == XFA.IMAGETAG) {
eType = eImageLayout;
}
}
}
}
else if (oNode instanceof Draw) {
Element oValue = oNode.peekElement(XFA.VALUETAG, true, 0);
Node oContent = oValue.getOneOfChild(true, true);
int oContentTag = oContent.getClassTag();
if (oContentTag == XFA.TEXTTAG
|| oContentTag == XFA.DECIMALTAG
|| oContentTag == XFA.INTEGERTAG
|| oContentTag == XFA.FLOATTAG
|| oContentTag == XFA.DATETAG
|| oContentTag == XFA.DATETIMETAG
|| oContentTag == XFA.TIMETAG
|| oContentTag == XFA.EXDATATAG)
eType = eTextContentLayout;
//
// Do we have an image?
//
else if (oContentTag == XFA.IMAGETAG)
eType = eImageLayout;
}
else if (oNode instanceof Subform) {
//subforms are noncaptionable, thus they use vanilla boxmodel
}
else if (oNode instanceof ExclGroup) {
//excl groups are captionable, so they get their own box model
eType = eExclGroupLayout;
}
else if (oNode instanceof PageArea) {
eType = ePageLayout;
}
}
return eType;
}
/**
* Creates a new, fully initialized instance of a box model.
*
* @param node the form node that the box model applies to.
* @param env the layout environment.
* @param bAllowProxy allow proxy -- ie. drawn from stored text run definitions. ????.
* @return a new box model layout.
*/
public static BoxModelLayout newBoxModel(Element node, LayoutEnv env, boolean bAllowProxy) {
//
//Create a new, fully initialized box model instance with the an implementation
//that's customized for the node type.
//
BoxModelLayout oBM = null;
try {
int eType = getBoxModelType(node, env);
if (eType == eTextContentLayout) {
//
// Cached text runs?
// If we're derived from a and have proxy info, then
// create a simpler box model object.
//
if (bAllowProxy && node instanceof Draw) {
oBM = new BoxModelRenderProxy(env);
} else {
oBM = new BoxModelContent(env, bAllowProxy);
}
oBM.initialize(node);
}
// Javaport: none of these are needed yet.
// else if (eType == eCheckButtonLayout) {
// oBM = new BoxModelChkBttn(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == eButtonLayout) {
// oBM = new BoxModelButton(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == eBarcodeLayout) {
// oBM = new BoxModelBarcode(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == ePMDBarcodeLayout) {
// oBM = new BoxModelPMDBarcode(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == eImageLayout) {
// oBM = new BoxModelImage(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == ePageLayout) {
// oBM = new BoxModelPage(oEnv);
// oBM.initialize(oNode);
// }
// else if (eType == eExclGroupLayout) {
// oBM = new BoxModelExclGroup(oEnv);
// oBM.initialize(oNode);
// }
// else {
// // Cached text runs?
// if (bAllowProxy) {
// oBM = new BoxModelRenderProxy(oEnv);
// }
// else {
// //Generic box model
// oBM = new BoxModelLayout();
// }
// oBM.initialize(oNode);
// }
} catch (ExFull e) {
// Javaport: For now, return null for UNSUPPORTED_OPERATION ExFulls.
if (e.firstResId() != ResId.UNSUPPORTED_OPERATION)
throw e;
oBM = null;
}
return oBM;
}
BoxModelLayout() {
super();
m_oAnchorType = EnumAttr.TOP_LEFT;
m_bWasRotated = false;
m_bGrowableH = false;
m_bGrowableW = false;
m_bInfiniteMaxH = false;
m_bInfiniteMaxW = false;
m_oRotationAngle = Angle.ZERO;
}
/**
* Clears the box model layout.
*
* @exclude from published api.
*/
public void clear() {
//Reset the box model layout
m_oAnchorType = EnumAttr.TOP_LEFT;
m_oAnchorPoint = CoordPair.ZERO_ZERO;
m_oBorderMargins = new Margins();
m_oContentBorderMargins = new Margins();
m_oNEMargins = new Margins();
m_oContMargins = new Margins();
m_oCapMargins = new Margins();
m_oNE = Rect.ZERO;
m_oRotatedContentTopLeft = CoordPair.ZERO_ZERO;
m_oMaxEdgeThickness = UnitSpan.ZERO;
m_oRotationAngle = Angle.ZERO;
m_bGrowableH = false;
m_bGrowableW = false;
m_bInfiniteMaxH = false;
m_bInfiniteMaxW = false;
m_oMinW = m_oMaxW = m_oMinH = m_oMaxH = UnitSpan.ZERO;
}
/**
* Gets the nominal bounding rectangle
* relative to the local box model origin.
* @return The nominal extent rectangle.
*/
public Rect getNominalExtent() {
return m_oNE;
}
/**
* Gets the width of the box model.
* @return The width of the box model.
*/
public UnitSpan getWidth() {
return m_oNE.width();
}
/**
* Gets the height of the box model.
* @return The height of the box model.
*/
public UnitSpan getHeight() {
return m_oNE.height();
}
/**
* Gets the visual bounding rectangle
* relative to the local box model origin.
* The visual extent is the nominal extent plus any border thickness
* that extends outside.
* @return The visual extent rectangle.
*/
public Rect getVisualExtent() {
//Strategy
//Add maximum border thicknesses to the nominal extent to yield the visual extent.
//This implementation offers only a quicker (less accurate) visual extent by adjusting
//each side of the nominal extent by max edge thickness. See initVisualExtent(..) for
//more details.
Rect oNE = getNominalExtent();
return new Rect(oNE.left().subtract(m_oMaxEdgeThickness),
oNE.top().subtract(m_oMaxEdgeThickness),
oNE.right().add(m_oMaxEdgeThickness),
oNE.bottom().add(m_oMaxEdgeThickness));
}
/**
* Gets the nominal extent border rectangle
* relative to the local box model origin.
* @return The border extent rectangle.
*/
public Rect getBorderExtent() {
//Border = NE - border_margins
Rect oNE = getNominalExtent();
return new Rect(oNE.left().add(m_oBorderMargins.marginLeft()),
oNE.top().add(m_oBorderMargins.marginTop()),
oNE.right().subtract(m_oBorderMargins.marginRight()),
oNE.bottom().subtract(m_oBorderMargins.marginBottom()));
}
/**
* Gets the content border rectangle
* relative to the local box model origin.
* @return The content border extent rectangle.
*/
public Rect getContentBorderExtent() {
// default is empty
return Rect.ZERO;
}
/**
* Gets the caption bounding rectangle
* relative to the local box model origin.
* @return The caption extent rectangle, which may be empty.
*/
public Rect getCaptionExtent() {
//default is empty
return Rect.ZERO;
}
/**
* Gets the content rectangle.
* @return The content extent rectangle.
*/
public Rect getContentExtent() {
//Default content = NE - NEMargins - ContentMargins
Rect oNE = getNominalExtent();
return new Rect(oNE.left().add(m_oNEMargins.marginLeft()).add(m_oContMargins.marginLeft()),
oNE.top().add(m_oNEMargins.marginTop()).add(m_oContMargins.marginTop()),
oNE.right().subtract(m_oNEMargins.marginRight()).subtract(m_oContMargins.marginRight()),
oNE.bottom().subtract(m_oNEMargins.marginBottom()).subtract(m_oContMargins.marginBottom()));
}
/**
* Gets the margins (top/left/bottom/right insets) of the caption extent.
* @return The caption extent margins.
*/
public Margins getCaptionExtentMargins() {
return m_oCapMargins;
}
/**
* Gets the margins (top/left/bottom/right insets) of the content extent.
* @return The content extent margins.
*/
public Margins getContentExtentMargins() {
return m_oContMargins;
}
/**
* Gets the rotation angle.
* @return The rotation angle.
*/
public Angle getAngle() {
return m_oRotationAngle;
}
/**
* Gets the margins (top/left/bottom/right insets) of the nominal extent.
* @return The nominal extent margins.
*/
public Margins getNominalExtentMargins() {
return m_oNEMargins;
}
/**
* Resets the top margin inset to zero.
*
* @exclude from published api.
*/
public void disableTopMargin() {
m_oNEMargins = new Margins(m_oNEMargins.marginLeft(),
UnitSpan.ZERO,
m_oNEMargins.marginRight(),
m_oNEMargins.marginBottom());
}
/**
* Resets the bottom margin inset to zero.
*
* @exclude from published api.
*/
public void disableBottomMargin() {
m_oNEMargins = new Margins(m_oNEMargins.marginLeft(),
m_oNEMargins.marginTop(),
m_oNEMargins.marginRight(),
UnitSpan.ZERO);
}
/**
* Resets the content top margin to zero.
*
* @exclude from published api.
*/
public void disableContentTopMargin() {
m_oContMargins = new Margins(m_oContMargins.marginLeft(),
UnitSpan.ZERO,
m_oContMargins.marginRight(),
m_oContMargins.marginBottom());
}
/**
* Resets the content bottom margin to zero.
*
* @exclude from published api.
*/
public void disableContentBottomMargin() {
m_oContMargins = new Margins(m_oContMargins.marginLeft(),
m_oContMargins.marginTop(),
m_oContMargins.marginRight(),
UnitSpan.ZERO);
}
/**
* Resets the caption top margin to zero.
*
* @exclude from published api.
*/
public void disableCaptionTopMargin() {
m_oCapMargins = new Margins(m_oCapMargins.marginLeft(),
UnitSpan.ZERO,
m_oCapMargins.marginRight(),
m_oCapMargins.marginBottom());
}
/**
* Reset the caption bottom margin to zero.
*
* @exclude from published api.
*/
public void disableCaptionBottomMargin() {
m_oCapMargins = new Margins(m_oCapMargins.marginLeft(),
m_oCapMargins.marginTop(),
m_oCapMargins.marginRight(),
UnitSpan.ZERO);
}
/**
* Enumerates the box model's caption using given layout handler.
* @param pHandler enumeration handler.
* @param oOffset offset to apply to all enumerated caption data.
*
* @exclude from published api.
*/
public boolean enumerateCaption(LayoutHandler pHandler,
CoordPair oOffset,
boolean bTruncate /* = false */,
Rect oInvalidatedRect /* = Rect.ZERO */) {
return true;
}
/**
* Enumerates the box model's contents through the given
* layout handler.
* @param pHandler enumeration handler.
* @param oOffset offset to apply to all enumerated content data.
* @param bWrapText indicates whether content data should wrap to.
* extents
*
* @exclude from published api.
*/
public boolean enumerateContent(LayoutHandler pHandler,
CoordPair oOffset,
boolean bWrapText /* = true */,
boolean bTruncate /* = false */,
Rect oInvalidatedRect /* = Rect.ZERO */) {
return true;
}
/**
* Gets a string containing the box model caption.
* @param sType indicates how to represent the data.
* within the returned string (rtf|html|plain)
* @param colorTable array of color values, can be null.
* @param fontTable array of fonts used, can be null.
* @return the string representation of box model caption,
* or the empty string if there isn't any.
*
* @exclude from published api.
*/
public String getCaptionByType(String sType,
List colorTable /* = null */,
List fontTable /* = null */) {
return "";
}
/**
* Gets a string containing the box model content.
* @param sType indicates how to represent the data.
* within the returned string (rtf|html|plain)
* @param colorTable array of color values, can be null.
* @param fontTable array of fonts used, can be null.
* @return the string representation of box model contents,
* or the empty string if there isn't any.
*
* @exclude from published api.
*/
public String getContentByType(String sType,
List colorTable /* = null */,
List fontTable /* = null */) {
return "";
}
/**
* Gets the top left of the content region adjusted for rotations.
* This is key since a rotated content region is adjusted so
* that it is oriented 'up' always, which is not the correct
* top left for text when a rotation is used
*
* @exclude from published api.
*/
public CoordPair getRotatedContentTopLeft() {
//This is the top left of content region adjusted for any rotation.
return m_oRotatedContentTopLeft;
}
/**
* Gets the top left of the caption region adjusted for rotations.
* This is key since a rotated caption region is adjusted so
* that it is oriented 'up' always, which is not the correct
* top left for text when a rotation is used
*
* @exclude from published api.
*/
public CoordPair getRotatedCaptionTopLeft() {
//This is the top left of caption region adjusted for any rotation.
return m_oRotatedCaptionTopLeft;
}
/**
* Determines if this box model has any formattable content
* within it's caption region.
* @return true if it has.
*/
public boolean hasCaption() {
return false;
}
/**
* Determines if this box model has any formattable content
* within it's content region.
* @return true if it has.
*
* @exclude from published api.
*/
public boolean hasContent() {
return false;
}
/**
* Determines if this box model has had a rotation applied.
* @return true if it has.
*/
public boolean hasRotation() {
return m_bWasRotated;
}
/**
* Determines if the caption extent has been fully contained
* within this box model.
* @return true if it has.
*
* @exclude from published api.
*/
public boolean hasCaptionExtentContained() {
Rect oNE = getNominalExtent();
Rect oCE = getCaptionExtent();
return oNE.contains(oCE);
}
/**
* Determines if the content extent has been fully contained
* within this box model.
* @return true if it has.
*
* @exclude from published api.
*/
public boolean hasContentExtentContained() {
Rect oNE = getNominalExtent();
Rect oCE = getContentExtent();
return oNE.contains(oCE);
}
/**
* Determines if this box model has embedded content.
* @return true if the box model has.
*
* @exclude from published api.
*/
public boolean hasEmbeddedContent() {
return false;
}
/**
* Determines if this box model has been involved in a split.
* @return true if the box model has.
*
* @exclude from published api.
*/
public boolean hasSplit() {
return false;
}
/**
* Initializes the box model internals.
* It is not necessary to call this since newBoxModel(...)
* does this automatically.
* @param oNode the form node that the box model applies to.
*
* @exclude from published api.
*/
public void initialize(Element oNode) {
//Strategy
//1)Set xy/anchor point
//2)Set nominal extent
//3)Set margins
//4)Set border
//5)Adjust to rotation (if any)
assert(oNode != null);
if (! isBoxModelCompatible(oNode))
return;
initWidthHeight(oNode, false);
initAnchorPoint(oNode);
initMargins(oNode);
initRotatedContentTopLeft();
initBorder(oNode);
respectRotation(oNode);
initVisualExtent(oNode);
}
/**
* Determines if this box model has a font that's been
* substituted. Used for text caching.
* @return true if the box model has fonts that were substituted.
*
* @exclude from published api.
*/
public boolean isFontSubstituted() {
return false;
}
/**
* Is this box model is based on a proxy definition -- ie. drawn from stored text run definitions.
* @return true if drawn from text runs.
*
* @exclude from published api.
*/
public boolean isProxy() {
return false;
}
/**
* Re-initializes the box model.
* @param oNode the form node that the box model applies to.
*
* @exclude from published api.
*/
public void reinitialize(Element oNode) {
clear();
assert(oNode != null);
initialize(oNode);
}
/**
* Resizes the box model by setting it's new nominal extent.
* Content/caption extents will be updated as well.
* @param oW new nominal extent width.
* @param oH new nominal extent height.
* @param oNode the form node that the box model applies to.
*
* @exclude from published api.
*/
public void resizeToNominal(UnitSpan oW, UnitSpan oH, Element oNode) {
//
// All we have to do is set nominal extent and then
// border/content/position will be updated automatically.
//
assert(UnitSpan.ZERO.lte(oW));
assert(UnitSpan.ZERO.lte(oH));
m_oNE = m_oNE.width(oW, false);
m_oNE = m_oNE.height(oH, false);
}
/**
* Resizes the box model by setting it's new nominal extent width.
* The height will automatically update if the box model is h-growable.
* Content/caption extents will be updated as well.
* @param oW new nominal extent width.
* @param oNode the form node that the box model applies to.
*
* @exclude from published api.
*/
public void resizeToNominalWidth(UnitSpan oW, Element oNode) {
//
// All we have to do is set nominal extent width and then
// border/content/position will be updated automatically.
//
assert(UnitSpan.ZERO.lte(oW));
m_oNE.width(oW, false);
}
/**
* Resizes the box model by setting it's new content extent.
* The nominal extent + caption extent will be reformulated based
* on the new content size.
* @param oContentW new width of content.
* @param oContentH new height of content.
* @param oNode the form node that the box model applies to.
*
* @exclude from published api.
*/
public void resizeToContent(UnitSpan oContentW, UnitSpan oContentH, Element oNode) {
//
// Resize this box-model around the given dimensions of
// it's new content extents and current margin values.
// Content = NE - margins
//
assert(UnitSpan.ZERO.lte(oContentW));
assert(UnitSpan.ZERO.lte(oContentW));
m_oNE = m_oNE.width(oContentW
.add(m_oNEMargins.marginLeft())
.add(m_oContMargins.marginLeft())
.add(m_oContMargins.marginRight())
.add(m_oNEMargins.marginRight()),
false);
m_oNE = m_oNE.height(oContentH
.add(m_oNEMargins.marginTop())
.add(m_oContMargins.marginTop())
.add(m_oContMargins.marginBottom())
.add(m_oNEMargins.marginBottom()),
false);
}
/**
* Split the contents of this box model by trimming
* anything would not fit within new height.
* @param oNewHeight the distance from top of nominal extent.
* at which to perform split.
* @param oNewBM upon return this refers to a box model.
* is the portion of the original box modle that did
* not fit.
* @param oNode the form node that the box model applies to.
* @return true if box model was successfully split.
*
* @exclude from published api.
*/
public boolean split(UnitSpan oNewHeight, ObjectHolder oNewBM, Element oNode) {
boolean bSplit = false;
BoxModelLayout pImpl = null;
bSplit = splitImpl(oNewHeight, pImpl, oNode);
if (bSplit)
oNewBM.value = pImpl;
else
oNewBM.value = new BoxModelLayout();
return bSplit;
}
/**
* @exclude from published api.
*/
protected boolean splitImpl(UnitSpan oNewHeight, BoxModelLayout oNewBM, Element oNode) {
return false;
}
// // These function guard against precision errors with our unitspans that affect
// // our <= and >= unitspan comparisons. These macros will compare give or take a 5 millionths of an inch epsilon.
// private static boolean UNITS_LTE(UnitSpan a, UnitSpan b) {
// if (a.gt(b))
// return Math.abs(a.valueAsUnit(UnitSpan.INCHES_1M) - b.valueAsUnit(UnitSpan.INCHES_1M)) <= 5;
// return true;
// }
private static boolean UNITS_GTE(UnitSpan a, UnitSpan b) {
if (a.lt(b))
return Math.abs(a.valueAsUnit(UnitSpan.INCHES_1M) - b.valueAsUnit(UnitSpan.INCHES_1M)) >= 5;
return true;
}
/**
* Determines if this box model has a growable width.
* @return true if it does.
*
* @exclude from published api.
*/
public boolean hasGrowableW() {
return m_bGrowableW;
}
/**
* Determines if this box model has a growable height.
* @return true if it does.
*
* @exclude from published api.
*/
public boolean hasGrowableH() {
return m_bGrowableH;
}
/**
* @exclude from published api.
*/
public int getAnchor() {
return m_oAnchorType;
}
/**
* @exclude from published api.
*/
public CoordPair getAnchorPoint() {
return m_oAnchorPoint;
}
/**
* @exclude from published api.
*/
public void setAnchor(int eVal) {
m_oAnchorType = eVal;
}
/**
* @exclude from published api.
*/
public void setAnchorPoint(CoordPair oPt) {
m_oAnchorPoint = oPt;
}
/**
* @exclude from published api.
*/
public void initAnchorPoint(Element oNode) {
// int eType = EnumAttr.TOP_LEFT;
// if (oNode.isPropertySpecified(XFA.ANCHORTYPETAG, true, 0))
// eType = oNode.getEnum(XFA.ANCHORTYPETAG);
m_oAnchorPoint = CoordPair.ZERO_ZERO;
// TOP_LEFT. Do nothing.
if (EnumAttr.TOP_CENTER == m_oAnchorType)
m_oAnchorPoint = new CoordPair(m_oNE.width().divide(2), m_oAnchorPoint.y());
else if (EnumAttr.TOP_RIGHT == m_oAnchorType)
m_oAnchorPoint = new CoordPair(m_oNE.width(), m_oAnchorPoint.x());
else if (EnumAttr.MIDDLE_LEFT == m_oAnchorType)
m_oAnchorPoint = new CoordPair(m_oAnchorPoint.x(), m_oNE.height().divide(2));
else if (EnumAttr.MIDDLE_CENTER == m_oAnchorType) {
m_oAnchorPoint = new CoordPair(m_oNE.width().divide(2), m_oNE.height().divide(2));
}
else if (EnumAttr.MIDDLE_RIGHT == m_oAnchorType) {
m_oAnchorPoint = new CoordPair(m_oNE.width(), m_oNE.height().divide(2));
}
else if (EnumAttr.BOTTOM_LEFT == m_oAnchorType)
m_oAnchorPoint = new CoordPair(m_oAnchorPoint.x(), m_oNE.height());
else if (EnumAttr.BOTTOM_CENTER == m_oAnchorType) {
m_oAnchorPoint = new CoordPair(m_oNE.width().divide(2), m_oNE.height());
}
else if (EnumAttr.BOTTOM_RIGHT == m_oAnchorType) {
m_oAnchorPoint = new CoordPair(m_oNE.width(), m_oNE.height());
}
}
/**
* @exclude from published api.
*/
public void initBorder(Element oNode) {
if (oNode.isPropertySpecified(XFA.BORDERTAG, true, 0)) {
Element oBorder = oNode.peekElement(XFA.BORDERTAG, true, 0);
Element oBorderMargins = oBorder.peekElement(XFA.MARGINTAG, true, 0);
ObjectHolder oLHolder = new ObjectHolder();
ObjectHolder oTHolder = new ObjectHolder();
ObjectHolder oRHolder = new ObjectHolder();
ObjectHolder oBHolder = new ObjectHolder();
((Margin)oBorderMargins).getInsets(oTHolder, oRHolder, oBHolder, oLHolder);
m_oBorderMargins = new Margins(oLHolder.value, oTHolder.value, oRHolder.value, oBHolder.value);
}
if (oNode instanceof Field) {
UI oUI = (UI) oNode.peekElement(XFA.UITAG, false, 0);
if (oUI != null) {
Element oCurrentUI = oUI.getUIElement(false);
if (oCurrentUI != null &&
(oCurrentUI.isSameClass(XFA.CHECKBUTTONTAG) ||
oCurrentUI.isSameClass(XFA.CHOICELISTTAG) ||
oCurrentUI.isSameClass(XFA.DATETIMEEDITTAG) ||
oCurrentUI.isSameClass(XFA.NUMERICEDITTAG) ||
oCurrentUI.isSameClass(XFA.PASSWORDEDITTAG) ||
oCurrentUI.isSameClass(XFA.SIGNATURETAG) ||
oCurrentUI.isSameClass(XFA.TEXTEDITTAG)) &&
oCurrentUI.isPropertySpecified(XFA.BORDERTAG, true, 0)) {
Element oBorder = oCurrentUI.peekElement(XFA.BORDERTAG, true, 0);
Element oBorderMargins = oBorder.peekElement(XFA.MARGINTAG, true, 0);
ObjectHolder oLHolder = new ObjectHolder();
ObjectHolder oTHolder = new ObjectHolder();
ObjectHolder oRHolder = new ObjectHolder();
ObjectHolder oBHolder = new ObjectHolder();
((Margin)oBorderMargins).getInsets(oTHolder, oRHolder, oBHolder, oLHolder);
m_oContentBorderMargins = new Margins(oLHolder.value, oTHolder.value, oRHolder.value, oBHolder.value);
}
}
}
}
/**
* @exclude from published api.
*/
public void initVisualExtent(Element oNode) {
//
//Strategy
//Perform the necessary calculations for the approximate visual extent of this box model.
//The visual extent of a box model will be calculated by taking the nominal extent and expanding
//it by the maximum border thickness. Yes, this is an approximation of the visual extent, but doing so
//has several benefits:
//i) automatically works for rotated box models
//ii) it's smaller, faster and less complex than a more exact implementation
//iii)at this time perfect accuracy is not warranted for getVisualExtent() - as long as the returned
//visual extent covers the entire object.
//
//Rather than have each boxmodel instance store another Rect for the visual extent, simply store
//the max border thickness. Whenever the visual extent is required it will be calculated using the current
//nominal extent. A single unitspan member var uses 1/4 the memory footprint of having a Rect member var.
//
assert(oNode != null);
m_oMaxEdgeThickness = UnitSpan.ZERO;
if (oNode != null) {
if (oNode.isPropertySpecified(XFA.BORDERTAG, true, 0)) {
Element oBorder = oNode.peekElement(XFA.BORDERTAG, false, 0);
//
//Right handedness means border is entirely within nominal extent
//
if (oBorder.getEnum(XFA.HANDTAG) != EnumAttr.HAND_RIGHT) {
for (int nNumEdges = 0; nNumEdges < 4; nNumEdges++) {
Element oEdge = oBorder.peekElement(XFA.EDGETAG, false, nNumEdges);
if (oEdge == null)
break;
Measurement oThickness = new Measurement(oEdge.getAttribute(XFA.THICKNESSTAG));
if (oThickness.getUnitSpan().gt(m_oMaxEdgeThickness))
m_oMaxEdgeThickness = oThickness.getUnitSpan();
}
}
}
//
//Some draws have special cicumstances. Arcs, rectangles and line edges
//can affect the visual extents with their edge thicknesses
//
if (oNode instanceof Draw) {
Element oValue = oNode.peekElement(XFA.VALUETAG, true, 0);
Content oContent = (Content) oValue.getOneOfChild(true, true);
int oContentTag = oContent.getClassTag();
if (oContentTag == XFA.RECTANGLETAG
|| oContentTag == XFA.ARCTAG
|| oContentTag == XFA.LINETAG) {
int nTotalPossibleEdges = 1;
if (oContentTag == XFA.RECTANGLETAG)
nTotalPossibleEdges = 4;
for (int nNumEdges = 0; nNumEdges < nTotalPossibleEdges; nNumEdges++) {
Element oEdge = ((Element) oContent).peekElement(XFA.EDGETAG, false, nNumEdges);
if (oEdge == null) {
//
//WATSON 1110608 - Line edges are special, er more special (specialer?).
//Even if there is no edge defined, a default edge still gets drawn, which affects the visual extent.
//This is different from most objects in that other objects require at least an element to be specified.
//
if (oContentTag == XFA.LINETAG) {
oEdge = ((Element) oContent).peekElement(XFA.EDGETAG, true, nNumEdges);//default
}
else
break;
}
Measurement oThickness = new Measurement(oEdge.getAttribute(XFA.THICKNESSTAG));
if (oThickness.getUnitSpan().gt(m_oMaxEdgeThickness))
m_oMaxEdgeThickness = oThickness.getUnitSpan();
}
}
}
}
}
/**
* @exclude from published api.
*/
protected void initWidthHeight(Element oNode, boolean bSupportsGrowability) {
//Retrieve the w/h of the nominal extents.
//Default implementation of initWidtHeight makes objects non-growable.
//Objects that can be growable should override this function.
//If not specified the default for objects is to use the minW/minH in its place.
//In other words, objects that do not support the notion of 'growing'
//will use the min values in the absence of a specified w/h.
//Boxmodel objects that want to support growable will do so
//in thier own boxmodel implementation.
//Objects like rects/arcs/lines/images etc that do not specify a w/h but specify
//a minW/H will use the min value, ie
//
//will result in an arc draw that has a nominal extent 3in high and 2in wide.
UnitSpan oW = UnitSpan.ZERO;
UnitSpan oH = UnitSpan.ZERO;
if (oNode.isPropertySpecified(XFA.WTAG, true, 0)) {
Measurement oMeasW = new Measurement(oNode.getAttribute(XFA.WTAG));
oW = oMeasW.getUnitSpan();
}
else if (oNode.isPropertySpecified(XFA.MINWTAG, true, 0)) {
Measurement oMeasW = new Measurement(oNode.getAttribute(XFA.MINWTAG));
oW = oMeasW.getUnitSpan();
}
if (oNode.isPropertySpecified(XFA.HTAG, true, 0)) {
Measurement oMeasH = new Measurement(oNode.getAttribute(XFA.HTAG));
oH = oMeasH.getUnitSpan();
}
else if (oNode.isPropertySpecified(XFA.MINHTAG, true, 0)) {
Measurement oMeasH = new Measurement(oNode.getAttribute(XFA.MINHTAG));
oH = oMeasH.getUnitSpan();
}
//
//Guard against bad values
//
if (0 > oW.value())
oW = UnitSpan.ZERO;
if (0 > oH.value())
oH = UnitSpan.ZERO;
//
//Set the nominal extents
//
m_oNE = m_oNE.changeUnits(UnitSpan.INCHES_72K);
m_oNE = m_oNE.width(oW, false); // lefWidth(moLeft, oW);
m_oNE = m_oNE.height(oH, false); // topHeight(moTop, oH);
m_bGrowableH = false;
m_bGrowableW = false;
m_bInfiniteMaxH = false;
m_bInfiniteMaxW = false;
if (bSupportsGrowability) {
assert(oNode instanceof Container);
if (oNode instanceof Container) {
Container oContainer = (Container) oNode;
m_bGrowableW = oContainer.isWidthGrowable();
m_bGrowableH = oContainer.isHeightGrowable();
}
}
if (! m_bGrowableW) {
//
//Non-growable ne width was determined in initWH()
//
m_oMinW = m_oMaxW = m_oNE.width();
}
else {
//
//For now just record the ranges
//Important to only query for min values if they are specified. Otherwise they'll
//return the h value by default.
//
m_oMinW = UnitSpan.ZERO;
if (oNode.isPropertySpecified(XFA.MINWTAG, true, 0)) {
Measurement oMeas = new Measurement(oNode.getAttribute(XFA.MINWTAG));
m_oMinW = oMeas.getUnitSpan();
}
Measurement oMeas = new Measurement(oNode.getAttribute(XFA.MAXWTAG));
m_oMaxW = oMeas.getUnitSpan();
m_bInfiniteMaxW = ! oNode.isPropertySpecified(XFA.MAXWTAG, true, 0);
//Kludge - if maxW is zero then treat this as infinite maxW.
//The reason is that maxW="" returns as specified, and the value is zero.
//The downside is that if someone says maxW="0in" then this too is treated
//as infinite growability. For now I'd rather support maxW="" properly, since
//maxW="0in" is not really a good idea on any day.
if (! m_bInfiniteMaxW && m_oMaxW.equals(UnitSpan.ZERO))
m_bInfiniteMaxW = true;
}
if (! m_bGrowableH) {
//Non-growable ne height was determined in initWH()
m_oMinH = m_oMaxH = m_oNE.height();
}
else {
//For now just record the ranges.
//Important to only query for min values if they are specified. Otherwise they'll
//return the h value by default.
m_oMinH = UnitSpan.ZERO;
if (oNode.isPropertySpecified(XFA.MINHTAG, true, 0)) {
Measurement oMeas = new Measurement(oNode.getAttribute(XFA.MINHTAG));
m_oMinH = oMeas.getUnitSpan();
}
Measurement oMeas = new Measurement(oNode.getAttribute(XFA.MAXHTAG));
m_oMaxH = oMeas.getUnitSpan();
m_bInfiniteMaxH = ! oNode.isPropertySpecified(XFA.MAXHTAG, true, 0);
//Kludge - if maxH is zero then treat this as infinite maxH.
//The reason is that maxH="" returns as specified, and the value is zero.
//The downside is that if someone says maxH="0in" then this too is treated
//as infinite growability. For now I'd rather support maxH="" properly, since
//maxH="0in" is not really a good idea on any day.
if (! m_bInfiniteMaxH && m_oMaxH.equals(UnitSpan.ZERO))
m_bInfiniteMaxH = true;
}
assert(UnitSpan.ZERO.lte(m_oMinW));
assert(UnitSpan.ZERO.lte(m_oMinH));
assert(UnitSpan.ZERO.lte(m_oMaxH));
assert(UnitSpan.ZERO.lte(m_oMaxW));
assert(m_bInfiniteMaxW || UNITS_GTE(m_oMaxW, m_oMinW));
assert(m_bInfiniteMaxH || UNITS_GTE(m_oMaxH, m_oMinH));
if (UnitSpan.ZERO.gt(m_oMinW))
m_oMinW = UnitSpan.ZERO;
if (UnitSpan.ZERO.gt(m_oMinH))
m_oMinH = UnitSpan.ZERO;
if (UnitSpan.ZERO.gt(m_oMaxH))
m_oMaxH = UnitSpan.ZERO;
if (UnitSpan.ZERO.gt(m_oMaxW))
m_oMaxW = UnitSpan.ZERO;
//
//Imperative to set units to inches_72k to limit prevision loss in text engine.
//
m_oMinH = new UnitSpan(UnitSpan.INCHES_72K, m_oMinH.value());
m_oMinW = new UnitSpan(UnitSpan.INCHES_72K, m_oMinW.value());
m_oMaxH = new UnitSpan(UnitSpan.INCHES_72K, m_oMaxH.value());
m_oMaxW = new UnitSpan(UnitSpan.INCHES_72K, m_oMaxW.value());
}
/**
* @exclude from published api.
*/
protected void initMargins(Element oNode) {
//
// There are 3 types of margins we are interested in.
//
// 1. Nominal Extent Margins
// 2. Content Margins
// 3. Caption Margins
//
// These margins are used to calculate the appropriate
// extents for our caption and content regions.
//
// Content margins are currently reserved for the
// following UI types:
//
// DateTimeEdit, ChoiceList, NumericEdit, PasswordEdit,
// TextEdit, CheckButton and Signature.
//
//
// Init Nominal Extent Margins
//
if (oNode.isPropertySpecified(XFA.MARGINTAG, true, 0)) {
Element oMargins = oNode.peekElement(XFA.MARGINTAG, true, 0);
ObjectHolder oLHolder = new ObjectHolder();
ObjectHolder oTHolder = new ObjectHolder();
ObjectHolder oRHolder = new ObjectHolder();
ObjectHolder oBHolder = new ObjectHolder();
((Margin)oMargins).getInsets(oTHolder, oRHolder, oBHolder, oLHolder);
UnitSpan oL = oLHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oR = oRHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oT = oTHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oB = oBHolder.value.changeUnits(UnitSpan.INCHES_72K);
m_oNEMargins = new Margins(oL, oT, oR, oB);
}
//
// If this is a field or a draw, then we will initialize the content and caption margins appropriately.
//
if (oNode instanceof Field || oNode instanceof Draw) {
//
//Watson 1430544 + 1487018, 1487021, 1487025, 1487027, 1487891.
//We cannot use peekElement calls to drill down to query widget/caption margins, since we the default auto-calculates in a context
//sensitive manner (based on parental border thickness + font size).
//So the returned margin must be a resident of the same model as it's parental ui type, which must be in the same model as the etc
//Calling getProperty() ensures that default nodes get copied over to the form model, if that's where the field resides.
//To avoid receiving damage notifications we must mute the nodes before and unmute them again afterwards. Otherwise the act of building
//a layout could cause be broadcasting fake damage.
//
Element oNonConstMutableNode = oNode;
oNonConstMutableNode.mute();
//
// Drill down to the content margins
//
Element oUI = (Element) oNode.getProperty(XFA.UITAG, 0);
oUI.mute(); //to be safe, mute the ui so the no peers hear this
Element oCurrentUI = (Element) oUI.getOneOfChild();
oUI.unMute();
int oCurrentUITag = oCurrentUI.getClassTag();
if (oCurrentUITag == XFA.TEXTEDITTAG
|| oCurrentUITag == XFA.NUMERICEDITTAG
|| oCurrentUITag == XFA.CHECKBUTTONTAG
|| oCurrentUITag == XFA.DATETIMEEDITTAG
|| oCurrentUITag == XFA.SIGNATURETAG
|| oCurrentUITag == XFA.PASSWORDEDITTAG
|| oCurrentUITag == XFA.IMAGEEDITTAG
|| oCurrentUITag == XFA.CHOICELISTTAG) {
oCurrentUI.mute();//to be safe, mute the ui so the no peers hear this
Element oMargins = (Element) oCurrentUI.getProperty(XFA.MARGINTAG, 0);
oCurrentUI.unMute();
ObjectHolder oLHolder = new ObjectHolder();
ObjectHolder oTHolder = new ObjectHolder();
ObjectHolder oRHolder = new ObjectHolder();
ObjectHolder oBHolder = new ObjectHolder();
((Margin)oMargins).getInsets(oTHolder, oRHolder, oBHolder, oLHolder);
UnitSpan oL = oLHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oR = oRHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oT = oTHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oB = oBHolder.value.changeUnits(UnitSpan.INCHES_72K);
m_oContMargins = new Margins(oL, oT, oR, oB);
}
//
//Drill down to the caption margins using getProperty() (if there is a caption)
//
if (oNode.isPropertySpecified(XFA.CAPTIONTAG, true, 0)) {
Element oCaption = (Element) oNode.getProperty(XFA.CAPTIONTAG, 0);
oCaption.mute(); //to be safe, mute the ui so the layout is undamaged by this act
Element oMargins = (Element) oCaption.getProperty(XFA.MARGINTAG, 0);
oCaption.unMute();
ObjectHolder oLHolder = new ObjectHolder();
ObjectHolder oTHolder = new ObjectHolder();
ObjectHolder oRHolder = new ObjectHolder();
ObjectHolder oBHolder = new ObjectHolder();
((Margin)oMargins).getInsets(oTHolder, oRHolder, oBHolder, oLHolder);
UnitSpan oL = oLHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oR = oRHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oT = oTHolder.value.changeUnits(UnitSpan.INCHES_72K);
UnitSpan oB = oBHolder.value.changeUnits(UnitSpan.INCHES_72K);
m_oCapMargins = new Margins(oL, oT, oR, oB);
}
//Finished drilling down to the content/caption margins. Allow the field/draw to send peer notifications again
oNonConstMutableNode.unMute();
}
}
/**
* @exclude from published api.
*/
protected void initRotatedContentTopLeft() {
m_oRotatedContentTopLeft = getContentExtent().topLeft();
}
/**
* @exclude from published api.
*/
protected boolean isBoxModelCompatible(Element oNode) {
//
//Return true if oNode represents a form node that
//subscribes to the box model layout paradigm {draw, field,
//subform, area, contentArea, pageArea, exclGroup } and
//false otherwise.
//
boolean bCompatible = false;
if (oNode != null) {
bCompatible = oNode instanceof Draw
|| oNode instanceof Field
|| oNode instanceof Subform
|| oNode instanceof AreaContainer
|| oNode instanceof ContentArea
|| oNode instanceof PageArea
|| oNode instanceof ExclGroup;
}
return bCompatible;
}
/**
* @exclude from published api.
*/
protected void respectRotation(Element oNode) {
//Adjust the box model if it is rotated.
//Only support rotation on fields+draws,
m_bWasRotated = false;
m_oRotationAngle = Angle.ZERO;
if (oNode instanceof Draw || oNode instanceof Field) {
// Set the default value for the rotated top left,
// since this value gets referenced whether the object
// is rotated or not.
m_oRotatedContentTopLeft = getContentExtent().topLeft();
if (oNode.isPropertySpecified(XFA.ROTATETAG, true, 0)) {
Rotate oRotate = new Rotate(XFA.ROTATE, oNode.getAttribute(XFA.ROTATETAG).toString());
Angle oAngle = oRotate.getAngle();
int lAngle = oAngle.degrees();
lAngle = lAngle % 360;
if (0 > lAngle)
lAngle += 360;
oAngle = new Angle(lAngle);
m_oRotationAngle = oAngle;
Margins oPrevMargins = m_oNEMargins;
Margins oPrevBorderMargins = m_oBorderMargins;
Margins oPrevWidgetBorderMargins = m_oContentBorderMargins;
Margins oPrevWidgetContentMargins = m_oContMargins;
Margins oPrevWidgetCaptionMargins = m_oCapMargins;
CoordPair oPrevContentExtentTL = getContentExtent().topLeft();
Rect rcPrevNE = getNominalExtent();
CoordPair oNewTL = CoordPair.ZERO_ZERO;
CoordPair oNewTR = CoordPair.ZERO_ZERO;
//CoordPair oNewBL = new CoordPair();
CoordPair oNewBR = CoordPair.ZERO_ZERO;
CoordPair oPrevTL = rcPrevNE.topLeft();
CoordPair oPrevTR = rcPrevNE.topRight();
CoordPair oPrevBL = rcPrevNE.bottomLeft();
CoordPair oPrevBR = rcPrevNE.bottomRight();
//
//Line slope will have to be adjusted
//by renderer - can't modify template/form nodes here.
//
switch (lAngle) {
case 0: //nothing to do
return;
case 270: {
//1)rotate the 4 corner points 270 degrees about 'm_oAnchorPoint'
//2)adjust new anchor point to new orientation
//3)flip content margins + border margins
//4)for both caption+non caption extents,
// rotate the 4 corner points 270 degrees about 'anchor' point
oNewTR = oPrevTL.rotatePoint(m_oAnchorPoint, oAngle);
oNewBR = oPrevTR.rotatePoint(m_oAnchorPoint, oAngle);
//oNewBL = oPrevBR.rotatePoint(m_oAnchorPoint, oAngle);
oNewTL = oPrevBL.rotatePoint(m_oAnchorPoint, oAngle);
m_oNEMargins = new Margins(oPrevMargins);
m_oNEMargins = new Margins(oPrevMargins.marginBottom(),
oPrevMargins.marginLeft(),
oPrevMargins.marginTop(),
oPrevMargins.marginRight());
m_oBorderMargins = new Margins(oPrevBorderMargins.marginBottom(),
oPrevBorderMargins.marginLeft(),
oPrevBorderMargins.marginTop(),
oPrevBorderMargins.marginRight());
m_oContentBorderMargins = new Margins(oPrevWidgetBorderMargins.marginBottom(),
oPrevWidgetBorderMargins.marginLeft(),
oPrevWidgetBorderMargins.marginTop(),
oPrevWidgetBorderMargins.marginRight());
m_oContMargins = new Margins(oPrevWidgetContentMargins.marginBottom(),
oPrevWidgetContentMargins.marginLeft(),
oPrevWidgetContentMargins.marginTop(),
oPrevWidgetContentMargins.marginRight());
m_oCapMargins = new Margins(oPrevWidgetCaptionMargins.marginBottom(),
oPrevWidgetCaptionMargins.marginLeft(),
oPrevWidgetCaptionMargins.marginTop(),
oPrevWidgetCaptionMargins.marginRight());
//
//Adjust the top left of content extent for text, etc.
//
m_oRotatedContentTopLeft = oPrevContentExtentTL.rotatePoint(m_oAnchorPoint, oAngle);
}
break;
case 180: {
//1)rotate the 4 corner points 180 degrees about 'm_oAnchorPoint'
//2)adjust new anchor point to new orientation
//3)flip content margins + border margins
//4)for both caption+non caption extents,
// rotate the 4 corner points 180 degrees about 'anchor' point
oNewBR = oPrevTL.rotatePoint(m_oAnchorPoint, oAngle); //A
//oNewBL = oPrevTR.rotatePoint(m_oAnchorPoint, oAngle); //B
oNewTL = oPrevBR.rotatePoint(m_oAnchorPoint, oAngle); //C
oNewTR = oPrevBL.rotatePoint(m_oAnchorPoint, oAngle); //D
m_oNEMargins = new Margins(oPrevMargins.marginRight(),
oPrevMargins.marginBottom(),
oPrevMargins.marginLeft(),
oPrevMargins.marginTop());
m_oBorderMargins = new Margins(oPrevBorderMargins.marginRight(),
oPrevBorderMargins.marginBottom(),
oPrevBorderMargins.marginLeft(),
oPrevBorderMargins.marginTop());
m_oContentBorderMargins = new Margins(oPrevWidgetBorderMargins.marginRight(),
oPrevWidgetBorderMargins.marginBottom(),
oPrevWidgetBorderMargins.marginLeft(),
oPrevWidgetBorderMargins.marginTop());
m_oContMargins = new Margins(oPrevWidgetContentMargins.marginRight(),
oPrevWidgetContentMargins.marginBottom(),
oPrevWidgetContentMargins.marginLeft(),
oPrevWidgetContentMargins.marginTop());
m_oCapMargins = new Margins(oPrevWidgetCaptionMargins.marginRight(),
oPrevWidgetCaptionMargins.marginBottom(),
oPrevWidgetCaptionMargins.marginLeft(),
oPrevWidgetCaptionMargins.marginTop());
//
//Adjust the top left of content extent for text, etc.
//
m_oRotatedContentTopLeft = oPrevContentExtentTL.rotatePoint(m_oAnchorPoint, oAngle);
}
break;
case 90: {
//1)rotate the 4 corner points 90 degrees about 'm_oAnchorPoint'
//2)adjust new anchor point to new orientation
//3)flip content margins + border margins
//4)for both caption+non caption extents,
// rotate the 4 corner points 180 degrees about 'anchor' point
//oNewBL = oPrevTL.rotatePoint(m_oAnchorPoint, oAngle); //A
oNewTL = oPrevTR.rotatePoint(m_oAnchorPoint, oAngle); //B
oNewTR = oPrevBR.rotatePoint(m_oAnchorPoint, oAngle); //C
oNewBR = oPrevBL.rotatePoint(m_oAnchorPoint, oAngle); //D
m_oNEMargins = new Margins(oPrevMargins.marginTop(),
oPrevMargins.marginRight(),
oPrevMargins.marginBottom(),
oPrevMargins.marginLeft());
m_oBorderMargins = new Margins(oPrevBorderMargins.marginTop(),
oPrevBorderMargins.marginRight(),
oPrevBorderMargins.marginBottom(),
oPrevBorderMargins.marginLeft());
m_oContentBorderMargins = new Margins(oPrevWidgetBorderMargins.marginTop(),
oPrevWidgetBorderMargins.marginRight(),
oPrevWidgetBorderMargins.marginBottom(),
oPrevWidgetBorderMargins.marginLeft());
m_oContMargins = new Margins(oPrevWidgetContentMargins.marginTop(),
oPrevWidgetContentMargins.marginRight(),
oPrevWidgetContentMargins.marginBottom(),
oPrevWidgetContentMargins.marginLeft());
m_oCapMargins = new Margins(oPrevWidgetCaptionMargins.marginTop(),
oPrevWidgetCaptionMargins.marginRight(),
oPrevWidgetCaptionMargins.marginBottom(),
oPrevWidgetCaptionMargins.marginLeft());
//Adjust the top left of content extent for text, etc.
m_oRotatedContentTopLeft = oPrevContentExtentTL.rotatePoint(m_oAnchorPoint, oAngle);
}
break;
default: {
//Only 90 degree increments are handled
//Todo: log warning message?
assert(false);
}
break;
}
// Record if there was a rotation
m_bWasRotated = (0 != lAngle);
//
//Todo: calculate true anchor
//
m_oAnchorType = EnumAttr.TOP_LEFT;
m_oAnchorPoint = oNewTL;
//
// Set the new nominal extent
//
UnitSpan oNewWidth = oNewTR.x().subtract(oNewTL.x());
UnitSpan oNewHeight = oNewBR.y().subtract(oNewTR.y());
if (oNewWidth.value() < 0)
oNewWidth = oNewWidth.multiply(-1L);
if (oNewHeight.value() < 0)
oNewHeight = oNewHeight.multiply(-1L);
assert(oNewWidth.gte(UnitSpan.ZERO));
assert(oNewHeight.gte(UnitSpan.ZERO));
m_oNE = m_oNE.width(oNewWidth, false);
m_oNE = m_oNE.height(oNewHeight, false);
}
}
}
/**
* @exclude from published api.
*/
protected void setGrowableH(UnitSpan oMinH, UnitSpan oMaxH) {
assert(oMinH.lte(oMaxH) || oMaxH.equals(new UnitSpan(UnitSpan.INCHES_72K, -1)));
assert(oMinH.gte(UnitSpan.ZERO));
m_bInfiniteMaxH = (oMaxH.equals(new UnitSpan(UnitSpan.INCHES_72K, -1)));
if (oMinH.lt(oMaxH) || m_bInfiniteMaxH) {
m_bGrowableH = true;
m_oMinH = oMinH;
m_oMaxH = oMaxH;
}
else {
m_bGrowableH = false;
m_bInfiniteMaxH = false;
m_oMinH = oMinH;
m_oMaxH = oMinH;
}
}
/**
* @exclude from published api.
*/
protected void setGrowableW(UnitSpan oMinW, UnitSpan oMaxW) {
assert(oMinW.lte(oMaxW) || oMaxW.equals(new UnitSpan(UnitSpan.INCHES_72K, -1)));
assert(oMinW.gte(UnitSpan.ZERO));
m_bInfiniteMaxW = (oMaxW.equals(new UnitSpan(UnitSpan.INCHES_72K, -1)));
if (oMinW.lt(oMaxW) || m_bInfiniteMaxW) {
m_bGrowableW = true;
m_oMinW = oMinW;
m_oMaxW = oMaxW;
}
else {
m_bGrowableW = false;
m_bInfiniteMaxW = false;
m_oMinW = oMinW;
m_oMaxW = oMinW;
}
}
/**
* Return true if this box model contains text that overflows it's allotted space
* and false otherwise.
* @return false if this object contains no text.
* @exclude from published api.
*/
public boolean hasOverflowingContentText() {
return false;
}
/**
* Return true if this box model contains text that overflows it's allotted space
* and false otherwise.
* @return false if this object contains no text.
* @exclude from published api.
*/
public boolean hasOverflowingCaptionText() {
return false;
}
}