com.adobe.xfa.text.TextPosnBase Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
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;
}
}