All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.text.TextDisplay Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
package com.adobe.xfa.text;


import com.adobe.xfa.gfx.GFXEnv;
import com.adobe.xfa.ut.Rect;
import com.adobe.xfa.ut.Storage;
import com.adobe.xfa.ut.UnitSpan;

/**
 * The text display plays a key role in the presentation of text, and in
 * operations that map between graphic locations and text positions
 * (e.g., move up one line).
 * 

* An application never creates a display directly. Instead, it asks a * displayable stream (derived class of TextDispStr) to create one. * Once created, the text display instance belongs to the displayable * stream instance until the latter is destroyed. The application must * never destroy a text display. *

*

* While the presence of a text display enables a large number of * operations, the display API is fairly light. Many display-related * operations are handled through the text stream, position and range * APIs. *

*

* For more information, please see the external documentation. *

* @exclude from published api. */ public class TextDisplay { static class FindCaretInfo { final int mFrameIndex; final int mLineIndex; final Rect mCaret; FindCaretInfo (int frameIndex, int lineIndex, Rect caret) { mFrameIndex = frameIndex; mLineIndex = lineIndex; mCaret = caret; } } // public class UpdateIgnore { // TODO: what to do with this? // private TextDisplay mpoDisplay; // // public UpdateIgnore (TextDisplay poDisplay) { // mpoDisplay = poDisplay; // if (mpoDisplay != null) { // mpoDisplay.ignoreUpdates (true); // } // } // // public void finalize () { // if (mpoDisplay != null) { // mpoDisplay.ignoreUpdates (false); // } // } // } TextSparseStream mpoStream; // owning stream // public TextGfxConnect moConnect; // invalidation connection public GFXEnv mpoEnv; // graphic environment // TODO: this is never initialized public int mnLineCount; public int mnSuppressFormat; // suppress regeneration? public int mnIgnoreUpdates; // ignore updates altogether? public final DispChange moChange = new DispChange(); // stream change information public final Storage moTabs = new Storage(); // embedded tab objects public LocaleInfo moLocaleInfo; // locale info for layout public TextBreakFinder mpoBreakFinder; // line break finder public int meLayoutOrientation = TextAttr.ORIENTATION_HORIZONTAL; public boolean mbHasFontSubstitution; // has font substitution taken place? /** * Render the display 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}. * @return False if truncate was requested and some text was truncated. * true in all other cases. */ public boolean gfxDraw (TextDrawInfo oDrawInfo) { Rect oTranslated = oDrawInfo.getInvalid(); if ((oTranslated == null) || oTranslated.isDegenerate()) { oTranslated = computeClipRect (mpoStream, oDrawInfo.getInvalidDefault()); } // GFXEnv oGfxEnv = oDrawInfo.getGfxEnv(); // moConnect.beginPaint (oGfxEnv, oTranslated); // make sure EndPaint gets called on exit // EndPaint oEndPaint (moConnect, oGfxEnv); DrawParm oParm = new DrawParm (oDrawInfo); oParm.setInvalid (oTranslated); Rect oExtent; if (oDrawInfo.getTruncate()) { oExtent = computeClipRect (mpoStream); oParm.setTruncate (oExtent); } FrameGfxDraw oDraw = new FrameGfxDraw (mpoStream, oParm); // TBD: what process level? oDraw.processFrames(); return oDraw.fits(); } /** * Obtain the number of lines of text. * @return Number of lines of text in the display. */ public int lines () { return mnLineCount; } /** * Determine a split point in the display. *

* This method exists for applications that wish to split a text display * across pages without actually modifying the underlying text. Given a * vertical split point target, this method returns the bottom offset of * the last line that fits entirely within the requested range. The * apllication can then use this in constructing two invalidation * rectangles to constrain the drawing of this text display in two * separate gfxDraw() calls, one for each page. * @param oTarget - Vertical split target. * @param poSplitPosn - (optional) Pointer to text position object to * receive the split position. If NULL (default) no position is * returned. * @return Split offset of last line that fits completely. */ // public UnitSpan split (UnitSpan oTarget, TextPosnBase poSplitPosn, boolean pbEndsInNewLine) { // if (pbEndsInNewLine != null) { // pbEndsInNewLine = false; // } // // UnitSpan oSplit (oTarget); // TextFrameOffsetFinder oFinder (mpoStream, oSplit); // // if (oFinder.processFrames()) { // split comes after last line // if (poSplitPosn != null) { // poSplitPosn.associate (mpoStream, UINT_MAX); // } // } // // else { // DispLineWrapped poLine = oFinder.getLine(); //// Note: there can be blank space between lines or before the first line //// (vertical justification). If the split point comes before the line, //// we can use as is--it's in the blank space. // UnitSpan oOffset (UnitSpan.PICA_PT_1K, 0); // oOffset += poLine.getBMin(); // // if (oOffset < oTarget) { // oSplit = oOffset; // } // // if (poSplitPosn != null) { // if (poLine.getPositionCount() == 0) { // empty line // poSplitPosn.associate (mpoStream, UINT_MAX); // } else { // poSplitPosn = poLine.getPosition (0); // poSplitPosn.tighten (false); // before any leading attributes // } // // if (pbEndsInNewLine != null) { // TextPosnBase oNewLineTest (poSplitPosn); // TextItemCode ePrev = oNewLineTest.prevUserPosnType(); // // if (ePrev == TEXT_ITEM_PARA) { // pbEndsInNewLine = true; // } else if (ePrev == TEXT_ITEM_CHAR) { // UniChar c = oNewLineTest.nextChar(); // int eData = TextCharProp.getCharProperty (c); // int eBreak = TextCharProp.getBreakClass (eData); // if ((eBreak == TextCharProp.BREAK_BK) || (eBreak == TextCharProp.BREAK_CR) || (eBreak == TextCharProp.BREAK_LF) || (eBreak == TextCharProp.BREAK_NL)) { // pbEndsInNewLine = true; // } // } // } // } // } // // return oSplit; // } /** * Truncate underlying displayable stream to a given number of lines. * @param nEndLine - First line not to be included in the result. * @param pStream - Pointer to text stream object to receive the rich * text being removed as a result of the truncation. If NULL, that text * is simply discarded. */ // public void truncateLines (int nEndLine, TextStream pStream) { // FrameLineFinder oFinder (mpoStream, nLine); // if (! oFinder.processFrames()) { // DispLineWrapped poLine = oFinder.getLine(); // TextPosnBase oResult; // poLine.getCaretStartEnd (mpoStream, false, false, oResult); // TextRange oRange (mpoStream, oResult.index()); // // if (poOverflow != null) { // if (mpoStream.attrPool() != null) { // poOverflow.attrPool (mpoStream.attrPool()); // } // if (mpoStream.fontService() != null) { // poOverflow.fontService (mpoStream.fontService()); // } // oRange.text (poOverflow); // } // // TextPosnBase oDeleteStart (oRange.start()); // TextPosnBase oTestPrev (oDeleteStart); // TextItemCode ePrev = oTestPrev.prevUserPosnType(); // // if ((ePrev == TEXT_ITEM_CHAR) && (oTestPrev.nextChar() == '\n')) { // oDeleteStart.prevUserPosn(); // } else if (ePrev == TEXT_ITEM_PARA) { // oDeleteStart.prevUserPosn(); // } // // mpoStream.posnDelete (oDeleteStart, oRange.end().index() - oDeleteStart.index(), true); // } // } /** * Locate split point. *

* Given a vertical split target, this method returns the index of the * first line that is not completely contained within that target. * @param oTarget - Vertical split target. * @return Index of first line that can be split off. */ // public int lineSplit (UnitSpan oTarget) { // TextFrameOffsetFinder oFinder (mpoStream, oTarget); // oFinder.processFrames(); // return oFinder.getAbsLineIndex(); // } /** * Determine whether formatting is suppressed. *

* An application that plans to make a number of changes to a text * stream can turn off formatting (layout) of the display after each * change, and restore it after the last change. This may improve * performance. * @return TRUE if formatting is currently suppressed; FALSE otherwise. */ public boolean suppressFormat () { return mnSuppressFormat > 0; } /** * Push request to suppress formatting. *

* In a complex application, there may be many levels in the call stack * that wish to suppress formatting. This method pushes a suppress * format request onto a stack managed by the text display. */ public void pushSuppressFormat () { mnSuppressFormat++; } /** * Pop request to suppress formatting. *

* In a complex application, there may be many levels in the call stack * that wish to suppress formatting. This method pops a suppress format * request from a stack managed by the text display. When the stack is * empty, formatting is turned back on. */ public void popSuppressFormat () { if (mnSuppressFormat > 0) { if (mnSuppressFormat > 1) { mnSuppressFormat--; } else { clearSuppressFormat(); } } } /** * Clear the suppress formatting request stack. *

* This method pops all outstanding requests off the supress formatting * stack and turns formatting back on. */ public void clearSuppressFormat () { if (mnSuppressFormat == 0) { return; } mnSuppressFormat = 0; layout (true); } /** * Query whether there is any font substitution in this display. *

* This method exists to assist text layout caching. A text display * with font substitution cannot be cached. As the display is built, it * records a Boolean indicating whether any substitution takes place. * Note that an incremental re-layout may clear the condition causing * the font substitution; that will not be reflected in the value of * this Boolean. However, in expected usage, this will not be an issue. * @return TRUE if there has been any font substitution; FALSE if there * is no substitution. */ public boolean hasFontSubstitution () { return mbHasFontSubstitution; } /** * Initialize the font substitution flag. *

* This method allows the caller to pre-populate the font substitution * flag, typically when the caller knows something that AXTE doesn't. * @param bSubstitution - TRUE to indicate that font substitution has * taken place; FALSE to indicate it hasn't. */ public void setFontSubstitution (boolean bSubstitution) { mbHasFontSubstitution = bSubstitution; } public void updateSuspectLayout () { boolean bUpdated = false; for (int i = 0; i < mpoStream.getFrameCount(); i++) { TextFrame poFrame = mpoStream.getFrame (i); if ((poFrame != null) && (poFrame.getLayoutState() == TextFrame.LAYOUT_SUSPECT)) { int nStartIndex = poFrame.getStart().index(); int nEndIndex; int nNextFrame = i + 1; if (nNextFrame >= mpoStream.getFrameCount()) { nEndIndex = mpoStream.posnCount(); } else { TextFrame poNextFrame = mpoStream.getFrame (nNextFrame, true); TextPosnBase oEndPosn = new TextPosnBase (poNextFrame.getStart()); int nLines = poFrame.getLineCount(); if (nLines > 0) { DispLineWrapped poLast = poFrame.getLine (nLines - 1); if (poLast.getLastParaLine() >= DispLine.HARD_NEW_LINE) { oEndPosn.prevUserPosn(); // suppress handling of line break char } } nEndIndex = oEndPosn.index(); } moChange.frame (mpoStream, nStartIndex, nEndIndex - nStartIndex); layout (true, i, true, null); bUpdated = true; } } if (bUpdated) { // DebugFrames(); } } public TextSparseStream stream () { return mpoStream; } // TextGfxConnect textConnect () { // return moConnect; // } // GFXConnect gfxConnect () { // return moConnect.gfxConnect(); // } // void gfxConnect (GFXConnect poNewGfxConnect) { // moConnect.gfxConnect (poNewGfxConnect); // } // void hideEmbed () { // } // boolean pickEmbed (CoordPair oPickPoint, GFXEnv poGfxEnv) { // return false; // } //---------------------------------------------------------------------- // // UpdateSelected: Invalidate the display in a graphic // environment when the selected range changes. // //---------------------------------------------------------------------- // void updateSelected (GFXEnv oGfxEnv, TextRange oOldSel, TextRange oNewSel, boolean bEraseBkgnd) { // UpdateSelected (oOldSel, oNewSel, oGfxEnv, bEraseBkgnd); // } //---------------------------------------------------------------------- // // UpdateSelected: Invalidate the display in a graphic // environment (or all graphic environments) when a selected // range changes. // //---------------------------------------------------------------------- // void updateSelected (TextRange poOldSel, TextRange poNewSel, GFXEnv poGfxEnv, boolean bEraseBkgnd) { // TextStream poOldStream = null; // TextStream poNewStream = null; // // if (poOldSel != null) { // poOldStream = poOldSel.stream(); // } // if (poNewSel != null) { // poNewStream = poNewSel.stream(); // } // // boolean bOldLoose = (poOldStream == null); // boolean bNewLoose = (poNewStream == null); // // if (bOldLoose && bNewLoose) { // return; // } // // TextRange oOldRange; // TextRange oNewRange; // TextRange poOld = poOldSel; // TextRange poNew = poNewSel; // // if (bOldLoose) { // poOld = null; // } else if (bNewLoose) { // poNew = null; // } // // else if (poOldStream == poNewStream) { // int nOldStart = poOldSel.start().index(); // int nOldEnd = poOldSel.end().index(); // int nNewStart = poNewSel.start().index(); // int nNewEnd = poNewSel.end().index(); // // if (nOldStart < nNewStart) { // if (nNewStart < nOldEnd) { // oOldRange.associate (poOldStream, nOldStart, nNewStart); // poOld = oOldRange; // oNewRange.associate (poNewStream, nOldEnd, nNewEnd); // poNew = oNewRange; // } // } else { // (nNewStart <= nOldStart) // if (nOldStart < nNewEnd) { // oNewRange.associate (poNewStream, nNewStart, nOldStart); // poNew = oNewRange; // oOldRange.associate (poOldStream, nNewEnd, nOldEnd); // poOld = oOldRange; // } // } // } // // if (poOld != null) { // InvalidateSel (poOld, poGfxEnv, true); // } // if (poNew != null) { // InvalidateSel (poNew, poGfxEnv, bEraseBkgnd); // } // } //---------------------------------------------------------------------- // // GetSelectionRectangles: Return all rectangles // corresponding to a given range. // //---------------------------------------------------------------------- // boolean getSelectionRectangles (TextRange poRange, List oRectangles) { // TextSelection oSelection; // oSelection = poRange; // oRectangles.setSize (0, false); // // TextFrameSelRect oFinder (mpoStream, oSelection, oRectangles); // oFinder.processFrames(); // return oRectangles.size() > 0; // } // void invalidateArea (Rect oInvalid, boolean bEraseBkgnd, TextFrame poFrame) { // if (poFrame == null) { // moConnect.invalidateArea (oInvalid, bEraseBkgnd); // } else { // moConnect.invalidateContext (poFrame, oInvalid, bEraseBkgnd); // } // } Rect runtimeExtent (boolean bExtended) { // TODO: FrameDispInfo oInfoFinder = new FrameDispInfo (mpoStream, bExtended); oInfoFinder.processFrames(); return oInfoFinder.getExtent(); } public Rect runtimeExtent () { return runtimeExtent (false); } Rect frame0Extent () { if (mpoStream == null) { return null; } TextFrame poFrame = mpoStream.forceFrame (0); if (poFrame == null) { return null; } return poFrame.getExtent(); } void recomputeLineCount () { mnLineCount = 0; int nFrames = mpoStream.getFrameCount(); for (int i = 0; i < nFrames; i++) { TextFrame poFrame = mpoStream.getFrame (i); if (poFrame != null) { mnLineCount += poFrame.getLineCount(); } } } // TextDisplay (TextScroller poNewScroller, GFXEnv poEnv) { // moConnect = poScroller; // Initialize(); // mpoEnv = poEnv; // } // TextDisplay (GFXConnect poNewGfxConnect) { // moConnect = poGfxConnect; // Initialize(); // } void connectStream (TextSparseStream poNewStream, boolean bSuppressLayout, TextAttr poDefaultAttr) { cleanup(); mpoStream = poNewStream; // moConnect.display (this); mpoStream.textDisplaySet (this); if (! bSuppressLayout) { create (poDefaultAttr); } } void connectStream (TextSparseStream poNewStream, boolean bSuppressLayout) { connectStream (poNewStream, false, null); } void connectStream (TextSparseStream poNewStream) { connectStream (poNewStream, false); } //---------------------------------------------------------------------- // // Create: Initialize the display object. // //---------------------------------------------------------------------- void create (TextAttr poDefaultAttr) { moChange.full(); layout (false, 0, false, poDefaultAttr); // updateConnect(); } //---------------------------------------------------------------------- // // Update: Full regeneration of the display. // //---------------------------------------------------------------------- boolean update () { if (mnIgnoreUpdates > 0) { return true; } moChange.full(); return layout (true); } //---------------------------------------------------------------------- // // UpdateInsert (with parameters): Partial update of the // display, given insertion hint. // //---------------------------------------------------------------------- boolean updateInsert (TextStream poStream, int nIndex, int nChange) { if (mnIgnoreUpdates > 0) { return true; } moChange.insert (poStream, nIndex, nChange); return layout (true); } //---------------------------------------------------------------------- // // UpdateDelete (with parameters): Partial update of the // display, given deleteion hint. // //---------------------------------------------------------------------- boolean updateDelete (TextStream poStream, int nIndex, int nChange) { if (mnIgnoreUpdates > 0) { return true; } moChange.delete (poStream, nIndex, nChange); return layout (true); } //---------------------------------------------------------------------- // // UpdateOther (with parameters): Partial update of the // display, given any other hint. // //---------------------------------------------------------------------- boolean updateOther (TextStream poStream, int nIndex, int nCount) { if (mnIgnoreUpdates > 0) { return true; } moChange.other (poStream, nIndex, nCount); return layout (true); } //---------------------------------------------------------------------- // // UpdateToEnd: Stream has changed from this point on. // //---------------------------------------------------------------------- boolean updateToEnd (TextStream poStream, int nIndex) { if (mnIgnoreUpdates > 0) { return true; } moChange.toEnd (poStream, nIndex); return layout (true); } //---------------------------------------------------------------------- // // UpdateJustify: Only justification has changed. // //---------------------------------------------------------------------- void updateJustify () { if (mnIgnoreUpdates > 0) { return; } moChange.setJustify(); layout (true); } void detach (TextStream poStream) { if (poStream != mpoStream) { update(); } poStream.textDisplaySet (null); } void ignoreUpdates (boolean bIgnore) { if (bIgnore) { mnIgnoreUpdates++; } else { assert (mnIgnoreUpdates > 0); mnIgnoreUpdates--; } } //---------------------------------------------------------------------- // // CaretPos: Given a text position, determine the position // and size of the caret. Return FALSE if the position // cannot be located. // //---------------------------------------------------------------------- // boolean caretPos (TextPosnBase oPosn, Rect oCaret, boolean bAllowDangling) { // TextFrameCaretRect oSearch (mpoStream, oPosn, bAllowDangling, false, oCaret); // oSearch.processFrames(); // if (! oSearch.success()) { // return false; // } // oCaret += CoordPair (UnitSpan.ZERO, oSearch.getOffset()); // // return true; // } //---------------------------------------------------------------------- // // CaretPos: given a stream and a coordinate pair, determine // the nearest index in that stream corresponding to the // point. Return FALSE if the stream cannot be located. // // Note: this function handles all cases, including checking // for a poisition in an embedded field or a parent stream // that includes a large embedded field. // //---------------------------------------------------------------------- // boolean caretPos (TextStream poStream, CoordPair oSearchPt, TextPosnBase oResult, boolean bAllowDescendents) { // TextFrameCaretPosn oCaret (mpoStream, poStream, oSearchPt, bAllowDescendents, oResult); // oCaret.processFrames(); // return oCaret.success(); // } //---------------------------------------------------------------------- // // FrameCaretPos: Given a text position, determine the frame // and position and size of the caret relative to that frame. // Return FALSE if the position cannot be located. // //---------------------------------------------------------------------- // boolean frameCaretPos (TextPosnBase oPosn, TextFrame poFrame, Rect oCaret, boolean bAllowDangling) { // poFrame = null; // // TextFrameCaretRect oSearch (mpoStream, oPosn, bAllowDangling, false, oCaret); // oSearch.processFrames(); // if (! oSearch.success()) { // return false; // } // poFrame = oSearch.getFrame(); // // return true; // } //---------------------------------------------------------------------- // // FrameCaretPos: Given a frame, a stream and a coordinate // pair, determine the nearest index in that stream // corresponding to the point, within the given frame Return // FALSE if the stream cannot be located. // // Note: this function handles all cases, including checking // for a poisition in an embedded field or a parent stream // that includes a large embedded field. // //---------------------------------------------------------------------- // boolean frameCaretPos (TextFrame poFrame, TextStream poStream, CoordPair oSearchPt, TextPosnBase oResult, boolean bAllowDescendents) { // TextFrameCaretPosn oCaret (mpoStream, poStream, oSearchPt, bAllowDescendents, oResult); // oCaret.lockFrame (poFrame); // oCaret.processFrames(); // return oCaret.success(); // } //---------------------------------------------------------------------- // // CaretBaseline: Given a text position, return the "caret // baseline point" for that position. See the definition of // jtTextPosnBase for more information. // //---------------------------------------------------------------------- // CoordPair caretBaseline (TextPosnBase oPosn, boolean bAllowDangling) { // Rect oCaret; // TextFrameCaretRect oSearch (mpoStream, oPosn, bAllowDangling, true, oCaret); // oSearch.processFrames(); // if (! oSearch.success()) { // return CoordPair.zeroZero(); // } // //// TBD: doesn't seem to account for frame offset // DispLineWrapped poLine = oSearch.getLine(); // // CoordPair oResult; // oResult.X ((oCaret.left() + oCaret.right()) / 2); // oResult.Y (poLine.getBaselineOffset (true)); // // ABXY.toXY (poLine.getXYOrigin(), poLine.frame().getLayoutOrientation(), oResult); // // return oResult; // } UnitSpan caretUp (TextPosnBase oPosn, UnitSpan poTarget, TextPosnBase oResult) { return vertMove (oPosn, true, poTarget, oResult); } UnitSpan caretDown (TextPosnBase oPosn, UnitSpan poTarget, TextPosnBase oResult) { return vertMove (oPosn, false, poTarget, oResult); } //---------------------------------------------------------------------- // // CaretStartEnd: Move a position's caret to the start or end // of its line. // //---------------------------------------------------------------------- boolean caretStartEnd (TextPosnBase oPosn, boolean bEnd, boolean bVisual, TextPosnBase oResult) { FrameCaretStartEnd oCaret = new FrameCaretStartEnd (mpoStream, oPosn, bEnd, bVisual, oResult); oCaret.processFrames(); return oCaret.success(); } //---------------------------------------------------------------------- // // CaretLeftRight: Move a position's caret to the left or // right. // //---------------------------------------------------------------------- int caretLeftRight (TextPosnBase oPosn, boolean bRight, TextPosnBase oResult) { FindCaretInfo findInfo = findCaretLine (oPosn); if (findInfo == null) { return '\0'; } int nFrameIndex = findInfo.mFrameIndex; int nLineIndex = findInfo.mLineIndex; TextFrame poFrame = mpoStream.getFrame (nFrameIndex); DispLineWrapped poLine = poFrame.getLine (nLineIndex); int pcChar = poLine.getCaretLeftRight (oPosn, bRight, oResult); if (pcChar != '\0') { return pcChar; } TextPosnBase oOtherPosn = new TextPosnBase(); int eOtherCaret = DispLineWrapped.CARET_INVALID; // TBD: do this in a paragraph basis; avoid infinite loop if (bRight == isRTL()) { // left in LTR or right in RTL ... for (; ; ) { if (nLineIndex == 0) { if (nFrameIndex == 0) { break; } nFrameIndex--; poFrame = mpoStream.forceFrame (nFrameIndex); nLineIndex = poFrame.getLineCount(); } if (nLineIndex > 0) { nLineIndex--; poLine = poFrame.getLine (nLineIndex); eOtherCaret = poLine.getCaretStartEnd (oPosn.stream(), true, true, oOtherPosn); if (eOtherCaret != DispLineWrapped.CARET_INVALID) { // Obscure: If the main direction is RTL and the text is true BiDi, // prevent the ambiguous position at the end (left) of the previous line // from mapping to the same ambiguous position in the middle of the line // before that on caret display. if (isRTL()) { oOtherPosn.affinity (TextPosnBase.AFFINITY_AFTER); } pcChar = poLine.getLastGlyphChar(); break; } } } } else { // right in LTR or left in RTL ... pcChar = poLine.getLastGlyphChar(); for (; ; ) { nLineIndex++; if (nLineIndex >= poFrame.getLineCount()) { nFrameIndex++; if (nFrameIndex >= mpoStream.getFrameCount()) { break; } poFrame = mpoStream.forceFrame (nFrameIndex); nLineIndex = 0; } poLine = poFrame.getLine (nLineIndex); eOtherCaret = poLine.getCaretStartEnd (oPosn.stream(), false, true, oOtherPosn); if (eOtherCaret != DispLineWrapped.CARET_INVALID) { break; } } } if (eOtherCaret == DispLineWrapped.CARET_INVALID) { return '\0'; } oResult.copyFrom (oOtherPosn); return pcChar; } void checkAXTELigature (TextPosnBase oPosn, boolean bForward) { FindCaretInfo info = findCaretLine (oPosn); if (info == null) { return; } TextFrame poFrame = mpoStream.getFrame (info.mFrameIndex); poFrame.getLine (info.mLineIndex).checkAXTELigature (oPosn, bForward); } //---------------------------------------------------------------------- // // IsAtStart: Determine whether the position is at the start // of the line. // //---------------------------------------------------------------------- boolean isAtStart (TextPosnBase oPosn, boolean bCheckFirstLineOnly) { FindCaretInfo info = findCaretLine (oPosn); if (info == null) { return false; } if (bCheckFirstLineOnly && ((info.mFrameIndex > 0) || (info.mLineIndex > 0))) { return false; } TextFrame poFrame = mpoStream.getFrame (info.mFrameIndex); return poFrame.getLine (info.mLineIndex).isAtStart (oPosn); } // void scrollTo (TextPosnBase oPosn, GFXEnv oGfxEnv) { // moConnect.scrollTo (oPosn, oGfxEnv); // } //---------------------------------------------------------------------- // // Proprietary, for use by class TextEditor // //---------------------------------------------------------------------- // void onChange (boolean bExtentChanged) { // moConnect.onChange (bExtentChanged); // } //---------------------------------------------------------------------- // // Proprietary, for use by class TextScroller. // //---------------------------------------------------------------------- // void adjustObjects (GFXEnv poEnv, CoordPair oDisplacement) { // for (int i = 0; i < moLines.size(); i++) { // moLines[i].adjustObjects (poEnv, oDisplacement); // } // } // UnitSpan subHeight (int nStart, int nEnd) { // if (nEnd <= nStart) { // return UnitSpan.ZERO; // } // // int nFrames = mpoStream.getFrameCount(); // if (nFrames == 0) { // return UnitSpan.ZERO; // } // // DispLineWrapped poLine; // // FrameLineFinder oFindStart (mpoStream, nStart); // if (oFindStart.processFrames()) { // return UnitSpan.ZERO; // } // poLine = oFindStart.getFrame().getLine (oFindStart.getLineIndex()); // // UnitSpan oStart (oFindStart.getOffset() + poLine.getBMin()); // // FrameLineFinder oFindEnd (mpoStream, nEnd - 1); // UnitSpan oEnd; // if (oFindEnd.processFrames()) { // oEnd = oFindEnd.getOffset(); // ran off end // } else { // poLine = oFindEnd.getFrame().getLine (oFindEnd.getLineIndex()); // oEnd = oFindEnd.getOffset() + poLine.getBMax(); // } // // if (mpoEnv != null) { // oStart = mpoEnv.unitH (mpoEnv.devH (oStart)); // oEnd = mpoEnv.unitH (mpoEnv.devH (oEnd)); // } // // return oEnd - oStart; // } // UnitSpan lineMinY (int nIndex) { // FrameLineFinder oFinder (mpoStream, nIndex); // if (oFinder.processFrames()) { // return UnitSpan.ZERO; // ran off the end // } // DispLineWrapped poLine = oFinder.getFrame().getLine (oFinder.getLineIndex()); // return oFinder.getOffset() + poLine.getBMin(); // } TextContext getContext () { return (mpoStream == null) ? null : mpoStream.forceContext(); } GFXEnv getGFXEnv () { return mpoEnv; } // GFXEnv getGfxEnv () { // return mpoEnv; // } DispChange getChange () { return moChange; } // TextFontMap getFontMap () { // return getContext().getFontMap(); // } DispMapSet getDisposableMaps () { return getContext().getDisposableMaps(); } public void releaseDisposableMaps (DispMapSet poMaps) { getContext().releaseDisposableMaps (poMaps); } // AXTEWRSBase getWRS () { // return getContext().getWRS(); // } // TextGlyphArray getGlyphArray () { // return getContext().getGlyphArray(); // } void setLocale (TextAttr poAttr) { int eDirection = TextAttr.DIRECTION_NEUTRAL; String sLocale = ""; if (poAttr != null) { eDirection = poAttr.paraDirection(); if (eDirection == TextAttr.DIRECTION_NEUTRAL) { eDirection = poAttr.direction(); } sLocale = poAttr.actualLocale(); } moLocaleInfo = getContext().lookupLocale (sLocale); moLocaleInfo = new LocaleInfo (moLocaleInfo); // TODO: could this be cached? if (mpoStream.defaultDirection() != TextAttr.DIRECTION_NEUTRAL) { eDirection = mpoStream.defaultDirection(); } switch (eDirection) { case TextAttr.DIRECTION_LTR: moLocaleInfo.mbIsRTL = false; break; case TextAttr.DIRECTION_RTL: moLocaleInfo.mbIsRTL = true; break; } } LocaleInfo getLocale (String sLocaleName) { return getContext().lookupLocale (sLocaleName); } TextBreakFinder getBreakFinder () { mpoBreakFinder = TextBreakFinder.recycle (moLocaleInfo.mpoLocale, mpoBreakFinder); return mpoBreakFinder; } int getLayoutOrientation () { return meLayoutOrientation; } boolean isRTL (TextAttr poAttr) { boolean bRTL = moLocaleInfo.mbIsRTL; if (poAttr != null) { int eDirection = poAttr.direction(); if (eDirection == TextAttr.DIRECTION_LTR) { bRTL = false; } else if (eDirection == TextAttr.DIRECTION_RTL) { bRTL = true; } else { LocaleInfo oLocaleInfo = getContext().lookupLocale (poAttr.actualLocale()); bRTL = oLocaleInfo.mbIsRTL; } } return bRTL; } boolean isRTL () { return isRTL (null); } boolean isIdeographic (TextAttr poAttr) { boolean bIdeographic = moLocaleInfo.mbIsIdeographic; if (poAttr != null) { LocaleInfo oLocaleInfo = getContext().lookupLocale (poAttr.actualLocale()); bIdeographic = oLocaleInfo.mbIsIdeographic; } return bIdeographic; } boolean isIdeographic () { return isIdeographic (null); } // boolean optycaJustify (TextAttr poAttr) { // boolean bOptycaJustify = moLocaleInfo.mbOptycaJustify; // // if (poAttr != null) { // LocaleInfo oLocaleInfo = GetContext().lookupLocale (poAttr.actualLocale()); // bOptycaJustify = oLocaleInfo.mbOptycaJustify; // } // // return bOptycaJustify; // } int getDigits (TextAttr poAttr) { if ((poAttr == null) || (poAttr.digits() == TextAttr.DIGITS_LOCALE)) { return moLocaleInfo.meDigits; } else { return poAttr.digits(); } } boolean[] getBreakCandidates (int nCount, int nPreserve) { return getContext().getBreakCandidates (nCount, nPreserve); } boolean[] getBreakCandidates (int nCount) { return getContext().getBreakCandidates (nCount, 0); } int getLegacyLevel () { return mpoStream.getLegacyLevel(); } //---------------------------------------------------------------------- // // FindCaretLine: given a stream and an index in that stream, // determine the position and size of the caret, and the // index of the line containing the caret. Return FALSE if // the position cannot be located. // //---------------------------------------------------------------------- FindCaretInfo findCaretLine (TextPosnBase oPosn) { FrameCaretRect oSearch = new FrameCaretRect (mpoStream, oPosn, false, true); oSearch.processFrames(); if (! oSearch.success()) { return null; } return new FindCaretInfo (oSearch.getFrameIndex(), oSearch.getLineIndex(), oSearch.getCaret()); } //---------------------------------------------------------------------- // // Layout: Format the text, building the completed line list. // // This code handles both the full format and the case when // only a partial reformat is required. There is a // substantial cost in reformatting a large, multi-line text // block, so we try to avoid it on small changes. // // There are two phases to the format operation: layout, // justification and display invalidation. Because much of // the layout happens in the various TextDispLine... // classes, most of the code here is for justification and // invalidation. // //---------------------------------------------------------------------- private boolean layout (boolean bUpdate, int nFrameIndex, boolean bSuppressSuspectFrames, TextAttr poDefaultAttr) { int i; // Do not do anything if formatting is disabled. if ((mnSuppressFormat > 0) || (mnIgnoreUpdates > 0)) { return true; } // Treat initial call as full format even if caller didn't request it. // TODO: is this check necessary? // if (lines() == 0) { // bUpdate = false; // moChange.full(); // } // Horizontally growing object: we currently cannot optimize, as a size // change may influence the layout of unchanged lines. TBD: this is far // too restrictive; most cases could be treated like fixed-size blocks. // Need to investigate. if (mpoStream.getFrameCount() > 0) { TextFrame poFrame = mpoStream.getFrame (0); if ((poFrame != null) && (! poFrame.alignHPoint()) && (poFrame.minWidth() != poFrame.maxWidth())) { moChange.full(); } } // Stream/attr initializations. TextPosnBase oStartStream = new TextPosnBase (mpoStream); TextAttr poAttr = oStartStream.attributePtr(); int eJustH = TextAttr.JUST_H_LEFT; int eJustV = TextAttr.JUST_V_TOP; meLayoutOrientation = TextAttr.ORIENTATION_HORIZONTAL; if (poAttr != null) { eJustH = poAttr.justifyH(); eJustV = poAttr.justifyV(); if (poAttr.layoutOrientationEnable()) { meLayoutOrientation = poAttr.layoutOrientation(); } if (poAttr.fontEnable() && poAttr.substituteFont()) { setFontSubstitution (true); } } setLocale (poAttr); // Create important objects for the formatting operation. FormatInfo oInfo = new FormatInfo (this, moTabs, bUpdate, eJustH, eJustV, nFrameIndex, poDefaultAttr); // Clear the last line's last line flag because we may add new lines. if (oInfo.isUpdate() && (oInfo.getChange().type() != DispChange.CHANGE_NONE)) { mpoStream.updateLastLineFlag (false); } // The following if-block does the (possibly optimized) layout. Skip it // if this is just a justification update. // and not all loaded layouts? // The following loop does the actual layout of the lines, all or changed // only. Each iteration creates one raw line, which in turn creates all // the wrapped lines that correspond to it. if ((oInfo.getChange().type() != DispChange.CHANGE_NONE) && (! oInfo.allInitialLayout())) { // not justify only? boolean bNewPara = oInfo.isNewPara(); boolean bContinue = true; do { DispLineRaw oRawLine = new DispLineRaw (oInfo, bNewPara); bContinue = oRawLine.fill(); bNewPara = oRawLine.isLastParaLine(); oRawLine.detach(); } while (bContinue); } // Restore the last line's last line flag. if (oInfo.getChange().type() != DispChange.CHANGE_NONE) { mpoStream.updateLastLineFlag (true); } oInfo.finish(); // Count the number of lines by iterating through the frames. If this is // a justify-only operation, justify each frame. mnLineCount = 0; int nFrames = mpoStream.getFrameCount(); for (i = 0; i < nFrames; i++) { TextFrame poFrame = mpoStream.getFrame (i); if (poFrame != null) { if (moChange.type() == DispChange.CHANGE_NONE) { // poFrame.justify (oInfo); // TODO: } mnLineCount += poFrame.getLineCount(); } } if (oInfo.updateConnect()) { // UpdateConnect(); } // DebugFrames(); moChange.reset(); if (! bSuppressSuspectFrames) { // UpdateSuspectLayout(); } return oInfo.fits(); } private boolean layout (boolean bUpdate) { return layout (bUpdate, 0, true, null); } //---------------------------------------------------------------------- // // UpdateConnect: Update our connect object (notably for // scrolling information). // //---------------------------------------------------------------------- // void updateConnect () { //// TBD: does this need to be extended to multi-frame environment? // TextFrame poFrame = mpoStream.forceFrame (0); // moConnect.onUpdate (poFrame.getLineCount(), poFrame.getExtent()); // } //---------------------------------------------------------------------- // // VertMove: Perform vertical move, up or down. // //---------------------------------------------------------------------- UnitSpan vertMove (TextPosnBase oPosn, boolean bUp, UnitSpan poTarget, TextPosnBase oResult) { // First, find the line that contains this position's index. FindCaretInfo info = findCaretLine (oPosn); if (info == null) { return null; } Rect oCaret = info.mCaret; int nFrameIndex = info.mFrameIndex; int nLineIndex = info.mLineIndex; // The position has a horizontal "target" that we try to match as we move // up or down, so that moving through a short line doesn't cause the // horizontal caret position to change. If the target hasn't been set, // set at as the horizontal midpoint of the caret rectangle. Note that // the caller's target pointer is passed by reference, but we cache our // own pointer on the stack. This is because if we set the caller's // target too soon, it may get cleared as a result of calls we make here // (notably forcing frames to load). if (poTarget == null) { poTarget = oCaret.left().add (oCaret.right()); poTarget = poTarget.divide (2); } TextFrame poFrame = mpoStream.getFrame (nFrameIndex); boolean bFound = false; if (bUp) { // If moving up, iterate upward from the found line in the found frame. // If all previous lines in the frame are exhausted, go to the previous // frame. while (! bFound) { if (nLineIndex == 0) { if (nFrameIndex == 0) { break; } nFrameIndex--; poFrame = mpoStream.forceFrame (nFrameIndex); nLineIndex = poFrame.getLineCount(); } if (nLineIndex > 0) { nLineIndex--; DispLineWrapped poLine = poFrame.getLine (nLineIndex); if (poLine.getCaretPosn (oPosn.stream(), poTarget, oResult) != DispLine.CARET_INVALID) { bFound = true; } } } } else { // If moving down, iterate downward from the found line in the found // frame. If all subsequent lines in the frame are exhausted, go to the // next frame. nLineIndex++; while (! bFound) { if (nLineIndex >= poFrame.getLineCount()) { nFrameIndex++; if (nFrameIndex >= mpoStream.getFrameCount()) { break; } poFrame = mpoStream.forceFrame (nFrameIndex); nLineIndex = 0; } DispLineWrapped poLine = poFrame.getLine (nLineIndex); if (poLine.getCaretPosn (oPosn.stream(), poTarget, oResult) != DispLine.CARET_INVALID) { bFound = true; } nLineIndex++; } } return poTarget; } void detachStream (TextStream poStream) { if (poStream == null) { return; } poStream.textDisplaySet (null); Storage oFields = new Storage(); poStream.enumField (oFields); for (int i = 0; i < oFields.size(); i++) { detachStream (oFields.get(i)); } } //---------------------------------------------------------------------- // // InvalidateSel: Invalidate the display by a given range. // //---------------------------------------------------------------------- // void invalidateSel (TextRange oRange, GFXEnv poGfxEnv, boolean bEraseBkgnd) { // TextPosn oStart = oRange.start(); // TextPosn oEnd = oRange.end(); // // if (oStart.index() == oEnd.index()) { // return; // } // // int nFrame; // int nLine; // CoordPair oOffset; // if (! FindCaretLine (oStart, nFrame, nLine, null, oOffset)) { // return; // } // // int nFrameEnd; // int nLineEnd; // if (! FindCaretLine (oEnd, nFrameEnd, nLineEnd)) { // return; // } // // TextFrame poFrame = mpoStream.getFrame (nFrame); // boolean bIsFirstLine = true; // // for (; ; ) { // if (poFrame != null) { // DispLineWrapped poLine = poFrame.getLine (nLine); // // Rect oInvalid; // boolean bAnyInvalid; // // boolean bIsLastLine = (nLine == nLineEnd) && (nFrame == nFrameEnd); // if ((! bIsFirstLine) && (! bIsLastLine)) { // oInvalid.leftRight (poLine.getAMin(), poLine.getAMax (true)); // bAnyInvalid = true; // } else { // UnitSpan oLeft; // UnitSpan oRight; // bAnyInvalid = poLine.getInvalidationRect (oStart, oEnd, oLeft, oRight); // oInvalid.leftRight (oLeft, oRight); // } // // oInvalid.topBottom (poLine.getBMinExtended (true), poLine.getBMaxExtended (true)); // ABXY.toXY (poLine.getXYOrigin(), poLine.frame().getLayoutOrientation(), oInvalid); // oInvalid += oOffset; // moConnect.invalidateArea (poGfxEnv, oInvalid, bEraseBkgnd); // // if (bIsLastLine) { // break; // } // } // // nLine++; // if ((poFrame == null) || (nLine >= poFrame.getLineCount())) { // nFrame++; // if (nFrame >= mpoStream.getFrameCount()) { // break; // } // poFrame = mpoStream.getFrame (nFrame); // if (poFrame != null) { // oOffset.Y (oOffset.Y() + poFrame.getBMax()); // } // nLine = 0; // } // } // // UpdateConnect(); // } void cleanup () { detachStream (mpoStream); mpoStream = null; // mpoEnv = null; mnSuppressFormat = 0; mnIgnoreUpdates = 0; mpoBreakFinder = null; moTabs.setSize (0); mnLineCount = 0; mbHasFontSubstitution = false; } // void debugFrames () { // if (AXTEFp != null) { // int nFrames = mpoStream.getFrameCount(); // for (int i = 0; i < nFrames; i++) { // fprintf (AXTEFp, "Frame %d:\\\\n", i); // TextFrame poFrame = mpoStream.getFrame (i); // if (poFrame != null) { // poFrame.debugLines(); // } // } // } // } private static Rect computeClipRect (TextSparseStream poStream) { return computeClipRect (poStream, TextDrawInfo.INVALID_DEFAULT_DECLARED_SIZE); } private static Rect computeClipRect (TextSparseStream poStream, int eInvalidDefault) { Rect oExtent; if ((eInvalidDefault == TextDrawInfo.INVALID_DEFAULT_RUNTIME_EXTENT) && (poStream.display() != null)) { oExtent = poStream.display().runtimeExtent(); } else { // A bit of a hack. TextFrame poFrame = poStream.forceFrame (0); oExtent = poFrame.getExtent(); if (eInvalidDefault == TextDrawInfo.INVALID_DEFAULT_DECLARED_SIZE) { UnitSpan oMaxWidth = poFrame.maxWidth(); if (oMaxWidth.value() >= 0) { // oMaxWidth value of 0 is valid , Watson bug 1171970 oExtent = oExtent.leftRight (UnitSpan.ZERO, oMaxWidth); } UnitSpan oMaxHeight = poFrame.maxHeight(); if (oMaxHeight.value() > 0) { oExtent = oExtent.topBottom (UnitSpan.ZERO, oMaxHeight); } } } return oExtent; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy