You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by rw...@apache.org on 2018/04/03 23:01:30 UTC

svn commit: r1828291 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk: TextPane.java skin/TextPaneSkin.java text/ComponentNode.java text/Element.java text/Node.java text/TextNode.java

Author: rwhitcomb
Date: Tue Apr  3 23:01:30 2018
New Revision: 1828291

URL: http://svn.apache.org/viewvc?rev=1828291&view=rev
Log:
PIVOT-891:  Further improvements to TextPane:
* Fix a problem doing delete at end of paragraph and then inserting
  right there again -- the last node in the sequence may not get
  its offset updated because the character count is decremented
  first; should be decremented after.
* Make a bunch of additions to deal with the new CharSpan class
  and so we can make use of common code to do word, line selection
  amongst all three text input controls.
* Add some debug code in TextPane to print out node trees with their
  offsets and counts.
* Use the new CharUtils methods for moving around in and selecting
  text "words".

Modified:
    pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/text/ComponentNode.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Element.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Node.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/text/TextNode.java

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java Tue Apr  3 23:01:30 2018
@@ -28,6 +28,7 @@ import org.apache.pivot.beans.DefaultPro
 import org.apache.pivot.collections.LinkedStack;
 import org.apache.pivot.collections.Sequence;
 import org.apache.pivot.text.AttributedStringCharacterIterator;
+import org.apache.pivot.text.CharSpan;
 import org.apache.pivot.util.ListenerList;
 import org.apache.pivot.util.StringUtils;
 import org.apache.pivot.util.Utils;
@@ -428,9 +429,11 @@ public class TextPane extends Container
                 Paragraph paragraph = (Paragraph) descendant;
 
                 Node node = getRightmostDescendant(paragraph);
+dumpNode("insertText, paragraph", paragraph, 0);
                 if (node instanceof TextNode) {
                     // Insert the text into the existing node
                     TextNode textNode = (TextNode) node;
+System.out.format("textNode insert: index=%1$d, textNode.doc offset=%2$d, paragraph.doc offset=%3$d%n", index, textNode.getDocumentOffset(), paragraph.getDocumentOffset());
                     textNode.insertText(text, index - textNode.getDocumentOffset());
                 } else if (node instanceof Element) {
                     // Append a new text node
@@ -586,12 +589,11 @@ public class TextPane extends Container
 
         if (offset >= 0 && offset < document.getCharacterCount()) {
             Node descendant = document.getDescendantAt(offset);
-
+dumpNode("removeText, grandparent", descendant.getParent().getParent(), 0);
             // Used to be: if (selectionLength == 0 && ...
             if (characterCount <= 1 && descendant instanceof Paragraph) {
                 // We are deleting a paragraph terminator
                 Paragraph paragraph = (Paragraph) descendant;
-
                 Element parent = paragraph.getParent();
                 int index = parent.indexOf(paragraph);
 
@@ -710,8 +712,9 @@ public class TextPane extends Container
                 int start = selectionStart;
                 int tabWidth = ((TextPane.Skin) getSkin()).getTabWidth();
                 Node currentNode = document.getDescendantAt(start);
-                while (!(currentNode instanceof Paragraph))
+                while (!(currentNode instanceof Paragraph)) {
                     currentNode = currentNode.getParent();
+                }
                 int paragraphStartOffset = start - currentNode.getDocumentOffset();
 
                 bulkOperation = true;
@@ -993,6 +996,15 @@ public class TextPane extends Container
     }
 
     /**
+     * Returns a character span (start, length) representing the current selection.
+     *
+     * @return A char span with the start and length values.
+     */
+    public CharSpan getCharSelection() {
+        return new CharSpan(selectionStart, selectionLength);
+    }
+
+    /**
      * Sets the selection. The sum of the selection start and length must be
      * less than the length of the text input's content.
      *
@@ -1029,6 +1041,7 @@ public class TextPane extends Container
      *
      * @param selection The new span describing the selection.
      * @see #setSelection(int, int)
+     * @throws IllegalArgumentException if the span is {@code null}.
      */
     public final void setSelection(Span selection) {
         Utils.checkNull(selection, "selection");
@@ -1037,6 +1050,19 @@ public class TextPane extends Container
     }
 
     /**
+     * Sets the selection.
+     *
+     * @param selection The character span (start and length) for the selection.
+     * @see #setSelection(int, int)
+     * @throws IllegalArgumentException if the character span is {@code null}.
+     */
+    public final void setSelection(CharSpan selection) {
+        Utils.checkNull(selection, "selection");
+
+        setSelection(selection.start, selection.length);
+    }
+
+    /**
      * Change the selection to the given location (and length 0),
      * with checks to make sure the selection doesn't go out of bounds.
      * <p> Meant to be called from {@link #undo} (that is, internally).
@@ -1169,4 +1195,18 @@ public class TextPane extends Container
     public ListenerList<TextPaneSelectionListener> getTextPaneSelectionListeners() {
         return textPaneSelectionListeners;
     }
+
+    private static final String FORMAT = "%1$s%2$s: doc=%3$d, count=%4$d%n";
+
+    public static void dumpNode(String msg, Node node, int indent) {
+        if (msg != null && !msg.isEmpty()) {
+            System.out.println(msg + ":");
+        }
+        System.out.format(FORMAT, StringUtils.fromNChars(' ', indent*2), node.getClass().getSimpleName(), node.getDocumentOffset(), node.getCharacterCount());
+        if (node instanceof Element) {
+            for (Node n : (Element)node) {
+                dumpNode("", n, indent + 1);
+            }
+        }
+    }
 }

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/TextPaneSkin.java Tue Apr  3 23:01:30 2018
@@ -31,7 +31,9 @@ import java.text.AttributedCharacterIter
 import org.apache.pivot.collections.Dictionary;
 import org.apache.pivot.collections.Sequence;
 import org.apache.pivot.text.AttributedStringCharacterIterator;
+import org.apache.pivot.text.CharSpan;
 import org.apache.pivot.text.CompositeIterator;
+import org.apache.pivot.util.CharUtils;
 import org.apache.pivot.util.Utils;
 import org.apache.pivot.wtk.ApplicationContext;
 import org.apache.pivot.wtk.Bounds;
@@ -43,6 +45,7 @@ import org.apache.pivot.wtk.Insets;
 import org.apache.pivot.wtk.Keyboard;
 import org.apache.pivot.wtk.Mouse;
 import org.apache.pivot.wtk.Platform;
+import org.apache.pivot.wtk.SelectDirection;
 import org.apache.pivot.wtk.TextInputMethodListener;
 import org.apache.pivot.wtk.TextPane;
 import org.apache.pivot.wtk.TextPaneListener;
@@ -277,6 +280,7 @@ org.apache.pivot.util.Console.logMethod(
     protected boolean doingCaretCalculations = false;
 
     private int anchor = -1;
+    private SelectDirection selectDirection = null;
     private TextPane.ScrollDirection scrollDirection = null;
     private int mouseX = -1;
 
@@ -924,61 +928,6 @@ org.apache.pivot.util.Console.logMethod(
         return consumed;
     }
 
-    private void selectSpan(TextPane textPane, Document document, int start) {
-        int rowStart = getRowOffset(document, start);
-        int rowLength = getRowLength(document, start);
-        if (start - rowStart >= rowLength) {
-            start = rowStart + rowLength - 1;
-            if (start < 0) {
-                return;
-            }
-            char ch = document.getCharacterAt(start);
-            if (ch == '\r' || ch == '\n') {
-                start--;
-            }
-        }
-        if (start < 0) {
-            return;
-        }
-        char ch = document.getCharacterAt(start);
-        int selectionStart = start;
-        int selectionLength = 1;
-        if (Character.isWhitespace(ch)) {
-            // Move backward to beginning of whitespace block
-            // but not before the beginning of the line.
-            do {
-                selectionStart--;
-            } while (selectionStart >= rowStart
-                && Character.isWhitespace(document.getCharacterAt(selectionStart)));
-            selectionStart++;
-            selectionLength = start - selectionStart;
-            // Move forward to end of whitespace block
-            // but not past the end of the text or the end of line
-            do {
-                selectionLength++;
-            } while (selectionStart + selectionLength - rowStart < rowLength
-                && Character.isWhitespace(document.getCharacterAt(selectionStart + selectionLength)));
-        } else if (Character.isJavaIdentifierPart(ch)) {
-            // Move backward to beginning of identifier block
-            do {
-                selectionStart--;
-            } while (selectionStart >= rowStart
-                && Character.isJavaIdentifierPart(document.getCharacterAt(selectionStart)));
-            selectionStart++;
-            selectionLength = start - selectionStart;
-            // Move forward to end of identifier block
-            // but not past end of text
-            do {
-                selectionLength++;
-            } while (selectionStart + selectionLength - rowStart < rowLength
-                && Character.isJavaIdentifierPart(document.getCharacterAt(selectionStart
-                    + selectionLength)));
-        } else {
-            return;
-        }
-        textPane.setSelection(selectionStart, selectionLength);
-    }
-
     @Override
     public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
         boolean consumed = super.mouseClick(component, button, x, y, count);
@@ -990,7 +939,10 @@ org.apache.pivot.util.Console.logMethod(
             int index = getInsertionPoint(x, y);
             if (index != -1) {
                 if (count == 2) {
-                    selectSpan(textPane, document, index);
+                    CharSpan charSpan = CharUtils.selectWord(getRowCharacters(document, index), index);
+                    if (charSpan != null) {
+                        textPane.setSelection(charSpan);
+                    }
                 } else if (count == 3) {
                     textPane.setSelection(getRowOffset(document, index), getRowLength(document, index));
                 }
@@ -1022,35 +974,45 @@ org.apache.pivot.util.Console.logMethod(
         return consumed;
     }
 
-    private int getRowOffset(Document document, int index) {
+    private Node getParagraphAt(Document document, int index) {
         if (document != null) {
             Node node = document.getDescendantAt(index);
             while (node != null && !(node instanceof Paragraph)) {
                 node = node.getParent();
             }
-            // TODO: doesn't take into account the line wrapping within a paragraph
-            if (node != null) {
-                return node.getDocumentOffset();
-            }
+            return node;
+        }
+        return null;
+    }
+
+    private int getRowOffset(Document document, int index) {
+        Node node = getParagraphAt(document, index);
+        // TODO: doesn't take into account the line wrapping within a paragraph
+        if (node != null) {
+            return node.getDocumentOffset();
         }
         return 0;
     }
 
     private int getRowLength(Document document, int index) {
-        if (document != null) {
-            Node node = document.getDescendantAt(index);
-            while (node != null && !(node instanceof Paragraph)) {
-                node = node.getParent();
-            }
-            // TODO: doesn't take into account the line wrapping within a paragraph
-            // Assuming the node is a Paragraph, the count includes the trailing \n, so discount it
-            if (node != null) {
-                return node.getCharacterCount() - 1;
-            }
+        Node node = getParagraphAt(document, index);
+        // TODO: doesn't take into account the line wrapping within a paragraph
+        // Assuming the node is a Paragraph, the count includes the trailing \n, so discount it
+        if (node != null) {
+            return node.getCharacterCount() - 1;
         }
         return 0;
     }
 
+    private CharSequence getRowCharacters(Document document, int index) {
+        Node node = getParagraphAt(document, index);
+        // TODO: doesn't take into account the line wrapping within a paragraph
+        if (node != null) {
+            return node.getCharacters();
+        }
+        return null;
+    }
+
     @Override
     public boolean keyPressed(final Component component, int keyCode,
         Keyboard.KeyLocation keyLocation) {
@@ -1062,11 +1024,9 @@ org.apache.pivot.util.Console.logMethod(
         int selectionStart = textPane.getSelectionStart();
         int selectionLength = textPane.getSelectionLength();
 
-        Keyboard.Modifier commandModifier = Platform.getCommandModifier();
-        boolean commandPressed = Keyboard.isPressed(commandModifier);
+        boolean commandPressed = Keyboard.isPressed(Platform.getCommandModifier());
         boolean wordNavPressed = Keyboard.isPressed(Platform.getWordNavigationModifier());
         boolean shiftPressed = Keyboard.isPressed(Keyboard.Modifier.SHIFT);
-        // boolean ctrlPressed = Keyboard.isPressed(Keyboard.Modifier.CTRL);
         boolean metaPressed = Keyboard.isPressed(Keyboard.Modifier.META);
         boolean isEditable = textPane.isEditable();
 
@@ -1095,12 +1055,15 @@ org.apache.pivot.util.Console.logMethod(
                 }
 
                 if (shiftPressed) {
+
+                    // TODO: if last direction was left, then extend further left
+                    // but if right, then reverse selection from the pivot point
                     selectionLength += selectionStart - start;
                 } else {
                     selectionLength = 0;
                 }
 
-                if (selectionStart >= 0) {
+                if (start >= 0) {
                     textPane.setSelection(start, selectionLength);
                     scrollCharacterToVisible(start);
 
@@ -1122,6 +1085,8 @@ org.apache.pivot.util.Console.logMethod(
                 }
 
                 if (shiftPressed) {
+                    // TODO: if last direction was right, then extend further right
+                    // but if left, then reverse selection from the pivot point
                     selectionLength += end - index;
                 } else {
                     selectionStart = end;
@@ -1135,16 +1100,11 @@ org.apache.pivot.util.Console.logMethod(
                     consumed = true;
                 }
             } else if (keyCode == Keyboard.KeyCode.LEFT) {
-                if (shiftPressed) {
-                    // TODO: undo last right select depending... see TextInput skin
-                    // Add the previous character to the selection
-                    if (selectionStart > 0) {
-                        selectionStart--;
-                        selectionLength++;
-                    }
-                } else if (wordNavPressed) {
+                if (wordNavPressed) {
                     // Move the caret to the start of the next word to our left
                     if (selectionStart > 0) {
+                        int originalStart = selectionStart;
+                        // TODO: what if last select direction was to the right?
                         // first, skip over any space immediately to our left
                         while (selectionStart > 0
                             && Character.isWhitespace(document.getCharacterAt(selectionStart - 1))) {
@@ -1156,7 +1116,18 @@ org.apache.pivot.util.Console.logMethod(
                             selectionStart--;
                         }
 
-                        selectionLength = 0;
+                        if (shiftPressed) {
+                            selectionLength += (originalStart - selectionStart);
+                        } else {
+                            selectionLength = 0;
+                        }
+                    }
+                } else if (shiftPressed) {
+                    // TODO: undo last right select depending... see TextInput skin
+                    // Add the previous character to the selection
+                    if (selectionStart > 0) {
+                        selectionStart--;
+                        selectionLength++;
                     }
                 } else {
                     // Clear the selection and move the caret back by one character

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/ComponentNode.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/ComponentNode.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/ComponentNode.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/ComponentNode.java Tue Apr  3 23:01:30 2018
@@ -16,7 +16,9 @@
  */
 package org.apache.pivot.wtk.text;
 
+import org.apache.pivot.text.CharSpan;
 import org.apache.pivot.util.ListenerList;
+import org.apache.pivot.util.Utils;
 import org.apache.pivot.wtk.Button;
 import org.apache.pivot.wtk.Component;
 import org.apache.pivot.wtk.Container;
@@ -64,57 +66,76 @@ public class ComponentNode extends Block
     }
 
     private String getText(Component comp) {
+        return getCharacters(comp).toString();
+    }
+
+    public String getSubstring(Span range) {
+        Utils.checkNull(range, "range");
+        return getText(this.component).substring(range.start, range.end + 1);
+    }
+
+    public String getSubstring(int start, int end) {
+        return getText(this.component).substring(start, end);
+    }
+
+    public String getSubstring(CharSpan charSpan) {
+        Utils.checkNull(charSpan, "charSpan");
+        return getText(this.component).substring(charSpan.start, charSpan.start + charSpan.length);
+    }
+
+    public CharSequence getCharacters(Component comp) {
         if (comp instanceof TextInput) {
-            return ((TextInput)comp).getText();
+            return ((TextInput)comp).getCharacters();
         } else if (comp instanceof TextArea) {
-            return ((TextArea)comp).getText();
+            return ((TextArea)comp).getCharacters();
         } else if (comp instanceof TextPane) {
-            return ((TextPane)comp).getText();
+            return ((TextPane)comp).getText();   // TODO: use getCharacters when it is available
         } else if (comp instanceof Label) {
             return ((Label)comp).getText();
         } else if (comp instanceof Button) {
             Object buttonData = ((Button)comp).getButtonData();
             if (buttonData instanceof BaseContent) {
                 return ((BaseContent)buttonData).getText();
-            } else if (buttonData instanceof String) {
-                return (String)buttonData;
+            } else if (buttonData instanceof CharSequence) {
+                return (CharSequence)buttonData;
             } else {
                 return buttonData.toString();
             }
         } else if (comp instanceof Container) {
             StringBuilder buf = new StringBuilder();
             for (Component child : (Container)comp) {
-                buf.append(getText(child));
+                buf.append(getCharacters(child));
             }
-            return buf.toString();
+            return buf;
         }
         return "";
     }
 
-    public String getSubstring(Span range) {
-        return getText(this.component).substring(range.start, range.end + 1);
-    }
-
-    public String getSubstring(int start, int end) {
-        return getText(this.component).substring(start, end);
+    public CharSequence getCharacters() {
+        return getCharacters(this.component);
     }
 
     public CharSequence getCharacters(Span range) {
-        return getText(this.component).subSequence(range.start, range.end + 1);
+        Utils.checkNull(range, "range");
+        return getCharacters(this.component).subSequence(range.start, range.end + 1);
     }
 
     public CharSequence getCharacters(int start, int end) {
-        return getText(this.component).subSequence(start, end);
+        return getCharacters(this.component).subSequence(start, end);
+    }
+
+    public CharSequence getCharacters(CharSpan charSpan) {
+        return getCharacters(this.component).subSequence(charSpan.start, charSpan.start + charSpan.length);
     }
 
     @Override
     public char getCharacterAt(int offset) {
-        return getText(this.component).charAt(offset);
+        return getCharacters(this.component).charAt(offset);
     }
 
     @Override
     public int getCharacterCount() {
-        return getText(this.component).length();
+        return getCharacters(this.component).length();
     }
 
     @Override

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Element.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Element.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Element.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Element.java Tue Apr  3 23:01:30 2018
@@ -18,6 +18,7 @@ package org.apache.pivot.wtk.text;
 
 import java.awt.Color;
 import java.awt.Font;
+import java.io.IOException;
 import java.util.Iterator;
 
 import org.apache.pivot.collections.ArrayList;
@@ -111,6 +112,14 @@ public abstract class Element extends No
         }
     }
 
+    private Paragraph getParagraphParent(Node node) {
+        Element parent = node.getParent();
+        while ((parent != null) && !(parent instanceof Paragraph)) {
+            parent = parent.getParent();
+        }
+        return (Paragraph)parent;
+    }
+
     @Override
     public Node removeRange(int offset, int charCount) {
         Utils.checkIndexBounds(offset, charCount, 0, characterCount);
@@ -121,8 +130,9 @@ public abstract class Element extends No
         if (charCount > 0) {
             Element element = (Element) range;
 
+            int endOffset = offset + charCount - 1;
             int start = getNodeAt(offset);
-            int end = getNodeAt(offset + charCount - 1);
+            int end = (endOffset == offset) ? start : getNodeAt(endOffset);
 
             if (start == end) {
                 // The range is entirely contained by one child node
@@ -136,8 +146,9 @@ public abstract class Element extends No
                     // underneath a paragraph to allow for composed text on an
                     // empty line, so check that condition and don't remove the
                     // entire node.
-                    if (node instanceof TextNode && node.getParent() instanceof Paragraph &&
-                        node.getParent().getLength() == 1) {
+                    Paragraph paragraph;
+                    if (node instanceof TextNode && (paragraph = getParagraphParent(node)) != null &&
+                        paragraph.getLength() == 1) {
                         segment = node.removeRange(0, charCount);
                     } else {
                         // Remove the entire node
@@ -205,8 +216,9 @@ public abstract class Element extends No
 
         if (charCount > 0) {
 
+            int endOffset = offset + charCount - 1;
             int start = getNodeAt(offset);
-            int end = getNodeAt(offset + charCount - 1);
+            int end = (endOffset == offset) ? start : getNodeAt(endOffset);
 
             if (start == end) {
                 // The range is entirely contained by one child node
@@ -274,27 +286,31 @@ public abstract class Element extends No
         return characterCount;
     }
 
-    private void addText(StringBuilder buf, Element element) {
-        for (Node child : element.nodes) {
-            if (child instanceof TextNode) {
-                TextNode textNode = (TextNode)child;
-                buf.append(textNode.getText());
-            } else if (child instanceof ComponentNode) {
-                ComponentNode compNode = (ComponentNode)child;
-                buf.append(compNode.getText());
-            } else if (child instanceof Paragraph) {
-                addText(buf, (Element)child);
+    private void addCharacters(Appendable buf, Element element) {
+        try {
+            for (Node child : element.nodes) {
+                if (child instanceof Element) {
+                    addCharacters(buf, (Element)child);
+                } else {
+                    buf.append(child.getCharacters());
+                }
+            }
+            if (element instanceof Paragraph) {
                 buf.append('\n');
-            } else if (child instanceof Element) {
-                addText(buf, (Element)child);
             }
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
         }
     }
 
     public String getText() {
+        return getCharacters().toString();
+    }
+
+    public CharSequence getCharacters() {
         StringBuilder buf = new StringBuilder(characterCount);
-        addText(buf, this);
-        return buf.toString();
+        addCharacters(buf, this);
+        return buf;
     }
 
     @Override
@@ -511,8 +527,6 @@ public abstract class Element extends No
 
     @Override
     protected void rangeRemoved(Node originalNode, int offset, int charCount, CharSequence removedChars) {
-        this.characterCount -= charCount;
-
         // Update the offsets of consecutive nodes, if any
         if (offset < this.characterCount) {
             int index = getNodeAt(offset);
@@ -522,6 +536,8 @@ public abstract class Element extends No
                 node.setOffset(node.getOffset() - charCount);
             }
         }
+        // Adjust our count last, so we make sure to get all our children updated
+        this.characterCount -= charCount;
 
         super.rangeRemoved(originalNode, offset, charCount, removedChars);
     }

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Node.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Node.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Node.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/Node.java Tue Apr  3 23:01:30 2018
@@ -78,7 +78,7 @@ public abstract class Node {
      * offset of our parent (if any) added to our own offset.
      */
     public int getDocumentOffset() {
-        return (parent == null) ? 0 : parent.getDocumentOffset() + offset;
+        return ((parent == null) ? 0 : parent.getDocumentOffset()) + offset;
     }
 
     /**
@@ -150,6 +150,11 @@ public abstract class Node {
     public abstract int getCharacterCount();
 
     /**
+     * @return The character sequence in this node.
+     */
+    public abstract CharSequence getCharacters();
+
+    /**
      * Creates a copy of this node.
      *
      * @param recursive Whether to duplicate the children also.

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/text/TextNode.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/text/TextNode.java?rev=1828291&r1=1828290&r2=1828291&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/text/TextNode.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/text/TextNode.java Tue Apr  3 23:01:30 2018
@@ -16,6 +16,7 @@
  */
 package org.apache.pivot.wtk.text;
 
+import org.apache.pivot.text.CharSpan;
 import org.apache.pivot.util.ListenerList;
 import org.apache.pivot.util.Utils;
 import org.apache.pivot.wtk.Span;
@@ -49,6 +50,18 @@ public final class TextNode extends Node
         return characters.substring(beginIndex, endIndex);
     }
 
+    public String getText(Span span) {
+        Utils.checkNull(span, "span");
+
+        return characters.substring(span.normalStart(), span.normalEnd() + 1);
+    }
+
+    public String getText(CharSpan charSpan) {
+        Utils.checkNull(charSpan, "charSpan");
+
+        return characters.substring(charSpan.start, charSpan.start + charSpan.length);
+    }
+
     public void setText(String text) {
         Utils.checkNull(text, "text");
 
@@ -94,6 +107,7 @@ public final class TextNode extends Node
     }
 
     public String getSubstring(Span range) {
+        Utils.checkNull(range, "range");
         return characters.substring(range.start, range.end + 1);
     }
 
@@ -101,6 +115,11 @@ public final class TextNode extends Node
         return characters.substring(start, end);
     }
 
+    public String getSubstring(CharSpan charSpan) {
+        Utils.checkNull(charSpan, "charSpan");
+        return characters.substring(charSpan.start, charSpan.start + charSpan.length);
+    }
+
     public CharSequence getCharacters() {
         return characters;
     }
@@ -110,9 +129,15 @@ public final class TextNode extends Node
     }
 
     public CharSequence getCharacters(Span range) {
+        Utils.checkNull(range, "range");
         return characters.subSequence(range.start, range.end + 1);
     }
 
+    public CharSequence getCharacters(CharSpan charSpan) {
+        Utils.checkNull(charSpan, "charSpan");
+        return characters.subSequence(charSpan.start, charSpan.start + charSpan.length);
+    }
+
     @Override
     public char getCharacterAt(int index) {
         return characters.charAt(index);