com.adobe.xfa.text.TextFrame Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
package com.adobe.xfa.text;
import com.adobe.xfa.gfx.GFXDriver;
import com.adobe.xfa.gfx.GFXEnv;
import com.adobe.xfa.gfx.GFXModelContext;
import com.adobe.xfa.ut.CoordPair;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.Storage;
import com.adobe.xfa.ut.UnitSpan;
/**
* The text frame class represents the geometry and eventually layout of
* a single frame.
*
* The client creates all frame instances, either to pass to the sparse
* stream API or as a result of its implementation of the sparse stream
* event handling methods. The client must populate its created frames
* with geometry information. A future implementation may allow the
* client to set text attributes for a frame as well.
*
*
* Once the client passes a frame instance to the sparse stream, AXTE
* takes over and populates its layout. The layout may come from a
* client-supplied layout object, or it may be generated by AXTE as the
* result of a reflow operation.
*
*
* Frames are reference counted objects, allowing the client to retain
* handles to objects that are managed by AXTE.
*
*
* While frames have width and height geometry, it is up to the client
* to decide how to map frames into its rendering space. AXTE treats
* the top-left corner of each frame as its origin.
*
* @exclude from published api.
*/
public abstract class TextFrame implements GFXModelContext {
static final int LAYOUT_NORMAL = 0;
static final int LAYOUT_INITIAL = 1;
static final int LAYOUT_SUSPECT = 2;
private static final Rect gDefaultExtent = Rect.ZERO;
private TextSparseStream mpoStream;
private TextPosn moStart; // TODO: which member objects to pre-create?
private CoordPair moXYOrigin = CoordPair.ZERO_ZERO;
private UnitSpan moAMax = UnitSpan.ZERO;
private UnitSpan moBMax = UnitSpan.ZERO;
private Rect moExtent = gDefaultExtent;
private final Storage moLines = new Storage(); // text broken into lines
private int mnActualCombCells;
private int meLayout = LAYOUT_NORMAL;
private int meOldOrientation = TextAttr.ORIENTATION_HORIZONTAL;
private boolean mbIsDirty;
/**
* Default constructor.
*/
public TextFrame () {
}
/**
* Copy constructor.
* @param oSource - Source frame to copy.
*/
public TextFrame (TextFrame oSource) {
copyFrom (oSource);
}
/**
* Query whether vertical alignment occurs at a point.
*
* The text label class performs vertical alignment at a point. The
* text block and region have a nominal height. Vertical alignment
* occurs within this height. This method asks the stream what sort of
* vertical alignment it supports.
* @return TRUE if the stream aligns vertically at a point; FALSE if it
* aligns vertically within a nominal height.
*/
public boolean alignVPoint () {
return minHeight().value() < 0;
}
/**
* Query whether horizontal alignment occurs at a point.
*
* The text label class performs horizontal alignment at a point. The
* text block and region have a nominal width. Horizontal alignment
* occurs within this width. This method asks the stream what sort of
* horizontal alignment it supports.
* @return TRUE if the stream aligns horizontally at a point; FALSE if
* it aligns horizontally within a nominal width.
*/
public boolean alignHPoint () {
return minWidth().value() < 0;
}
/**
* Query whether the stream has an unlimited width.
*
* The text label can grow arbitrarily in X. A text region may have a
* maximum width, or it may allow unlimited horizontal growth. This
* method asks the object whether it has an unlimited width.
* @return TRUE if the width is unlimited; FALSE if there is a limit on
* the width.
*/
public boolean unlimitedWidth () {
return maxWidth().value() < 0;
}
/**
* Query whether the stream has an unlimited height.
*
* The text label can grow arbitrarily in Y. A text region may have a
* maximum height, or it may allow unlimited vertical growth. This
* method asks the object whether it has an unlimited height.
* @return TRUE if the height is unlimited; FALSE if there is a limit on
* the height.
*/
public boolean unlimitedHeight () {
return maxHeight().value() < 0;
}
/**
* Query whether this frame is "dirty".
*
* A frame is considered dirty if a layout operation changes any of its
* lines. Any frame that contains a content change or any frame
* thereafter whose lines change due to reflow will be considered dirty.
* When a frame is initially loaded with layout through any of the
* TextSparse stream call-backs, it starts out not being dirty. The
* application can use this method to determine whether to write the
* frame's content on save.
* @return True if the frame is dirty; false if not.
*/
public boolean isDirty () {
return mbIsDirty;
}
/**
* Set the frame's dirty flag.
* @param bDirty - New value for the frame's dirty flag.
*/
public void setDirty (boolean bDirty) {
mbIsDirty = bDirty;
}
/**
* Obtain the rectangle(s) in the laid-out text corresponding to the
* given range in this frame.
*
* A single range might yield multiple rectangles if it spans multiple
* lines or there is bidirectional text. Note that the selection range
* may span multiple frames.
* @param oRange - Selection range.
* @param oRectangles - Array to receive the rectangles.
* @return True if any rectangles were found; false otherwise. This
* method could return false if the range doesn't correspond to text in
* this frame or the range is degenerate.
*/
// public boolean GetSelectionRectangles (TextRange oRange, Storage oRectangles) {
// TextSelection oSelection;
// oSelection = oRange;
// oRectangles.setSize (0, false);
// return GetSelectionRectangles (oSelection, UnitSpan.ZERO, oRectangles);
// }
/**
* Render the frame contents, using the properties provided.
*
* Given a set of rendering properties in an instance of class {@link
* TextDrawInfo}, this method performs the appropriate rendering.
* @param oDrawInfo - Rendering properties. For more information,
* please see the description of {@link TextDrawInfo}.
*/
public void gfxDraw (TextDrawInfo oDrawInfo) {
Rect oInvalid = oDrawInfo.getInvalid();
if (oInvalid.isDegenerate()) {
UnitSpan oLarge = new UnitSpan (UnitSpan.INCHES_72K, 0xFFFFFF);
UnitSpan oLargeNeg = new UnitSpan (UnitSpan.INCHES_72K, -0xFFFFFF);
if (unlimitedWidth()) {
oInvalid.leftRight (oLargeNeg, oLarge);
} else {
oInvalid.leftRight (UnitSpan.ZERO, maxWidth());
}
if (unlimitedHeight()) {
oInvalid.topBottom (oLargeNeg, oLarge);
} else {
oInvalid.topBottom (UnitSpan.ZERO, maxHeight());
}
}
DrawParm oParm = new DrawParm (oDrawInfo);
oParm.setInvalid (oInvalid);
gfxDraw (oParm, UnitSpan.ZERO);
}
/**
* Render the frame's content.
*
* This method is provided so that the application can render a single
* frame's content in an application-controlled environment. The top
* left corner of the frame is mapped to (0,0) in relative coordinates,
* so the application may need to push an offset before calling this
* method.
* @param poGfxEnv - Graphic environment in which to do the rendering.
* @param poInvalid - Pointer to invalidation rectangle (in frame
* coordinates). If NULL, the frame uses its maximum width and height.
* @param poSelection - Optional text selection. If non-null, this
* represents a range and alternate colours for drawing of selected
* text. Note that a single selection instance can be used across all
* frames in the sparse stream.
* @param eOpt - Run optimization flag. Enumeration values are in class
* TextPrefOpt. OPT_UNKNOWN or OPT_CHAR produces the most accurate
* behaviour, where each character is individually positioned. OPT_WORD
* will result in the first character of each word being positioned, and
* the graphic driver positioning the remaining characters based on the
* character advances. OPT_LINE extends the word concept to entire
* lines. Reducing the number of positioning operations performed by
* the display improves display performance, but it may result in a loss
* of accuracy when font sizes are rounded, depending on the abilities
* of the underlying graphics environment. Note that AGM- and PDF-based
* environments can use OPT_LINE for maximum performance without losing
* accuracy.
*/
public void gfxDraw (GFXEnv poGfxEnv, Rect poInvalid, TextSelection poSelection, int eOpt) {
TextDrawInfo oDrawInfo = new TextDrawInfo (poGfxEnv);
if (poInvalid != null) {
oDrawInfo.setInvalid (poInvalid);
}
oDrawInfo.setPrimary (poSelection);
// oDrawInfo.setOpt (eOpt);
gfxDraw (oDrawInfo);
}
public void gfxDraw (GFXEnv poGfxEnv, Rect poInvalid, TextSelection poSelection) {
gfxDraw (poGfxEnv, poInvalid, poSelection, 0);
}
public void gfxDraw (GFXEnv poGfxEnv, Rect poInvalid) {
gfxDraw (poGfxEnv, poInvalid, null, 0);
}
public void gfxDraw (GFXEnv poGfxEnv) {
gfxDraw (poGfxEnv, null, null, 0);
}
/**
* Generate a text layout object from this frame's layout.
* @param bAllowCharGlyphs - True if glyph IDs can be replaced with
* their corresponding Unicode character IDs where such corresponding
* characters exist. False if all output glyphs are to be represented
* by glyph IDs.
* @return Text layout object with the layout of this frame.
*/
public TextLayout format (boolean bAllowCharGlyphs) {
TextLayout poLayout = new TextLayout();
for (int i = 0; i < moLines.size(); i++) {
getLine(i).compose (poLayout, bAllowCharGlyphs);
}
return poLayout;
}
/**
* Return the frame's layout orientation.
* @return The layout orientation in effect for this text frame.
* Orienation may be horizontal, vertical RTL or vertical RTL.
*/
public int getLayoutOrientation () {
return display().getLayoutOrientation();
}
/**
* Assignment operator.
* @param oSource - Source frame to copy.
*/
public void copyFrom (TextFrame oSource) {
meLayout = oSource.meLayout;
mbIsDirty = oSource.mbIsDirty;
}
/**
* Return the frame's minimum width.
*
* All frames have both a minimum and maximum width, which may or may
* not be the same. This method returns the minimum width.
* @return Minimum width allowed for the frame. A value of zero is
* possible, as is a value less than zero. In the latter case, it
* indicates that the frame is horizontally aligned at a point, though
* callers should use the AlignHPoint() method to test for this.
*/
abstract public UnitSpan minWidth ();
/**
* Return the frame's minimum height.
*
* All frames have both a minimum and maximum height, which may or may
* not be the same. This method returns the minimum height.
* @return Minimum height allowed for the frame. A value of zero is
* possible, as is a value less than zero. In the latter case, it
* indicates that the frame is verrically aligned at a point, though
* callers should use the AlignVPoint() method to test for this.
*/
abstract public UnitSpan minHeight ();
/**
* Return the frame's maximum width.
*
* All frames have both a minimum and maximum width, which may or may
* not be the same. This method returns the maximum width.
* @return maximum width allowed for the frame. A value of zero is
* possible, as is a value less than zero. In the latter case, it
* indicates that the frame has unlimited width, though callers should
* use the UnlimitedWidth() method to test for this.
*/
abstract public UnitSpan maxWidth ();
/**
* Return the frame's maximum height.
*
* All frames have both a minimum and maximum height, which may or may
* not be the same. This method returns the maximum height.
* @return maximum height allowed for the frame. A value of zero is
* possible, as is a value less than zero. In the latter case, it
* indicates that the frame has unlimited height, though callers should
* use the Unlimitedheight() method to test for this.
*/
abstract public UnitSpan maxHeight ();
/**
* Create a copy (deep clone) of this frame object.
*
* The copy must have the same geometry as this object. It is up to
* AXTE to subsequently manage the clones layout and relationship to
* content.
* @return Cloned copy of the frame. The lifetime of this object will
* be managed with reference counting.
*/
abstract public TextFrame cloneFrame ();
TextSparseStream getStream () {
return mpoStream;
}
void setStream (TextSparseStream poStream) {
mpoStream = poStream;
}
TextPosn getStart () {
return moStart;
}
void setStart (TextPosnBase oStart) {
moStart = new TextPosn (oStart);
moStart.position (TextPosn.POSN_BEFORE);
// Obscure: Each frame's position will advance for content insertions
// only of those insertions strictly before the frame's position. An
// insertion at the frame's start ends up being included in the frame's
// content (which is normally the right thing). However, if the frame
// before this one was empty and just got loaded with content, our start
// position will not have advanced because it was not strictly after the
// empty frame's start position. Class TextSparseStream detects this
// condition and calls SetStart() to update our position. But we must
// also update our first line's start position in such a case.
if ((moLines != null) && (moLines.size() > 0)) {
DispLineWrapped poLine = getLine (0);
if (poLine.getPositionCount() > 0) {
TextPosn oLineStart = poLine.getPosition(0).pp();
if ((moStart.stream() == oLineStart.stream())
&& (moStart.index() >= oLineStart.index())) {
oLineStart.copyFrom (moStart);
oLineStart.tighten (true);
}
}
}
}
TextDisplay display () {
assert (mpoStream != null);
assert (mpoStream.display() != null);
return mpoStream.display();
}
// TextGfxConnect TextConnect () {
// return Display().textConnect();
// }
UnitSpan getAMax () {
return moAMax;
}
UnitSpan getBMax () {
return moBMax;
}
Rect getExtent () {
return moExtent;
}
void setExtent (Rect oExtent) {
moAMax = oExtent.right();
moBMax = oExtent.bottom();
moExtent = ABXY.toXY (getXYOrigin(), oExtent, getLayoutOrientation());
}
Rect runtimeExtent (boolean bExtended) {
return xyFullExtent (bExtended);
}
CoordPair getXYOrigin () {
return moXYOrigin;
}
// boolean GetSelectionRectangles (TextSelection poSelection, UnitSpan oOffset, Storage oRectangles) {
// boolean bFound = false;
// for (int i = 0; i < moLines.size(); i++) {
// boolean bThisTime;
// TextDispLineWrapped poLine = moLines[i];
// bThisTime = TextDispDrawAttr.getSelectionRectangles (poLine, poSelection, oYOffset, oRectangles);
// if (bThisTime) {
// bFound = true;
// } else if (bFound) {
// break;
// }
// }
//
// return bFound;
// }
int getActualCombCells () {
return mnActualCombCells;
}
int getLayoutState () {
return meLayout;
}
Rect preLayout (FormatInfo oFormatInfo) {
Rect oOldExtent = Rect.ZERO;
// Save the current extent, so that we can properly invalidate if it gets
// smaller.
if (moLines.size() > 0) {
oOldExtent = abSubExtent (0, moLines.size(), meOldOrientation, true);
}
switch (oFormatInfo.getChange().type()) {
case DispChange.CHANGE_NONE:
case DispChange.CHANGE_INSERT:
case DispChange.CHANGE_DELETE:
case DispChange.CHANGE_OTHER:
if (meLayout != LAYOUT_SUSPECT) {
meLayout = LAYOUT_NORMAL;
}
mbIsDirty = true;
break;
case DispChange.CHANGE_FRAME:
if (mpoStream.getFrame (oFormatInfo.getSuspectFrameIndex()) == this) {
meLayout = LAYOUT_NORMAL;
}
break;
default:
meLayout = LAYOUT_NORMAL;
mbIsDirty = true;
}
mnActualCombCells = combCells();
if ((mnActualCombCells > 0)
|| (oFormatInfo.getJustH() == TextAttr.JUST_H_COMB_LEFT)
|| (oFormatInfo.getJustH() == TextAttr.JUST_H_COMB_CENTRE)
|| (oFormatInfo.getJustH() == TextAttr.JUST_H_COMB_RIGHT)) {
if (mnActualCombCells == 0) {
mnActualCombCells = mpoStream.maxSize();
}
if (mnActualCombCells == 0) {
mnActualCombCells = 1; // fake at least one cell
}
}
return oOldExtent;
}
void postLayout (FormatInfo oFormatInfo, Rect oOldExtent, boolean bIsJustifyOnly) {
if (moLines.size() == 0) {
return; // This frame about to be removed
}
// Justification starts here
// First, compute the width of the text block. If there is a
// justification width, it takes priority. Otherwise, use the greater of
// the longest line width and the minimum width. This is the width in
// which all (other) lines will be horizontally justified. Iterating
// through the lines to examine their widths also gives us an opportunity
// to determine the text height.
UnitSpan oWidth = UnitSpan.ZERO;
UnitSpan oLinesHeight = UnitSpan.ZERO;
boolean bRestrictWidth = false;
boolean bIsHorizontalLayout = false;
if (isOrientationHorizontal()) {
if (enforceAlignmentWidth()) {
oWidth = alignmentWidth();
bRestrictWidth = true;
} else if (! alignHPoint()) {
oWidth = minWidth();
}
bIsHorizontalLayout = true;
} else {
if (enforceAlignmentHeight()) {
oWidth = alignmentHeight();
bRestrictWidth = true;
} else if (! alignHPoint()) {
oWidth = minHeight();
}
bIsHorizontalLayout = false;
}
int nLines = bIsJustifyOnly ? moLines.size() : oFormatInfo.getNewSize();
for (int i = 0; i < nLines; i++) {
DispLineWrapped poLine = getLine (i);
UnitSpan oRawWidth = poLine.getRawWidth();
if ((! bRestrictWidth) && (oRawWidth.gt (oWidth))) {
oWidth = oRawWidth;
}
oLinesHeight = oLinesHeight.add (poLine.getBExtent());
}
if (! bRestrictWidth) {
UnitSpan oMaxWidth = bIsHorizontalLayout ? maxWidth() : maxHeight();
if ((oMaxWidth.value() >= 0) && (oWidth.gt (oMaxWidth))) {
oWidth = oMaxWidth;
}
}
// Vertical justification starts here.
UnitSpan oJustifyHeight = UnitSpan.ZERO;
UnitSpan oOffsetY = UnitSpan.ZERO;
// If vertically justified at a point, compute the amount to move up if
// middle- or bottom-justified.
if (alignVPoint()) {
switch (oFormatInfo.getJustV()) {
case TextAttr.JUST_V_MIDDLE:
oOffsetY = oLinesHeight.divide (2);
oOffsetY = new UnitSpan (oOffsetY.units(), -oOffsetY.value());
oJustifyHeight = oOffsetY;
break;
case TextAttr.JUST_V_BOTTOM:
oOffsetY = new UnitSpan (oLinesHeight.units(), -oLinesHeight.value());
break;
default:
oJustifyHeight = oLinesHeight;
break;
}
}
// Vertical justification in a given height: compute the amount to move
// down if middle- or bottom-justified.
else {
if (bIsHorizontalLayout) {
oJustifyHeight = enforceAlignmentHeight() ? alignmentHeight() : minHeight();
} else {
oJustifyHeight = enforceAlignmentWidth() ? alignmentWidth() : minWidth();
}
UnitSpan oRoomLeft = oJustifyHeight.subtract (oLinesHeight);
if (oRoomLeft.value() > 0) {
switch (oFormatInfo.getJustV()) {
case TextAttr.JUST_V_MIDDLE:
oOffsetY = oLinesHeight.divide (2);
break;
case TextAttr.JUST_V_BOTTOM:
oOffsetY = oRoomLeft;
break;
}
} else {
oJustifyHeight = oLinesHeight;
}
}
UnitSpan oOriginX = bIsHorizontalLayout ? UnitSpan.ZERO : oJustifyHeight;
moXYOrigin = new CoordPair (oOriginX, moXYOrigin.y());
// Now we can do the actual justification. Run through the lines and get
// each to justify itself. Also set its final vertical position.
int i;
for (i = 0; i < nLines; i++) {
DispLineWrapped poLine = getLine (i);
if ((i >= oFormatInfo.getOldSize()) || (oFormatInfo.getOld (i) != null)) {
poLine.justify (oWidth); // this line has been reformatted
}
CoordPair oLineOrigin = new CoordPair (UnitSpan.ZERO, oOffsetY);
oLineOrigin = ABXY.toXY (moXYOrigin, oLineOrigin, getLayoutOrientation());
poLine.setXYOrigin (oLineOrigin);
oOffsetY = poLine.getBMax();
}
// Calculate the display extent for the owning stream.
Rect oExtent = abSubExtent (0, nLines);
// UnitSpan oAMin = oExtent.left();
// UnitSpan oBMin = getLine(0).getBMinExtended (false);
// UnitSpan oAMax = oExtent.right();
// UnitSpan oBMax = getLine(nLines-1).getBMaxExtended (false);
if (! adjustAndSetExtent (oLinesHeight, oExtent, oFormatInfo.getNewSize() > 1)) {
oFormatInfo.setFits (false);
}
// boolean bExtentSet = false;
// TODO: invalidation code removed
// Set the stream's extent if we haven't done so already.
// if (! bExtentSet) {
// setExtent (oExtent); // TODO: now done in adjustAndSetExtent()
// }
// This frees any lines that have been replaced, as well as any at the
// end of our array (if it got shorter).
oFormatInfo.releaseOldLines();
// Update font subsetting information for each line.
if (! oFormatInfo.isUpdate()) {
for (i = 0; i < moLines.size(); i++) {
getLine(i).updateSubsettedChars();
}
}
debugLines();
meOldOrientation = getLayoutOrientation();
moStart = getLine(0).getStartPosition();
}
/*
void InvalidateArea (Rect oInvalid, boolean bEraseBkgnd) {
Display().invalidateArea (oInvalid, bEraseBkgnd, this);
}
*/
/*
void Justify (TextDispFormatInfo oFormatInfo) {
Rect oOldExtent;
PreLayout (oFormatInfo, oOldExtent);
PostLayout (oFormatInfo, oOldExtent, true);
}
*/
/*
void ScrollTo (TextPosnBase oPosn, GFXEnv oGfxEnv) {
TextConnect().scrollTo (oPosn, oGfxEnv);
}
*/
/*
void OnChange (boolean bExtentChanged) {
TextConnect().onChange (bExtentChanged);
}
*/
UnitSpan subHeight (int nStart, int nEnd) {
int nMax = moLines.size();
if (nMax == 0) {
return UnitSpan.ZERO;
}
int nSafeStart = nStart;
if (nSafeStart >= nMax) {
nSafeStart = nMax - 1;
}
int nSafeEnd = nEnd;
if (nSafeEnd > nMax) {
nSafeEnd = nMax;
}
if (nSafeEnd <= nSafeStart) {
return UnitSpan.ZERO;
}
UnitSpan oStart = getLine(nSafeStart).getBMin();
UnitSpan oEnd = getLine(nSafeEnd-1).getBMax();
/*
if (GetGfxEnv() != null) {
oStart = GetGfxEnv().unitH (GetGfxEnv().devH (oStart));
oEnd = GetGfxEnv().unitH (GetGfxEnv().devH (oEnd));
}
*/
return oEnd.subtract (oStart);
}
UnitSpan lineMinY (int nIndex) {
if (nIndex >= moLines.size()) {
return UnitSpan.ZERO;
} else {
return getLine(nIndex).getXYOrigin().y();
}
}
void debugLines () {
TextContext context = getContext();
if (context.debug()) {
for (int i = 0; i < moLines.size(); i++) {
getLine(i).textDebug (i);
}
}
}
TextContext getContext () {
return mpoStream.getContext();
}
/*
GFXEnv GetGfxEnv () {
return Display().getGfxEnv();
}
*/
/*
TextFontMap GetFontMap () {
return Display().getFontMap();
}
*/
DispMapSet getDisposableMaps () {
return display().getDisposableMaps();
}
public void releaseDisposableMaps (DispMapSet poMaps) {
display().releaseDisposableMaps (poMaps);
}
/*
AXTEWRSBase GetWRS () {
return Display().getWRS();
}
*/
/*
TextGlyphArray GetGlyphArray () {
return Display().getGlyphArray();
}
*/
/*
void SetLocale (TextAttr poAttr) {
Display().setLocale (poAttr);
}
*/
/*
TextLocaleInfo GetLocale (String sLocaleName) {
return Display().getLocale (sLocaleName);
}
*/
/*
TextBreakFinder GetBreakFinder () {
return Display().getBreakFinder();
}
*/
boolean isOrientationHorizontal () {
return getLayoutOrientation() == TextAttr.ORIENTATION_HORIZONTAL;
}
boolean isOrientationVertical () {
return getLayoutOrientation() != TextAttr.ORIENTATION_HORIZONTAL;
}
boolean isRTL (TextAttr poAttr) {
return display().isRTL (poAttr);
}
boolean isIdeographic (TextAttr poAttr) {
return display().isIdeographic (poAttr);
}
/*
boolean OptycaJustify (TextAttr poAttr) {
return Display().optycaJustify (poAttr);
}
*/
/*
int GetDigits (TextAttr poAttr) {
return Display().getDigits (poAttr);
}
*/
/*
bool GetBreakCandidates (int nCount) {
return Display().getBreakCandidates (nCount);
}
*/
DispLineWrapped allocateWrappedLine (LineDesc oLineDesc) {
DispLineWrapped poLine = getContext().allocateWrappedLine (this, oLineDesc);
poLine.setVerticalOrientation (getLayoutOrientation() != TextAttr.ORIENTATION_HORIZONTAL);
return poLine;
}
void releaseWrappedLine (DispLineWrapped poLine) {
getContext().releaseWrappedLine (poLine);
}
int getLegacyLevel () {
return display().getLegacyLevel();
}
public int getLineCount () {
return moLines.size();
}
DispLineWrapped getLine (int nIndex) {
return (DispLineWrapped) moLines.get (nIndex);
}
Storage getLines () {
return moLines;
}
boolean gfxDraw (DrawParm oParm, UnitSpan oOffset) {
boolean bFit = true;
GFXModelContext poContext = ((TextFrame) (this));
GFXDriver poDriver = oParm.env().driverAttach (poContext);
oParm.setDriver (poDriver);
CoordPair oFrameOrigin = new CoordPair (UnitSpan.ZERO, oOffset);
oParm.driver().pushOffset (true, oFrameOrigin);
try {
oFrameOrigin = new CoordPair (getXYOrigin().x(), oFrameOrigin.y());
oParm.translate (oFrameOrigin, getLayoutOrientation());
for (int i = 0; i < moLines.size(); i++) {
DispLineWrapped poLine = getLine (i);
UnitSpan oBMin = poLine.getBMin();
UnitSpan oBMax = poLine.getBMax();
UnitSpan oBExtent = poLine.getBExtent();
if (oParm.truncate() != null) {
if (oBMax.gt (oParm.truncateBMax())) {
// Watson 1312011: Make sure it really overflows; not just round-off due
// to Designer's incessant switching between awkward units.
int oRound1 = UnitSpan.convertUnit (UnitSpan.POINTS_1K, oBMax.units(), oBMax.value());
int oRound2 = UnitSpan.convertUnit (UnitSpan.POINTS_1K, oParm.truncateBMax().units(), oParm.truncateBMax().value());
int nDiff = oRound1 - oRound2;
if (nDiff > 1) {
bFit = false;
break;
}
}
}
boolean bInvalidHasHeight = (oParm.invalid().height().value() > 0);
if (bInvalidHasHeight) {
if (oBMin.gt (oParm.invalidBMax())) {
break; // starts "below" invalid area // no need to continue
}
if (oBMin.equals (oParm.invalidBMax()) && oBExtent.gt (UnitSpan.ZERO)) { // starts at end of invalid area
break; // no need to continue
}
}
if (oBMax.gt (oParm.invalidBMin()) || (oBMax.equals (oParm.invalidBMin()) && (! bInvalidHasHeight))) { // see if there is any overlap and draw
bFit = poLine.gfxDraw (oParm) && bFit;
}
}
} finally {
oParm.driver().popOffset();
oParm.env().driverDetach (poContext);
}
oParm.setDriver (null);
return bFit;
}
boolean isInsertAtEnd (DispChange oChange, int nLine) {
if (oChange.type() != DispChange.CHANGE_INSERT) {
// Determine if the change is an insert at the end of a line. An
// insertion at the end of a line (a fairly common case in typing) means
// that we can start formatting on that line. Any other change might
// cause part of the line's text to now fit on the previous line (e.g.,
// inserting a space in the first word or deleting text).
return false;
}
DispLineWrapped poLine = getLine (nLine);
if ((poLine.getStartBreak() == DispLine.LINE_BREAK_HYPHEN) || (poLine.getStartBreak() == DispLine.LINE_BREAK_HYPHEN_SUPPRESS)) {
return false; // insertion may change hyphenation
}
TextPosnBase oResult = new TextPosnBase();
if (poLine.getCaretStartEnd (oChange.stream(), true, false, oResult) == DispLineWrapped.CARET_INVALID) {
return false;
}
return oChange.index() >= oResult.index();
}
public int combCells () {
return 0;
}
boolean allowExtension () {
return false;
}
boolean suppressWordWrap () {
return false;
}
UnitSpan alignmentHeight () {
return maxHeight();
}
boolean enforceAlignmentHeight () {
return false;
}
UnitSpan alignmentWidth () {
return minWidth();
}
boolean enforceAlignmentWidth () {
return false;
}
boolean testFitSize () {
return false;
}
boolean enforceDisplayExtent () {
return false;
}
TextNullFrame isNullFrame () {
return null;
}
protected void reflowFromHere () {
if (mpoStream == null) {
return;
}
TextDisplay poDisplay = mpoStream.display();
if (poDisplay == null) {
return;
}
poDisplay.updateToEnd (moStart.stream(), moStart.index());
}
// Adjusted stream's extent after formatting.
private boolean adjustAndSetExtent (UnitSpan oHeight, Rect oABExtent, boolean bTestFit) {
boolean bFit = true;
// Override the calculated width if the stream has a fixed width.
if ((! unlimitedWidth()) && (maxWidth().gt (oABExtent.width()))) {
oABExtent = oABExtent.leftRight (UnitSpan.ZERO, maxWidth());
}
if (! unlimitedHeight()) {
// If there is a fixed height, override the calculated height if it is
// smaller or the height must be enforced.
boolean bForceExtent = enforceDisplayExtent();
if (oHeight.lte (maxHeight())) {
bForceExtent = true;
} else if (bTestFit && testFitSize()) {
bFit = false; // should not be inserted
}
if (bForceExtent) {
oABExtent = oABExtent.topBottom (UnitSpan.ZERO, maxHeight());
}
}
setExtent (oABExtent);
return bFit;
}
// private Rect abFullExtent (boolean bExtended) {
// return abSubExtent (0, moLines.size(), bExtended);
// }
private Rect abSubExtent (int nStart, int nEnd) {
return abSubExtent (nStart, nEnd, getLayoutOrientation(), false);
}
// private Rect abSubExtent (int nStart, int nEnd, boolean bExtended) {
// return abSubExtent (nStart, nEnd, getLayoutOrientation(), bExtended);
// }
private Rect abSubExtent (int nStart, int nEnd, int eOrientation, boolean bExtended) {
return subExtent (true, nStart, nEnd, getLayoutOrientation(), bExtended);
}
private Rect xyFullExtent (boolean bExtended) {
return xySubExtent (0, moLines.size(), bExtended);
}
// private Rect XYFullExtent () {
// return xySubExtent (0, moLines.size(), false);
// }
private Rect xySubExtent (int nStart, int nEnd, boolean bExtended) {
return xySubExtent (nStart, nEnd, getLayoutOrientation(), bExtended);
}
private Rect xySubExtent (int nStart, int nEnd, int eOrientation, boolean bExtended) {
return subExtent (false, nStart, nEnd, getLayoutOrientation(), bExtended);
}
private Rect subExtent (boolean bAB, int nStart, int nEnd, int eOrientation, boolean bExtended) {
UnitSpan oLeft = UnitSpan.ZERO;
UnitSpan oTop = UnitSpan.ZERO;
UnitSpan oRight = UnitSpan.ZERO;
UnitSpan oBottom = UnitSpan.ZERO;
for (int i = nStart; i < nEnd; i++) {
DispLineWrapped poLine = getLine (i);
if (poLine != null) {
UnitSpan oAMin = poLine.getAMin();
UnitSpan oAMax = poLine.getAMax (bExtended);
if (oLeft == null) {
oTop = bExtended ? poLine.getBMinExtended (false) : poLine.getBMin();
oLeft = oAMin;
oRight = oAMax;
} else {
if (oAMin.lt (oLeft)) {
oLeft = oAMin;
}
if (oAMax.gt (oRight)) {
oRight = oAMax;
}
}
oBottom = bExtended ? poLine.getBMaxExtended (false): poLine.getBMax();
}
}
Rect result = new Rect (oLeft, oTop, oRight, oBottom);
if (! bAB) {
result = ABXY.toXY (getXYOrigin(), result, eOrientation);
}
return result;
}
//----------------------------------------------------------------------
//
// clearLineArray: Delete all the non-NULL pointers in a
// given line array. Note: this should be a template
// function because the functionality is used elsewhere it
// Text Services. Unfortunately, it doesn't with the Borland
// compiler.
//
//----------------------------------------------------------------------
// private static void clearLineArray (List oLines) {
// for (int i = 0; i < oLines.size(); i++) {
// oLines.set (i, null);
// }
// }
}