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

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

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

import java.util.List;
import com.adobe.xfa.ut.UnitSpan;

/**
 * 

* Class TextPosnBase represents a position in a particular text * stream. *

*

* Positions occur between items (typically characters) in the stream, * as well as before the first item and after the last. A position has * an index number; zero corresponds to the position at the start of the * stream and values increment from there. Thus, the last position * index is equal to the total number of items in the stream. *

*

* Technically there are actually two positions between each pair of * items. Though they share the same index number, one comes before the * other. These are referred to as a before position and an * after position. When an insertion happens between the two * characters, the before position does not advance while the after one * does. The before and after distinction is largely an internal * feature of AXTE. *

*

* A text position also has an affinity to one of the two surrounding * items. Affinity is independent from the notion of before/after. For * example a before position might have an affinity to the character * that comes after. Affinity is used only in resolving ambiguous * positions in bi-directional text. It is normally used only within * AXTE. The default affinity is to the character that comes after the * position. *

*

* With the addition of support for bidirectional text, the text * position object now carries a directionality flag. The RTL (right to * left) flag is true if the position object was populated in an * operation that processed RTL text. This flag is normally not used by * application-level software. Typically only AXTE uses it, to * "remember" state for ambiguous positions. *

*

* An instance of this class points into an instance of a text stream, * but that stream has no knowledge of the TextPosnBase objects * looking at it. Modifying the text stream may cause some or all of * those objects to become invalid. Typically one uses class * TextPosnBase for short-term stream manipulation, and derived class * TextPosition for longer-term access to the text stream. *

*

* This class supports a number of text unit movement operations, for * example next word or previous character. The concepts of next and * previous can be interpred in either a logical or a visual sense. * Logical moves apply to the underlying text stream data. Any Next * operation increases the position's stream index, while any Previous * operation decreases it. For visual moves, Next implies to the right * and Previous to the left. In right-to-left text, left is synonomous * with decreasing index and right with increasing index. In * right-to-left text, left corresoponds to increasing index and right * corresponds to decreasing index. The caller choses logical or visual * mode in the parameter list of movement operations. For visual moves * to be effective, there must be a text display associated with the * stream. *

*

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

* * @exclude from published api -- Mike Tardif, May 2006. */ public class TextPosnBase extends TextMarkupBase { /** * Position type enumeration. *

* As mentioned in the class description, each position object can be * designated as a before position or an after position. * These enumeration values are used with methods that manipulate the * position type. *

*
    *
  • * POSN_AFTER: Designates an "after" position. Insertions at this * position will cause the position to move, so that it stays after the * insertions. *
  • *
  • * POSN_BEFORE: Designates a "before" position. Insertions at this * position will not change position, so that it remains before the * insertions. *
  • *
*/ public final static int POSN_AFTER = 0; public final static int POSN_BEFORE = 1; /** * Affinity enumeration. *

* As mentioned in the class description, each position object has an * affinity to either the character that comes before in logical order * or the one that comes after. The affinity is used to determine where * to place the cursor when a direction change at the position causes * the glyphs for the surrounding characters to be disjoint. *

*
    *
  • * AFFINITY_AFTER: The position has an affinity to the character that * comes after. *
  • *
  • * AFFINITY_BEFORE: The position has an affinity to the character that * comes before. *
  • *
*/ public final static int AFFINITY_AFTER = 0; public final static int AFFINITY_BEFORE = 1; /** * Word positioning modes. *

* AXTE was originally developed with a relatively simple sense of word * boundaries. Essentially these occured only between space and * non-space characters. With the advent of language-related features * such as spelling and hyphenation, more sophisticated word analysis * became necessary. *

*

* This enumeration has values that can be used in word-oriented methods * of a number of AXTE classes to specify the type of word handling * required. *

*
    *
  • * WORD_MODE_LEGACY: Legacy word handling; word breaks occur between * space and non-space characters only. This is the current API * default, in order to ensure backward compatibility. *
  • *
  • * WORD_MODE_ALGORITHMIC: Apply the Unicode UAX29 algorithm to * distinguish more accurate word boundaries, notably between letters * and punctuation. *
  • *
  • * WORD_MODE_LOCALE_SENSITIVE: Use the locale for further refinement of * word processing algorithms. This would be useful in a language like * Thai, where a dictionary is required to find word breaks. Note: this * setting is not yet implemented, and behaviour reverts to * WORD_MODE_ALGORITHMIC if it is selected. *
  • *
*/ public static final int WORD_MODE_LEGACY = 0; public static final int WORD_MODE_ALGORITHMIC = 1; public static final int WORD_MODE_LOCALE_SENSITIVE = 2; private static final int WHITE_SPACE_SPACE = 0; private static final int WHITE_SPACE_TEXT = 1; private static final int WHITE_SPACE_BREAK = 2; private static final int WHITE_SPACE_NOT_FOUND = 3; // states for finding (start of) next word -- legacy private static final int NW_START = 0; // initial state private static final int NW_START_BREAK = 1; // first thing seen is a break private static final int NW_START_WORD = 2; // first thing seen is text private static final int NW_SPACES = 3; // into spaces before next word private static final int NW_DONE = 4; private static final int PW_START = 0; // initial state private static final int PW_START_SPACES = 1; // first thing seen is spaces private static final int PW_WORD = 2; // in word (for which we'll find the start) private static final int PW_BREAK = 3; // just saw a break private static final int PW_DONE = 4; private static final int WB_SPACE = 0; private static final int WB_LETTER = 1; private static final int WB_OTHER = 2; // These are package visible for class TextStream. TextStream mpoStream; // Associated text stream int mnMajor; // Major indicates which item in the stream int mnMinor; // Minor indicates a sub-position within // a major item. e.g. a character position // in a text string int mnIndex; // Current index number (cumulative minor positions) boolean mbIsBeforePosition; // Is this a "before" position? boolean mbIsPrevAffinity; // Affinity to previous character? boolean mbIsRTL; // Populated by RTL operation? boolean mbIsMarkerPosition; // Is this a marker position? boolean mbIsInvalid; // Has this position been invalidated? boolean mbTightenPending; // Tighten on next validate? UnitSpan mpoTarget; // Target offset in display private static final int[][] geLegacyNextWordState = { /* WHITE_SPACE_SPACE WHITE_SPACE_TEXT WHITE_SPACE_BREAK */ /* NW_START */ {NW_SPACES, NW_START_WORD, NW_START_BREAK }, /* NW_START_BREAK */ {NW_SPACES, NW_DONE, NW_DONE }, /* NW_START_WORD */ {NW_SPACES, NW_START_WORD, NW_DONE }, /* NW_SPACES */ {NW_SPACES, NW_DONE, NW_DONE } }; private static final int[][] geLegacyPrevWordState = { /* WHITE_SPACE_SPACE WHITE_SPACE_TEXT WHITE_SPACE_BREAK */ /* PW_START */ {PW_START_SPACES, PW_WORD, PW_BREAK }, /* PW_START_SPACES */ {PW_START_SPACES, PW_WORD, PW_BREAK }, /* PW_WORD */ {PW_DONE, PW_WORD, PW_DONE } }; private static final boolean[][] gbWordBreak = { /* ---Next--- WB_SPACE WB_LETTER WB_OTHER */ /* WB_SPACE */ { false, true, true }, /* WB_LETTER */ { false, false, true }, /* WB_OTHER */ { false, true, false }, /* ---Prev--- WB_SPACE WB_LETTER WB_OTHER */ /* WB_SPACE */ { false, false, false }, /* WB_LETTER */ { true, false, true }, /* WB_OTHER */ { true, true, false } }; /** * Default constructor. *

* The position is not initially associated with any stream. */ public TextPosnBase () { initialize(); } /** * Copy constructor. *

* Copy all contents of the source position, including stream * association, index and before/after state. * @param oSource Source position object to copy. */ public TextPosnBase (TextPosnBase oSource) { initialize(); copy (oSource); } /** * Constructor with stream and optional position type. * Construct a position object associated with the given stream and * optional position type. The position is placed before the first * non-attribute item in the stream. * @param poNewStream - Stream to associate with. NULL creates a * position object with no initial association. * @param eNewPosn - (optional) Position type to use for the object. * Default is POSN_AFTER. */ // public TextPosnBase (TextStream poNewStream, int eNewPosn) { // if (InitWithStream (poNewStream, eNewPosn)) { // mpoStream.PosnFirst (this); // } // } /** * Constructor with stream, index and optional position type. *

* Construct a position object associated with the given stream and * optional position type. The position is placed at the specified * index. * @param poNewStream Stream to associate with. NULL creates a * position object with no initial association. * @param nNewIndex Index number for the position. Will be truncated if too * large. * @param eNewPosn (optional) Position type to use for the object. * Default is POSN_AFTER. */ public TextPosnBase (TextStream poNewStream, int nNewIndex, int eNewPosn) { if (initWithStream (poNewStream, eNewPosn)) { mpoStream.posnUpdateStreamLoc (this, nNewIndex); } } public TextPosnBase (TextStream poNewStream, int nNewIndex) { if (initWithStream (poNewStream, POSN_AFTER)) { mpoStream.posnUpdateStreamLoc (this, nNewIndex); } } public TextPosnBase (TextStream poNewStream) { if (initWithStream (poNewStream, POSN_AFTER)) { mpoStream.posnFirst (this); } } /** * Overridable: Associate position with a new stream * This method associates the position object with the specified stream, * immediately before the first non-attribute item. * @param poNewStream - Stream to associate with. NULL leaves the * position object unassociated (and untracked). * @param eNewPosn - (optional) Position type to use for the object. * Default is POSN_AFTER. */ // public void Associate (TextStream poNewStream, int eNewPosn) { // CleanupTarget(); // if (InitWithStream (poNewStream, eNewPosn)) { // mpoStream.PosnFirst (this); // } // } /** * Overridable: Associate position with a new stream and index. *

* This method associates the position object with the specified stream, * at the specified index position. * @param poNewStream Stream to associate with. NULL leaves the * position object unassociated (and untracked). * @param nNewIndex Index of this position in the new stream. If the * value is too large, it is truncated. * @param eNewPosn (optional) Position type to use for the object. * Default is POSN_AFTER. */ public void associate (TextStream poNewStream, int nNewIndex, int eNewPosn) { // call private member in Java to avoid calling inherited methods in derived class doAssociate (poNewStream, nNewIndex, eNewPosn); } public void associate (TextStream poNewStream, int nNewIndex) { doAssociate (poNewStream, nNewIndex, POSN_AFTER); } public void associate (TextStream poNewStream) { cleanupTarget(); if (initWithStream (poNewStream, POSN_AFTER)) { mpoStream.posnFirst (this); } } /** * Return the index number of this position within its stream. * @return This position's index in its associated stream. */ public int index () { return mnIndex; } /** * Change the position's index number. * Move the position to a new index number. The position remains * associated with the same stream. * @param nNewIndex - New index within the associated stream. If the * value is too large, it is truncated. */ public void index (int nNewIndex) { if ((mpoStream != null) && (nNewIndex != mnIndex)) { mpoStream.posnUpdateStreamLoc (this, nNewIndex); } } /** * Obtain the position type (before or after) of this position. * @return POSN_BEFORE if this is a before position; POSN_AFTER if this * is an after position. */ public int position () { return mbIsBeforePosition ? POSN_BEFORE : POSN_AFTER; } /** * Change the position type (before/after) of this position. * @param ePosition POSN_BEFORE changes to a before position; * POSN_AFTER changes to an after position. */ public void position (int ePosition) { mbIsBeforePosition = ePosition == POSN_BEFORE; } /** * Obtain the affinity of this position. * @return AFFINITY_BEFORE if this position associates with the item * before; AFFINITY_AFTER if it associates with the one after. */ public int affinity () { return mbIsPrevAffinity ? AFFINITY_BEFORE : AFFINITY_AFTER; } /** * Change the position type (before/after) of this position. * @param eAffinity AFFINITY_BEFORE if this position is to associate * with the item before; AFFINITY_AFTER to associate with the one after. */ public void affinity (int eAffinity) { mbIsPrevAffinity = eAffinity == AFFINITY_BEFORE; } /** * Query whether the position was populated as a result of processing * RTL text. *

* This method exists primarily for internal AXTE use and tends to have * a meaningful value only in interactive applications. * @return TRUE if the position was populated as a result of processing * RTL text; FALSE otherwise. */ public boolean isRTL () { return mbIsRTL; } /** * Change the position object's RTL flag. *

* This method exists primarily for internal AXTE use. The RTL flag * tends to have a meaningful value only in interactive applications. * @param bRTL TRUE if the position was populated as a result of * processing RTL text; FALSE otherwise. */ public void setRTL (boolean bRTL) { mbIsRTL = bRTL; } /** * Enumerate markers at this position. * @param oMarkers - Array to contain pointers to the enumerated * markers. Any previous contents are removed by the call. Even though * the returned array contains non-const pointers to markers, those * markers are owned by AXTE and must not be deleted by the client, * though the client can cache pointers to these markers provide that it * participates in the marker reference counting mechanism. * @param bPositionMarkers - True (default) if all position markers at * this position are to be included in the array. False if position * markers are to be excluded. * @param bRangeMarkers - True if all range markers that span this * position are to be included in the array. False (default) if range * markers are to be excluded. A range marker spans the position if it * starts before or at the position, and ends at or after the position. */ public void enumerateMarkers (List oMarkers, boolean bPositionMarkers, boolean bRangeMarkers) { if (mpoStream != null) { mpoStream.rangeEnumMarker (this, this, oMarkers, bPositionMarkers, bRangeMarkers, false); } } /** * Move to the first position in the associated stream. * Note: the first position is not the same as position zero. This * method positions before the first non-attribute item in the stream. * In an interactive application we do not want the user inserting text * before their attributes. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. */ public void first (boolean bVisual) { if (mpoStream != null) { mpoStream.posnFirst (this); } if (bVisual) { start (true); } } public void first () { first (false); } /** * Move to the last position in the associated stream. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. */ public void last (boolean bVisual) { if (mpoStream != null) { mpoStream.posnUpdateStreamLoc (this, mpoStream.posnCount()); } if (bVisual) { end (true); } } public void last () { last (false); } /** * Move to the next position in the associated stream. * This method advances the position in the stream. In doing so, the * position passes over a single item, whose type is returned. * @param bTestOnly - (optional) If TRUE, the position simply returns * the next item type and does not actually move. If FALSE (default), * the position does move. * @return Item type of the item passed over. Returns * TextItem.UNKNOWN if at the last position. */ public int next (boolean bTestOnly) { return next (TextNullFrame.MODE_LOAD, bTestOnly); } public int next (int eNullFrameMode) { return next (eNullFrameMode, false); } public int next () { return next (false); } public int next (int eNullFrameMode, boolean bTestOnly) { int[] result = nextData (eNullFrameMode, bTestOnly); if (result == null) { return TextItem.UNKNOWN; } return result[0]; } public int[] nextData (boolean bTestOnly) { return nextData (TextNullFrame.MODE_LOAD, bTestOnly); } /** * Move to the previous position in the associated stream. * This method backs up the position in the stream. In doing so, the * position passes over a single item, whose type is returned. * @param bTestOnly - (optional) If TRUE, the position simply returns * the previous item type and does not actually move. If FALSE * (default), the position does move. * @return Item type of the item passed over. Returns * TextItem.UNKNOWN if at position zero. */ public int prev (boolean bTestOnly) { return prev (TextNullFrame.MODE_LOAD, bTestOnly); } public int prev (int eNullFrameMode) { return prev (eNullFrameMode, false); } public int prev () { return prev (false); } public int prev (int eNullFrameMode, boolean bTestOnly) { int[] result = prevData (eNullFrameMode, bTestOnly); if (result == null) { return TextItem.UNKNOWN; } return result[0]; } public int[] prevData (boolean bTestOnly) { return prevData (TextNullFrame.MODE_LOAD, bTestOnly); } /** * Obtain the next character. * Scan forward from the current position until a character (or end of * stream) is encountered. Return the character value. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * advanced so that it falls immediately after the character returned. * @return Character value. Null character if no characters after the * position. */ public int nextChar (boolean bTestOnly) { if (mpoStream == null) { return '\0'; } return mpoStream.posnNextChar (this, bTestOnly); } public int nextChar () { return nextChar (false); } /** * Obtain previous next character. * Scan backward from the current position until a character (or start * of stream) is encountered. Return the character value. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * backed up so that it falls immediately before the character returned. * @return Character value. Null character if no characters before the * position. */ public int prevChar (boolean bTestOnly) { if (mpoStream == null) { return '\0'; } return mpoStream.posnPrevChar (this, bTestOnly); } public int prevChar () { return nextChar (false); } /** * Obtain the next embedded field object. * Scan forward from the current position until an embedded field object * (or end of stream) is encountered. Return the embedded field * pointer. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * advanced so that it falls immediately after the embedded field * object. * @return Embedded field pointer. NULL if no embedded fields after the * position. */ public TextField nextField (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnNextField (this, bTestOnly); } public TextField nextField () { return nextField (false); } /** * Obtain previous next embedded field object. * Scan backward from the current position until an embedded field * object (or start of stream) is encountered. Return the embedded * field pointer. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * backed up so that it falls immediately before the embedded field * object. * @return Embedded field pointer. NULL if no embedded fields before * the position. */ public TextField prevField (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnPrevField (this, bTestOnly); } public TextField prevField () { return prevField (false); } /** * Obtain the next embedded object. * Scan forward from the current position until an embedded object (or * end of stream) is encountered. Return the embedded object pointer. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * advanced so that it falls immediately after the embedded object. * @return Embedded object pointer. NULL if no embedded objects after * the position. */ public TextEmbed nextEmbed (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnNextEmbed (this, bTestOnly); } public TextEmbed nextEmbed () { return nextEmbed (false); } /** * Obtain previous next embedded object. * Scan backward from the current position until an embedded object (or * start of stream) is encountered. Return the embedded object pointer. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * backed up so that it falls immediately before the embedded object. * @return Embedded object pointer. NULL if no embedded objects before * the position. */ public TextEmbed prevEmbed (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnPrevEmbed (this, bTestOnly); } public TextEmbed prevEmbed () { return prevEmbed (false); } /** * Obtain the next attribute change. * Scan forward from the current position until an attribute change (or * end of stream) is encountered. Return the attribute object. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * advanced so that it falls immediately after the attribute object * returned. * @return attribute object pointer. NULL if no attribute changes after * the position. */ public TextAttr nextAttr (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnNextAttr (this, bTestOnly); } public TextAttr nextAttr () { return nextAttr (false); } /** * Obtain the previous attribute change. * Scan backward from the current position until an attribute change (or * start of stream) is encountered. Return the attribute object. * @param bTestOnly - (optional) If TRUE, this position remains * unchanged after the call. If FALSE (default), the position is * backed up so that it falls immediately before the attribute object * returned. * @return attribute object pointer. NULL if no attribute changes * before the position. */ public TextAttr prevAttr (boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnPrevAttr (this, bTestOnly); } public TextAttr prevAttr () { return prevAttr (false); } /** * Advance one user position. * A user position is described as one where a caret may be placed in an * interactive application. Attribute items are transparent to user * positions. This method advances by one user position. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return TRUE if the object moved to a new user position; FALSE if it * was already at the last user position in the stream or there is no * displey. */ public boolean nextUserPosn (boolean bVisual) { int[] result = nextUserPosnTypeData (bVisual); return (result != null) && (result[0] != TextItem.UNKNOWN); } public boolean nextUserPosn () { return nextUserPosn (false); } /** * Back up one user position. * A user position is described as one where a caret may be placed in an * interactive application. Attribute items are transparent to user * positions. This method backs up to the previous user position. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return TRUE if the object moved to a new user position; FALSE if it * was already at the first user position in the stream or there is no * display. */ public boolean prevUserPosn (boolean bVisual) { int[] result = prevUserPosnTypeData (bVisual); return (result != null) && (result[0] != TextItem.UNKNOWN); } public boolean prevUserPosn () { return prevUserPosn (false); } /** * Advance one user position; return item type. * A user position is described as one where a caret may be placed in an * interactive application. Attribute items are transparent to user * positions. This method advances by one user position and returns the * (user) item type stepped over. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return Item type if the object moved to a new user position; * TextItem.UNKNOWN if it was already at the last user position in * the stream. */ public int nextUserPosnType (boolean bVisual) { return nextUserPosnType (TextNullFrame.MODE_LOAD, bVisual); } public int nextUserPosnType () { return nextUserPosnType (false); } public int nextUserPosnType (int eNullFrameMode) { return nextUserPosnType (eNullFrameMode, false); } public int nextUserPosnType (int eNullFrameMode, boolean bVisual) { int[] result = nextUserPosnTypeData (eNullFrameMode, bVisual); if (result == null) { return TextItem.UNKNOWN; } return result[0]; } public int[] nextUserPosnTypeData (boolean bVisual) { return nextUserPosnTypeData (TextNullFrame.MODE_LOAD, bVisual); } /** * Back up one user position; return item type. * A user position is described as one where a caret may be placed in an * interactive application. Attribute items are transparent to user * positions. This method backs up to the previous user position and * returns the (user) item type stepped over. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return Item type if the object moved to a new user position; * TextItem.UNKNOWN if it was already at the first user position in * the stream. */ public int prevUserPosnType (boolean bVisual) { return prevUserPosnType (TextNullFrame.MODE_LOAD, bVisual); } public int prevUserPosnType () { return prevUserPosnType (false); } public int prevUserPosnType (int eNullFrameMode) { return prevUserPosnType (eNullFrameMode, false); } public int prevUserPosnType (int eNullFrameMode, boolean bVisual) { int[] result = prevUserPosnTypeData (eNullFrameMode, bVisual); if (result == null) { return TextItem.UNKNOWN; } return result[0]; } public int[] prevUserPosnTypeData (boolean bVisual) { return prevUserPosnTypeData (TextNullFrame.MODE_LOAD, bVisual); } /** * Position at the start of the next word. * This method scans the associated stream from this position for the * start of the next word. If the position is in a word (even at its * start), the current word is ignored. If the position is already in * or after the last word of the stream, it advances to the last user * position. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @param eWordMode - Word positioning mode (see the enumeration * described in class {@link TextPosnBase}). * @return TRUE if the position moved; FALSE if it was already at the * last user position in the stream or there is no display. */ public boolean nextWord (boolean bVisual, int eWordMode) { if (eWordMode == WORD_MODE_LEGACY) { boolean bMoved = false; int eState = NW_START; for (; ; ) { int eItem = nextUserPosnWhiteSpace (bVisual); if (eItem == WHITE_SPACE_NOT_FOUND) { break; } eState = geLegacyNextWordState[eState][eItem]; if (eState == NW_DONE) { prevUserPosn (bVisual); // have to go one farther to detect word ... // ... so back up to start of word break; } bMoved = true; } return bMoved; } else { return algorithmicMoveWord (true, false, bVisual); } } public boolean nextWord (boolean bVisual) { return prevWord (bVisual, WORD_MODE_LEGACY); } public boolean nextWord () { return prevWord (false, WORD_MODE_LEGACY); } /** * Position at the start of the previous word. * This method scans the associated stream from this position for the * start of the previous word. If the position is in a word (but not at * its start), it moves to the start of the current word. If the * position is already before the first word of the stream, it moves to * the first user position. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @param eWordMode - Word positioning mode (see the enumeration * described in class {@link TextPosnBase}). * @return TRUE if the position moved; FALSE if it was already at the * first user position in the stream or there is no display. */ public boolean prevWord (boolean bVisual, int eWordMode) { if (eWordMode == WORD_MODE_LEGACY) { boolean bMoved = false; int eState = PW_START; for (; ; ) { int eItem = prevUserPosnWhiteSpace (bVisual); if (eItem == WHITE_SPACE_NOT_FOUND) { break; } bMoved = true; eState = geLegacyPrevWordState[eState][eItem]; if (eState == PW_DONE) { nextUserPosn (bVisual); // have to go one farther to detect word ... // ... so advance to start of word break; } else if (eState == PW_BREAK) { break; // skipping back over any break is a new word ... // ... so we're done } } return bMoved; } else { return algorithmicMoveWord (false, false, bVisual); } } public boolean prevWord (boolean bVisual) { return prevWord (bVisual, WORD_MODE_LEGACY); } public boolean prevWord () { return prevWord (false, WORD_MODE_LEGACY); } /** * Move to the start of the next line. * Note: the associated stream must have a text display for line * operations to succeed. This method moves to the start of the next * line in the stream. If already positioned on the last line, this * method moves to the end of the stream. * @return TRUE if the position moved. FALSE if there is no display or * the position was already at the last user position in the stream. */ public boolean nextLine () { if (display() == null) { return false; } if (! down()) { if (! nextUserPosn()) { return false; } last(); } else { start(); } tighten (false); // try to stay before any attr changes return true; } /** * Move to the start of the current or previous line. * Note: the associated stream must have a text display for line * operations to succeed. This method finds the previous start-of-line. * If the position is not at the start of a line, it is moved to the * start. Otherwise it is moved to the start of the previous line. * @return TRUE if the position moved. FALSE if there is no display or * the position was already at the first user position in the stream. */ public boolean prevLine () { if (display() == null) { return false; } TextPosnBase oTemp = new TextPosnBase (this); if (! prevUserPosn()) { return false; } oTemp.start(); if (lte (oTemp)) { start(); // PrevItem() forced us onto line above } else { copyFrom (oTemp); // simply move to start of line } tighten (false); // try to stay before any attr changes return true; } /** * Move to the start of the next paragraph. * Advance the position such that it is positioned immediately after the * next paragraph break in the associated stream. * @return TRUE if another paragraph break was found; FALSE if the * position was already in or after the last paragraph in the stream. */ public boolean nextPara () { return nextPara (TextNullFrame.MODE_LOAD); } /** * Move to the start of the current or previous paragraph. * Advance the position such that it is positioned immediately after the * previous paragraph break in the associated stream. If the position * is already at the start of a paragraph, it moves to the start of the * previous paragraph. Otherwise, it moves to the start of the current * paragraph. * @return TRUE if the position moved (even if no previous paragraph); * FALSE if already at the first user position in the stream. */ public boolean prevPara () { return prevPara (TextNullFrame.MODE_LOAD); } /** * Move to the start of the current word. * Note: this method assumes the position is within a word or * immediately adjacent to it. If the position is in white space, it * does not move. The position moves to the start of the current word. * If already at the start, the position does not move. * @param eWordMode - Word positioning mode (see the enumeration * described in class {@link TextPosnBase}). */ public void wordStart (int eWordMode) { if (eWordMode == WORD_MODE_LEGACY) { boolean bMoved = false; int eItem; for (eItem = prevUserPosnWhiteSpace(); eItem == WHITE_SPACE_TEXT; eItem = prevUserPosnWhiteSpace()) { bMoved = true; } if ((eItem == WHITE_SPACE_SPACE) || ((eItem == WHITE_SPACE_BREAK) && bMoved)) { nextUserPosn(); } } else { algorithmicMoveWord (false, true, false); } } public void wordStart () { wordEnd (WORD_MODE_LEGACY); } /** * Move to the end of the current word. * Note: this method assumes the position is within a word or * immediately adjacent to it. If the position is in white space, it * does not move. The position moves to the end of the current word. * If already at the end, the position does not move. * @param eWordMode - Word positioning mode (see the enumeration * described in class {@link TextPosnBase}). */ public void wordEnd (int eWordMode) { if (eWordMode == WORD_MODE_LEGACY) { boolean bMoved = false; int eItem; for (eItem = nextUserPosnWhiteSpace(); eItem == WHITE_SPACE_TEXT; eItem = nextUserPosnWhiteSpace()) { bMoved = true; } if ((eItem == WHITE_SPACE_SPACE) || ((eItem == WHITE_SPACE_BREAK) && bMoved)) { prevUserPosn(); } } else { algorithmicMoveWord (true, true, false); } } public void wordEnd () { wordEnd (WORD_MODE_LEGACY); } /** * Move to the start of the current paragraph. * The position moves to the start of the current paragraph (i.e., * immediately after the previous paragraph mark. If already at the * paragraph start, the position does not move. If there are no * paragraph marks between the position and the start of the stream, it * moves to the first user position. */ public void paraStart () { int eItem; for (eItem = prevUserPosnType(); eItem != TextItem.UNKNOWN; eItem = prevUserPosnType()) { if (eItem == TextItem.PARA) { break; } } if (eItem == TextItem.PARA) { nextUserPosn(); // reposition after para mark } } /** * Move to the end of the current paragraph. * The position moves to the end of the current paragraph (i.e., * immediately before the next paragraph mark. If already at the * paragraph end, the position does not move. If there are no paragraph * marks between the position and the end of the stream, it moves to the * last user position. */ public void paraEnd () { int eItem; for (eItem = nextUserPosnType(); eItem != TextItem.UNKNOWN; eItem = nextUserPosnType()) { if (eItem == TextItem.PARA) { break; } } if (eItem == TextItem.PARA) { prevUserPosn(); // reposition before para mark } } /** * Move up one line. * Note: the associated stream must have a text display for line * operations to succeed. This method moves the position up one line in * the displayed text. If already on the first line, the position does * not move. Upward and downward movement attempts to reestablish the * caret X co-ordinate as close as possible to its location on the * old line. Repeated calls to up/down methods use the original * position, so that moving through a short line doesn't truncate the * target X co-ordinate. * @return TRUE if the position moved up; FALSE if no display or already * on the first line. */ public boolean up () { TextDisplay poDisplay = display(); if (poDisplay == null) { return false; } TextPosnBase oResult = new TextPosnBase(); UnitSpan newTarget = poDisplay.caretUp (this, mpoTarget, oResult); if (newTarget == null) { return false; } copy (oResult); tighten (isAtStart()); // try to stay before any attr changes (unless 1st position in line) mpoTarget = newTarget; return true; } /** * Move down one line. * Note: the associated stream must have a text display for line * operations to succeed. This method moves the position down one line * in the displayed text. If already on the last line, the position * does not move. Upward and downward movement attempts to reestablish * the caret X co-ordinate as close as possible to its location on * the old line. Repeated calls to up/down methods use the original * position, so that moving through a short line doesn't truncate the * target X co-ordinate. * @return TRUE if the position moved down; FALSE if no display or * already on the last line. */ public boolean down () { TextDisplay poDisplay = display(); if (poDisplay == null) { return false; } TextPosnBase oResult = new TextPosnBase(); UnitSpan newTarget = poDisplay.caretDown (this, mpoTarget, oResult); if (newTarget == null) { return false; } copy (oResult); tighten (isAtStart()); // try to stay before any attr changes (unless 1st position in line) mpoTarget = newTarget; return true; } /** * Move to the start of the line. * Note: the associated stream must have a text display for line * operations to succeed. This method moves the position to the start * of the current line. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return FALSE if no display; TRUE otherwise. */ public boolean start (boolean bVisual) { TextDisplay poDisplay = display(); if (poDisplay == null) { return false; } TextPosnBase oResult = new TextPosnBase(); // TODO: ugh if (! poDisplay.caretStartEnd (this, false, bVisual, oResult)) { return false; } copy (oResult); tighten (false); // try to stay before any attr changes // or after in the case of the 1st line return true; } public boolean start () { return start (false); } /** * Move to the end of the line. * Note: the associated stream must have a text display for line * operations to succeed. This method moves the position to the end of * the current line. * @param bVisual - TRUE for a visual move; FALSE (default) for a * logical move. * @return FALSE if no display; TRUE otherwise. */ public boolean end (boolean bVisual) { TextDisplay poDisplay = display(); if (poDisplay == null) { return false; } TextPosnBase oResult = new TextPosnBase(); // TODO: ugh if (! poDisplay.caretStartEnd (this, true, bVisual, oResult)) { return false; } copy (oResult); tighten (false); // try to stay before any attr changes return true; } public boolean end () { return end (false); } /** * Determine whether the position is at the start of a line. * Note: the associated stream must have a text display for line * operations to succeed. * @param bCheckFirstLineOnly - (optional) If TRUE, further restrict the * test to check for start of the first line only. If FALSE (default), * check for the start of any line. * @return TRUE if the position is at the start of the line; FALSE if * not or no display. */ public boolean isAtStart (boolean bCheckFirstLineOnly) { TextDisplay poDisplay = display(); if (poDisplay == null) { return false; } // return poDisplay.IsAtStart (this, bCheckFirstLineOnly); // TODO: return false; } public boolean isAtStart () { return isAtStart (false); } /** * Adjust the position around attribute changes. * Typically used when constructing text ranges, this method adjusts the * position to be either before or after any adjacent attribute change. * Typically a text range does not include attribute changes at either * end, so its start position must be advanced if before an attribute * change and its end position must back up if after an attribute * change. * @param bSkipAhead - If TRUE, advance the position over any * immediately subsequent attribute changes in the stream. If FALSE, * back up the position over any immediately previous attribute changes * in the stream. */ public void tighten (boolean bSkipAhead) { int eItem; //int[] result; if (bSkipAhead) { for (eItem = next (TextNullFrame.MODE_ALLOW, true); eItem != TextItem.UNKNOWN; eItem = next (TextNullFrame.MODE_ALLOW, true)) { if (isUserPosnType (eItem)) { break; } next(); } } else { for (eItem = prev (TextNullFrame.MODE_ALLOW, true); eItem != TextItem.UNKNOWN; eItem = prev (TextNullFrame.MODE_ALLOW, true)) { if (isUserPosnType (eItem)) { break; } prev(); } if (index() == 0) { tighten (true); // never position before 1st attr change } } } /** * Return the number of characters that precede this position. * @return Number of characters before this position. */ public int charPosition () { TextPosnBase oPos = new TextPosnBase (this); int nResult = -1; int cChar; do { nResult++; cChar = oPos.prevChar(); } while (cChar != '\0'); return nResult; } /** * Create a position with the associated stream, given a character * index. * Return text position object that is associated with the same stream * as this object, but is positioned immediately after the Nth * character, where N is passed as a parameter. * @param nIndex - Character position to search for. * @return Resulting position object. Note: this is a const reference * to a static object in thread-local storage. The caller should make a * copy of the result, lest another call to this method clobber it. */ public TextPosnBase charPosition (int nIndex) { TextPosnBase poResult = new TextPosnBase (this); int nCurrent = nIndex; if (nCurrent >= 0) { for (; nCurrent > 0; nCurrent--) { if (poResult.nextChar() == '\0') { break; } } } else { for (; nCurrent < 0; nCurrent++) { if (poResult.prevChar() == '\0') { break; } } } return poResult; } /** * Create a range with the associated, given a character index and * length. * Return text range object that is associated with the same stream * as this object. The range will start after the Nth character and * include M characters, where N and M are passed as parameters. * @param lStart - Character position to search for. * @param lLength - Number of characters to include in the range. * @return Resulting range object. Note: this is a const reference to a * static object in thread-local storage. The caller should make a copy * of the result, lest another call to this method clobber it. */ public TextRange charRange (int lStart, int lLength) { TextPosnBase oStart = charPosition (lStart); TextRange poResult = new TextRange (mpoStream, oStart.index(), oStart.index()); TextPosnBase oEnd = charPosition (lStart + lLength); poResult.end (oEnd.index()); return poResult; } /** * Set this position object from graphic caret position. * Note: the associated stream must have a text display for this * operation to succeed. An application typically calls this method in * response to a user clicking on a text object. The position is * updated to represent the closest location in the underlying stream. * @param oNewLocation - Location to search for, in form units. * @param ppoFoundStream - (optional) Allows the call to return a * pointer to a descendant stream that actually held the position being * searched for. If NULL (default), the search will not descend into * embedded streams, instead searching only the position's associated * stream. * @return TRUE if the graphic location was located in the associated * (or possibly descendant) stream; FALSE if not found or no display. */ // public boolean CaretPos (CoordPair oNewLocation, TextStream ppoFoundStream) { // TODO: // TextDisplay poDisplay = display(); // if (poDisplay == null) { // return false; // } // // TextPosnBase oResult = new TextPosnBase(); // boolean bFound = poDisplay.caretPos (mpoStream, oNewLocation, oResult, ppoFoundStream != null); // if (! bFound) { // return false; // } // // CompleteCaretPos (oResult, ppoFoundStream); // return true; // } /** * Return the position as a graphic caret rectangle. * Note: the associated stream must have a text display for this * operation to succeed. This method performs the opposite function of * the other CaretPos() overload. It translates the current text * position represented by this object into a caret rectangle. * @param oCaretExtent - Resulting caret position, in form units. * @param bAllowDangling - (optional) Between successive lines, there is * a position. There are two possible graphic locations for such a * position: after the end of the previous line or before the start of * the next. AXTE normally places the caret at the start of the next in * such a case (value of FALSE, default). This parameter allows such * positions to be reported at the ends of the previous lines (value of * TRUE). * @return FALSE if no display; TRUE otherwise. */ // public boolean CaretPos (Rect oCaretExtent, boolean bAllowDangling) { // TODO: // TextDisplay poDisplay = Display(); // if (poDisplay == null) { // return false; // } // // return poDisplay.CaretPos (this, oCaretExtent, bAllowDangling); // } /** * Set this position object from graphic caret position and a given * frame. * Note: the associated stream must have a text display for this * operation to succeed. An application typically calls this method in * response to a user clicking on a text object. The application first * determines which frame got the click. The position is updated to * represent the closest location in the underlying stream. * @param poFrame - Pointer to the frame in which the click occurred. * @param oNewLocation - Location to search for, in form units, relative * to the top left corner of the given frame. * @param ppoFoundStream - (optional) Allows the call to return a * pointer to a descendant stream that actually held the position being * searched for. If NULL (default), the search will not descend into * embedded streams, instead searching only the position's associated * stream. * @return TRUE if the graphic location was located in the associated * (or possibly descendant) stream; FALSE if not found or no display. */ // public boolean FrameCaretPos (TextFrame poFrame, CoordPair oNewLocation, TextStream ppoFoundStream) { // TODO: // TextDisplay poDisplay = Display(); // if (poDisplay == null) { // return false; // } // // TextPosnBase oResult; // boolean bFound = poDisplay.FrameCaretPos (poFrame, mpoStream, oNewLocation, oResult, ppoFoundStream != null); // if (! bFound) { // return false; // } // // CompleteCaretPos (oResult, ppoFoundStream); // return true; // } /** * Return the position as a graphic caret rectangle and frame pointer. * Note: the associated stream must have a text display for this * operation to succeed. This method performs the opposite function of * the other FrameCaretPos() overload. It translates the current text * position represented by this object into a caret rectangle and frame * pointer. * @param poFrame - Returns a pointer to the frame in which the caret is * currently. * @param oCaretExtent - Resulting caret position, in form units, * relative to the top left of the returned frame. * @param bAllowDangling - (optional) Between successive lines, there is * a position. There are two possible graphic locations for such a * position: after the end of the previous line or before the start of * the next. AXTE normally places the caret at the start of the next in * such a case (value of FALSE, default). This parameter allows such * positions to be reported at the ends of the previous lines (value of * TRUE). * @return FALSE if no display; TRUE otherwise. */ // public boolean FrameCaretPos (TextFrame poFrame, Rect oCaretExtent, boolean bAllowDangling) { // TODO: // TextDisplay poDisplay = Display(); // if (poDisplay == null) { // return false; // } // // return poDisplay.FrameCaretPos (this, poFrame, oCaretExtent, bAllowDangling); // } /** * Return the baseline caret point for the position. * The baseline caret point is defined as having an X co-ordinate equal * to the horizontal centre of the rectangle returned by the CaretPos() * method; and a Y co-ordinate equal to the offset of the particular * line's baseline from the top of the text object. * @param bAllowDangling - (optional) Between successive lines, there is * a position. There are two possible graphic locations for such a * position: after the end of the previous line or before the start of * the next. AXTE normally places the caret at the start of the next in * such a case (value of FALSE, default). This parameter allows such * positions to be reported at the ends of the previous lines (value of * TRUE). * @return Baseline caret point as described above. If the position has * no display associated, the value (0,0) is returned. */ // public CoordPair CaretBaseline (boolean bAllowDangling) { // TODO // TextDisplay poDisplay = Display(); // if (poDisplay == null) { // return CoordPair.ZeroZero(); // } // // return poDisplay.CaretBaseline (this, bAllowDangling); // } /** * Make sure this position is visible in the given graphic environment. * Note: the associated stream must have a text display for this * operation to succeed. Additionally, the appropriate scroller must * have been created and associated with the root display stream. Given * a graphic environment, this method will locagte the appropriate * scroller and scroll it such that this position is visible within the * scroll window. * @param oGfxEnv - Graphic environment to scroll in. */ // public void ScrollTo (GFXEnv oGfxEnv) { // TODO: // TextDisplay poDisplay = Display(); // // if (poDisplay == null) { // return; // } // // poDisplay.ScrollTo (this, oGfxEnv); // } /** * Insert a character at this position. * The specified character is inserted at this position and the position * object then advances so that it is immediately after the inserted * character. The call is ignored id there is no associated stream. * @param cInsert - Character to insert. */ public void insert (char cInsert) { if (mpoStream != null) { mpoStream.posnInsert (this, cInsert); } } /** * Insert a string at this position. * The contents of the specified string are inserted at this position * and the position object then advances so that it is immediately after * the last inserted character. The call is ignored id there is no * associated stream. * @param sInsert - String to insert. */ public void insert (String sInsert) { if (mpoStream != null) { mpoStream.posnInsert (this, sInsert); } } /** * Insert rich text at this position. * The contents of the specified text stream are inserted (copied) at * this position and the position object then advances so that it is * immediately after the last inserted item. The call is ignored id * there is no associated stream. Note that rich text insertion may * involve the reconciliation of attributes. For example, if the * inserted stream specifically sets attributes to be the same as those * in the target stream, the inserted attribute change is redundant and * will be removed. On the other hand, if the inserted stream changes * attributes from those of the target, an attribute change must be * manufactured after the insert to restore attributes for subsequent * text in the target stream. * @param oInsert - Rich text stream to insert. */ public void insert (TextStream oInsert) { if (mpoStream != null) { mpoStream.posnInsert (this, oInsert); } } /** * Insert an embedded field at this position. * Clone a copy of the given text field object and insert the clone as * an embedded field at this position in the associated stream. This * position advances so that it is immediately after the inserted field. * The call is ignored id there is no associated stream. * @param poField - Field to insert. * @return Pointer to cloned field that has been embedded in the * associated stream. This object belongs to the stream and must not be * deleted by the caller. */ public TextField insert (TextField poField) { if (mpoStream == null) { return null; } TextPosnBase oAnchor = new TextPosnBase (this); oAnchor.position (POSN_BEFORE); mpoStream.posnInsert (this, poField); return oAnchor.nextField (true); } /** * Insert an embedded object at this position. * Clone a copy of the given embedded object and insert the clone as an * embedded object at this position in the associated stream. This * position advances so that it is immediately after the inserted * object. The call is ignored id there is no associated stream. * @param poEmbed - Object to insert. * @return Pointer to cloned object that has been embedded in the * associated stream. This object belongs to the stream and must not be * deleted by the caller. */ public TextEmbed insert (TextEmbed poEmbed) { if (mpoStream == null) { return null; } TextPosnBase oAnchor = new TextPosnBase (this); oAnchor.position (POSN_BEFORE); mpoStream.posnInsert (this, poEmbed); return oAnchor.nextEmbed (true); } /** * Insert a paragraph mark at this position. * The specified paragraph mark is inserted at this position and the * position object then advances so that it is immediately after the * inserted paragraph mark. The call is ignored id there is no * associated stream. */ public void insertPara () { if (mpoStream != null) { mpoStream.posnInsertPara (this); } } /** * Insert a position marker at the position represented by this position * object. * If this position is not associated with any text stream, the call is * ignored. * @param poMarker - Pointer to marker to insert. Note that markers are * always cloned on insertion, so a copy actually gets inserted. The * caller continues to retain ownership of the instance referred to by * this parameter, and can delete it any time after the call. * @return Pointer to the cloned copy actually inserted in the text * stream. While this is a non-const pointer, it is owned by AXTE and * must not be deleted by the client, though the client can cache a * pointers to this marker provide that it participates in the marker * reference counting mechanism. */ public TextMarker insert (TextMarker poMarker) { return (mpoStream == null) ? null : mpoStream.posnMarker (this, poMarker); } /** * Delete ahead from this position in the associated stream. * Delete items in the associated stream that come after this position. * Normally, there may be fix-up required after such a deletion. If * there are any attribute changes in the deleted items, the last one * must be preserved for text that follows the deletion. Also, if the * deletion spans paragraphs, the result may be a paragraph with * conflicting paragraph attributes. These are reconciled in favour of * the first paragraph (if it still has text left after the delete). * Alternatively, one can invoke a raw delete that doesn't do the * fix-ups (not recommended, except for very special cases). * @param nDelete - (optional) Number of items to delete. Default * is 1. * @param bRaw - (optional) TRUE for raw delete (see above); FALSE * (default) for normal delete. */ public void deleteAhead (int nDelete, boolean bRaw) { if (mpoStream == null) { return; } if (! bRaw) { // user obj delete ... tighten (true); // skip over any attr change // Watson 1198669: Deletion of base char must also delete all accents. // This means finding the last base char in the range and deleting all // accents that follow it, whether they are in the range or not. Note // that accents at the start of the range can be deleted without worrying // about their base char. Similarly, if the range consists only of // accents, it need not be extended to include a base char. TextPosnBase oEnd = new TextPosnBase (stream(), index() + nDelete); TextPosnBase oBack = new TextPosnBase (oEnd); while (oBack.isCombiningChar (false)) { // Scan back from the end of the range through all accents until we // encounter a base character or the start of the range. This is to // determine if there is a base character being deleted. oBack.tighten (true); if (oBack.lte (this)) { break; } } if (oBack.gt (this)) { // If there is a base character being deleted, scan forward beyond the // end of the range through trailing accents and include them in the // deletion. while (oEnd.isCombiningChar (true)) { ; } oEnd.tighten (false); nDelete = oEnd.index() - index(); } } mpoStream.posnDelete (this, nDelete, bRaw); if (! bRaw) { tighten (isAtStart()); // after attr unless SOL } } public void deleteAhead (int nDelete) { deleteAhead (nDelete, false); } public void deleteAhead () { deleteAhead (1, false); } /** * Delete back from this position in the associated stream. * Delete items in the associated stream that come before this position. * Normally, there may be fix-up required after such a deletion. If * there are any attribute changes in the deleted items, the last one * must be preserved for text that follows the deletion. Also, if the * deletion spans paragraphs, the result may be a paragraph with * conflicting paragraph attributes. These are reconciled in favour of * the first paragraph (if it still has text left after the delete). * Alternatively, one can invoke a raw delete that doesn't do the * fix-ups (not recommended, except for very special cases). * @param nDelete - (optional) Number of items to delete. Default * is 1. * @param bRaw - (optional) TRUE for raw delete (see above); FALSE * (default) for normal delete. */ public void deleteBack (int nDelete, boolean bRaw) { if (mpoStream == null) { return; } int nActual = nDelete; if (bRaw) { // raw delete: adjust if not enough items before if (nActual > mnIndex) { nActual = mnIndex; // can't delete more than this } index (mnIndex - nActual); // move back and delete ahead } else { // "normal" delete: we'll delete only user positionable items int nLeft = nDelete; while ((nLeft > 0) && prevUserPosn()) { nLeft--; // moves back // one more that can be deleted } nActual -= nLeft; } deleteAhead (nActual, bRaw); } public void deleteBack (int nDelete) { deleteBack (nDelete, false); } public void deleteBack () { deleteBack (1, false); } /** * Obtain a pointer to the attributes in effect at this position. * Scan back from this position for the attribute item applies. Return * a pointer to those attributes. If there are no such attributes, this * method will attempt to move up recursively through the text stream * ancestry (e.g., this might be a field in a parent stream that has * attributes). * @return Pointer to attributes in effect at this position. NULL if no * such attributes could be found. */ public TextAttr attributePtr () { if (mpoStream == null) { return null; } else { return mpoStream.posnAttrPtr (this); } } /** * Obtain the attributes in effect at this position. * Scan back from this position for the attribute item applies. Return * those attributes. If there are no such attributes, this method will * attempt to move up recursively through the text stream ancestry * (e.g., this might be a field in a parent stream that has attributes). * This method is generally not considered as efficient as * AttributePtr(). * @return Attributes in effect at this position. Default values if no * such attributes could be found. */ public TextAttr attribute () { TextAttr poAttr = attributePtr(); if (poAttr == null) { return TextAttr.defaultAttr (false); } else { return poAttr; } } /** * Set attributes at this position. * One uses this method to set attributes immediately before inserting * text with the new attributes. The act of setting attributes alone * does not change the appearance of any text already in the stream. If * you make any call other than inserting text at this position after * inserting the attributes, the inserted attributes will be considered * redundant and removed. The call actually results in two consecutive * attribute changes in the stream: the first sets the new attributes * for the anticipated text and the second restores attributes for * existing subsequent text. This position is adjusted so that it is * between the two attribute items. * @param oNewAttr - Attributes to insert. * @param bRaw - (optional) Deprecated parameter is ignored. */ public void attribute (TextAttr oNewAttr, boolean bRaw) { if (mpoStream != null) { mpoStream.posnAttr (this, oNewAttr, bRaw); } } public final void attribute (TextAttr oNewAttr) { attribute (oNewAttr, false); } /** * Establish the keyboard (left-to-right or right-to-left), based on * this position object. */ // AXTEWRSWrapper::SetKeyboard (mbIsRTL); public void setKeyboard () { } /** * Assignment operator. *

* Copy the entire content of the source position object including * stream association, index and before/after type. * @param oSource Source position object to copy. */ public void copyFrom (TextPosnBase oSource) { copy (oSource); } /** * Equality comparison. * Two text positions are considered equal if they are associated with * the same stream, have the same index number. Note that the operation * does not compare any other attributes (e.g., before/after, affinity, * RTL etc.) This is so that equality comparison has a consistent * behaviour with greater/less than comparisons. * @param object - Position to compare against. * @return TRUE if the positions are equal; FALSE if not. */ public boolean equals (Object object) { if (this == object) return true; // This overrides Object.equals(boolean) directly, so... if (object == null) return false; if (object.getClass() != getClass()) return false; TextPosnBase posn = (TextPosnBase) object; return (mpoStream == posn.mpoStream) && (mnIndex == posn.mnIndex); } public int hashCode() { int hash = 97; hash = (hash * 31) ^ mpoStream.hashCode(); hash = (hash * 31) ^ mnIndex; return hash; } /** * Inequality comparison. *

* Two text positions are considered unequal if they are associated with * the different streams, have the different index numbers or the * different before/after position types. Note that the current * implementation is rather crude in that it still compares index even * when both streams are NULL. * @param oCompare Position to compare against. * @return TRUE if the positions are not equal; FALSE if not. */ public boolean notEqual (TextPosnBase oCompare) { return ! equals (oCompare); } /** * Is this position less than the given one?. * The comparison is rather crude in that it compares index number only, * completely ignoring streams and before/after position types. * @param oCompare - Position to compare against. * @return TRUE if this position is less than the given one; FALSE * otherwise. */ public boolean lt (TextPosnBase oCompare) { return mnIndex < oCompare.mnIndex; } /** * Is this position less than or equal to the given one?. * The comparison is rather crude in that it compares index number only, * completely ignoring streams and before/after position types. * @param oCompare - Position to compare against. * @return TRUE if this position is less than or equal to the given one; * FALSE otherwise. */ public boolean lte (TextPosnBase oCompare) { return mnIndex <= oCompare.mnIndex; } /** * Is this position greater than the given one?. * The comparison is rather crude in that it compares index number only, * completely ignoring streams and before/after position types. * @param oCompare - Position to compare against. * @return TRUE if this position is greater than the given one; FALSE * otherwise. */ public boolean gt (TextPosnBase oCompare) { return mnIndex > oCompare.mnIndex; } /** * Is this position greater than or equal to the given one?. * The comparison is rather crude in that it compares index number only, * completely ignoring streams and before/after position types. * @param oCompare - Position to compare against. * @return TRUE if this position is greater than or equal to the given * one; FALSE otherwise. */ public boolean gte (TextPosnBase oCompare) { return mnIndex >= oCompare.mnIndex; } /** * Overridden: Insert a paragraph mark. * This method implements a virtual method declared in base class * TextMkBase. It simply calls the InsertPara() method. */ public void para () { insertPara(); } /** * Overridden: Insert field. * This method implements a virtual method declared in base class * TextMkBase. It simply calls the appropriate Insert() overload. * @param poField - Field to be inserted. */ public void field (TextField poField) { // TODO: there seems to be some confusion over the name of this insert (poField); } /** * Overridden: Insert text. * This method implements a virtual method declared in base class * TextMkBase. It simply calls the appropriate Insert() overload. * @param sText - Text to be inserted. */ public void text (String sText) { insert (sText); } /** * Overridden: Insert attribute change. * This method implements a virtual method declared in base class * TextMkBase. It simply calls the appropriate Attribute() overload. * @param oAttr - Attribute change to insert. */ public void attr (TextAttr oAttr) { attribute (oAttr); } public boolean legacyPositioning () { return (mpoStream == null) ? false : mpoStream.hasLegacyPositioning(); } public void marker (TextMarker poMarker) { insert (poMarker); } public TextMarker markerStart (TextMarker poMarker) { return (mpoStream == null) ? null : mpoStream.posnMarkerStart (this, poMarker); } public void markerEnd (TextMarker poMarker) { if (mpoStream != null) { mpoStream.posnMarkerEnd (this, poMarker); } } boolean isSamePosition (TextPosnBase oCompare) { return (mpoStream == oCompare.mpoStream) && (mnIndex == oCompare.mnIndex); } public TextStream stream () { return mpoStream; } boolean isMarkerPosition () { return mbIsMarkerPosition; } void insertNullFrame (TextNullFrame poNullFrame) { // TODO: if (mpoStream != null) { mpoStream.posnInsertNullFrame (this, poNullFrame); } } void removeNullFrame () { // TODO: if (mpoStream != null) { mpoStream.posnRemoveNullFrame (this); } } void invalidate (boolean bTighten) { mbIsInvalid = true; mbTightenPending = bTighten; } void testValid () { if (mbIsInvalid) { validate(); } } char charMove (boolean bForward, boolean bVisual) { int[] result = bForward ? nextUserPosnTypeData (bVisual) : prevUserPosnTypeData (bVisual); return ((result != null) && (result[0] != TextItem.UNKNOWN)) ? (char) result[1] : '\0'; } // proprietary: for use by class TextLoadText int major () { testValid(); return mnMajor; } int minor () { testValid(); return mnMinor; } // proprietary: for use by class TextRange void copyData (TextPosnBase oSource) { if (mpoStream == oSource.mpoStream) { if (mpoStream != null) { copySameStream (oSource); } } else if (mpoStream != null) { index (oSource.index()); } } int[] nextData (int eNullFrameMode, boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnNext (this, eNullFrameMode, bTestOnly); } int[] prevData (int eNullFrameMode, boolean bTestOnly) { if (mpoStream == null) { return null; } return mpoStream.posnPrev (this, eNullFrameMode, bTestOnly); } boolean nextPara (int eNullFrameMode) { int eItem; for (eItem = nextUserPosnType (eNullFrameMode); eItem != TextItem.UNKNOWN; eItem = nextUserPosnType (eNullFrameMode)) { if (eItem == TextItem.PARA) { break; } } return eItem == TextItem.PARA; } boolean prevPara (int eNullFrameMode) { boolean bMoved = false; int eItem; for (eItem = prevUserPosnType (eNullFrameMode); eItem != TextItem.UNKNOWN; eItem = prevUserPosnType (eNullFrameMode)) { if ((eItem == TextItem.PARA) && bMoved) { break; } bMoved = true; } if (eItem != TextItem.UNKNOWN) { nextUserPosn(); // reposition after para mark } return bMoved; } int[] nextUserPosnTypeData (int eNullFrameMode, boolean bVisual) { if (bVisual) { return visualMoveData (true); // TODO: is this correct? } int[] result; result = nextData (eNullFrameMode, false); while ((result != null) && (result[0] != TextItem.UNKNOWN)) { if (isUserPosnType (result[0])) { break; } result = nextData (eNullFrameMode, false); } if (result == null) { return null; } int eReturn = result[0]; char c = (char) result[1]; if ((eReturn == TextItem.PARA) || ((eReturn == TextItem.CHAR) && (c == '\n'))) { int[] skipAttrResult = nextData (eNullFrameMode, true); while ((skipAttrResult != null) && (skipAttrResult[0] != TextItem.UNKNOWN)) { if (isUserPosnType (skipAttrResult[0])) { break; } next (eNullFrameMode); skipAttrResult = nextData (eNullFrameMode, true); } } else if (eReturn == TextItem.CHAR) { if ((c >= 0x3040) // Hirigana or Katakana && (c <= 0x30FF) && (display() != null)) { // Display().CheckAXTELigature (this, true); } } switch (eReturn) { case TextItem.PARA: c = '\n'; break; default: c = 0xFFFC; break; } result[0] = eReturn; result[1] = c; return result; } int[] prevUserPosnTypeData (int eNullFrameMode, boolean bVisual) { if (bVisual) { return visualMoveData (true); // TODO: is this correct? } int[] result; // Scan back (skipping attrs) until we skip over a user position item result = prevData (eNullFrameMode, false); while ((result != null) && (result[0] != TextItem.UNKNOWN)) { if (isUserPosnType (result[0])) { break; } result = prevData (eNullFrameMode, false); } if (result == null) { return null; } int eReturn = result[0]; char c = (char) result[1]; // Now, scan again, skipping over any attrs, so that the position ends up // immediately after the previous user item (and before the attrs). if ((eReturn != TextItem.PARA) && ((eReturn != TextItem.CHAR) || (c != '\n'))) { if (eReturn == TextItem.CHAR) { if (((c == 0x3099) // Combining Hirigana/Katakana voiced sound mark || (c == 0x309A)) // Combining Hirigana/Katakana semi-voiced sound mark && (display() != null)) { // Display().CheckAXTELigature (this, false); // TODO: } } int[] skipAttrResult = prevData (eNullFrameMode, true); while ((skipAttrResult != null) && (skipAttrResult[0] != TextItem.UNKNOWN)) { if (isUserPosnType (skipAttrResult[0])) { break; } prev (eNullFrameMode); // need to skip over this attr skipAttrResult = prevData (eNullFrameMode, true); } if (mnIndex == 0) { first(); // hit start // never posn before 1st attr } } eReturn = result[0]; c = (char) result[1]; switch (eReturn) { case TextItem.PARA: c = '\n'; break; default: c = 0xFFFC; break; } result[0] = eReturn; result[1] = c; return result; } void setMarkerPosition (boolean bIsMarkerPosition) { mbIsMarkerPosition = bIsMarkerPosition; } private void doAssociate (TextStream poNewStream, int nNewIndex, int eNewPosn) { cleanupTarget(); if (initWithStream (poNewStream, eNewPosn)) { mpoStream.posnUpdateStreamLoc (this, nNewIndex); } } private void copy (TextPosnBase oSource) { mpoStream = oSource.mpoStream; copySameStream (oSource); } private void copySameStream (TextPosnBase oSource) { mnMajor = oSource.mnMajor; mnMinor = oSource.mnMinor; mnIndex = oSource.mnIndex; mbIsBeforePosition = oSource.mbIsBeforePosition; mbIsPrevAffinity = oSource.mbIsPrevAffinity; mbIsRTL = oSource.mbIsRTL; mbIsMarkerPosition = oSource.mbIsMarkerPosition; mbIsInvalid = oSource.mbIsInvalid; mbTightenPending = oSource.mbTightenPending; cleanupTarget(); if (oSource.mpoTarget != null) { mpoTarget = oSource.mpoTarget; // just copy because they're immutable } } private TextDisplay display () { if (mpoStream == null) { return null; } return mpoStream.display(); } private void initialize () { mpoStream = null; mnMajor = 0; mnMinor = 0; mnIndex = 0; mbIsBeforePosition = false; mbIsPrevAffinity = false; mbIsRTL = false; mbIsMarkerPosition = false; mbIsInvalid = false; mbTightenPending = false; mpoTarget = null; } private boolean initWithStream (TextStream poNewStream, int eNewPosn) { initialize(); position (eNewPosn); if (poNewStream == null) { return false; } mpoStream = poNewStream; return true; } void cleanup () { cleanupTarget(); initialize(); } void cleanupTarget () { mpoTarget = null; } // private int visualMove (boolean bRight) { // int[] result = visualMoveData (bRight); // return (result == null) ? TextItem.UNKNOWN : result[0]; // } private int[] visualMoveData (boolean bRight) { if (display() == null) { return null; } // TextPosnBase oResult = new TextPosnBase(); // TODO: ugh // return Display().CaretLeftRight (this, bRight, oResult); return null; // TODO: } // private void completeCaretPos (TextPosnBase oResult, TextStream ppoFoundStream) { // if (ppoFoundStream != null) { // if (oResult.stream() == mpoStream) { // ppoFoundStream = null; // } else { // ppoFoundStream = oResult.stream(); // oResult.stream().isDescendentOf (mpoStream, oResult); // oResult.nextUserPosn(); // } // } // // copyFrom (oResult); // tighten (false); // try to stay before any attr changes // } private void validate () { int nIndex = mnIndex; mnIndex = 0; mnMajor = 0; mnMinor = 0; if (mpoStream != null) { mpoStream.posnUpdateStreamLoc (this, nIndex); } if (mbTightenPending) { tighten (false); mbTightenPending = false; } mbIsInvalid = false; } private int nextUserPosnWhiteSpace (boolean bVisual) { int eItem = nextUserPosnType (bVisual); switch (eItem) { case TextItem.UNKNOWN: return WHITE_SPACE_NOT_FOUND; case TextItem.CHAR: return Character.isWhitespace((char) prevChar(true)) ? WHITE_SPACE_SPACE : WHITE_SPACE_TEXT; case TextItem.PARA: return WHITE_SPACE_SPACE; } return WHITE_SPACE_BREAK; } private int nextUserPosnWhiteSpace () { return nextUserPosnWhiteSpace (false); } private int prevUserPosnWhiteSpace (boolean bVisual) { int eItem = prevUserPosnType (bVisual); switch (eItem) { case TextItem.UNKNOWN: return WHITE_SPACE_NOT_FOUND; case TextItem.CHAR: return Character.isWhitespace((char) nextChar(true)) ? WHITE_SPACE_SPACE : WHITE_SPACE_TEXT; case TextItem.PARA: return WHITE_SPACE_SPACE; } return WHITE_SPACE_BREAK; } private int prevUserPosnWhiteSpace () { return prevUserPosnWhiteSpace (false); } private boolean algorithmicMoveWord (boolean bNext, boolean bLimitOnly, boolean bVisual) { int nIndex = index(); char c = charMove (bNext, bVisual); if (c == '\0') { return false; } int eData = TextCharProp.getCharProperty (c); int nPrev = propertyToNextWordBreak (eData); index (nIndex); boolean bMoved = false; TextStreamIterator oStreamIterator = new TextStreamIterator (this, bNext, bVisual); TextBreakIterator poBreakIterator = TextBreakIterator.createWordInstance (oStreamIterator); int nRowOffset = bNext ? 0 : 3; nIndex = poBreakIterator.next(); while (nIndex != TextBreakIterator.DONE) { bMoved = true; index (nIndex); c = charMove (bNext, bVisual); if (c == '\0') { break; } eData = TextCharProp.getCharProperty (c); int nNext = propertyToNextWordBreak (eData); if (bLimitOnly) { if (nPrev != nNext) { break; } } else { if (gbWordBreak[nPrev + nRowOffset][nNext]) { break; } } nPrev = nNext; nIndex = poBreakIterator.next(); } if (nIndex != TextBreakIterator.DONE) { index (nIndex); } return bMoved; } private boolean isCombiningChar (boolean bForward) { boolean bIsCombining = false; int eItem = bForward ? nextUserPosnType (TextNullFrame.MODE_ALLOW) : prevUserPosnType (TextNullFrame.MODE_ALLOW); if (eItem != TextItem.UNKNOWN) { if (eItem == TextItem.CHAR) { int c = bForward ? prevChar (true) : nextChar (true); int eBreak = TextCharProp.getCharProperty (c); if (TextCharProp.getBreakClass (eBreak) == TextCharProp.BREAK_CM) { bIsCombining = true; } } if (! bIsCombining) { if (bForward) { prevUserPosnType (TextNullFrame.MODE_ALLOW); } else { nextUserPosnType (TextNullFrame.MODE_ALLOW); } } } return bIsCombining; } private static int propertyToNextWordBreak (int eData) { if (TextCharProp.getBreakClass (eData) == TextCharProp.BREAK_SP) { return WB_SPACE; } if (TextCharProp.isWordEdge (eData)) { return WB_LETTER; } return WB_OTHER; } private static boolean isUserPosnType (int eItem) { return eItem != TextItem.ATTR; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy