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 2016/03/15 02:33:11 UTC

svn commit: r1735007 - /pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java

Author: rwhitcomb
Date: Tue Mar 15 01:33:10 2016
New Revision: 1735007

URL: http://svn.apache.org/viewvc?rev=1735007&view=rev
Log:
Misc. TextPane improvements:
* Add some Javadoc, mostly pertaining to the offsets and whether they
  are document-relative or block-relative.
* Notably add the "getText(start, end)" method so TextPane can (almost
  completely) be used as a drop-in replacement with added functionality
  for a TextArea control.
* Add some error messages to the IllegalStateExceptions that are thrown
  everywhere.

Modified:
    pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.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=1735007&r1=1735006&r2=1735007&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/TextPane.java Tue Mar 15 01:33:10 2016
@@ -28,11 +28,14 @@ import org.apache.pivot.beans.DefaultPro
 import org.apache.pivot.collections.LinkedList;
 import org.apache.pivot.collections.Sequence;
 import org.apache.pivot.util.ListenerList;
+import org.apache.pivot.wtk.Span;
 import org.apache.pivot.wtk.media.Image;
+import org.apache.pivot.wtk.text.Block;
 import org.apache.pivot.wtk.text.ComponentNode;
 import org.apache.pivot.wtk.text.ComponentNodeListener;
 import org.apache.pivot.wtk.text.Document;
 import org.apache.pivot.wtk.text.Element;
+import org.apache.pivot.wtk.text.ImageNode;
 import org.apache.pivot.wtk.text.Node;
 import org.apache.pivot.wtk.text.NodeListener;
 import org.apache.pivot.wtk.text.Paragraph;
@@ -178,6 +181,9 @@ public class TextPane extends Container
 
     private static class TextPaneCharacterListenerList extends
         WTKListenerList<TextPaneCharacterListener> implements TextPaneCharacterListener {
+        /**
+         * @param index Index into the whole document.
+         */
         @Override
         public void charactersInserted(TextPane textPane, int index, int count) {
             for (TextPaneCharacterListener listener : this) {
@@ -185,6 +191,9 @@ public class TextPane extends Container
             }
         }
 
+        /**
+         * @param index Index into the whole document.
+         */
         @Override
         public void charactersRemoved(TextPane textPane, int index, int count) {
             for (TextPaneCharacterListener listener : this) {
@@ -225,6 +234,9 @@ public class TextPane extends Container
     };
 
     private NodeListener documentListener = new NodeListener.Adapter() {
+        /**
+         * @param offset Offset into the document.
+         */
         @Override
         public void rangeInserted(Node node, int offset, int characterCount) {
             if (selectionStart + selectionLength > offset) {
@@ -244,9 +256,11 @@ public class TextPane extends Container
             }
         }
 
+        /**
+         * @param offset Offset into the document.
+         */
         @Override
         public void nodesRemoved(Node node, Sequence<Node> removed, int offset) {
-
             for (int i = 0; i < removed.getLength(); i++) {
                 Node descendant = removed.get(i);
                 if (descendant instanceof ComponentNode) {
@@ -261,6 +275,9 @@ public class TextPane extends Container
             }
         }
 
+        /**
+         * @param offset Offset into the document.
+         */
         @Override
         public void nodeInserted(Node node, int offset) {
             Node descendant = document.getDescendantAt(offset);
@@ -271,6 +288,9 @@ public class TextPane extends Container
             }
         }
 
+        /**
+         * @param offset Offset into the document.
+         */
         @Override
         public void rangeRemoved(Node node, int offset, int characterCount) {
             // if the end of the selection is in or after the range removed
@@ -425,7 +445,7 @@ public class TextPane extends Container
         }
 
         if (document == null) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null.");
         }
 
         if (document.getCharacterCount() == 0) {
@@ -478,7 +498,44 @@ public class TextPane extends Container
         }
 
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
+        }
+
+        if (selectionLength > 0) {
+            removeDocumentRange(selectionStart, selectionLength);
+        }
+
+        // TODO If the caret is placed in the middle of a text node, split it;
+        // otherwise, insert an ImageNode immediately following the block
+        // containing the caret
+
+        // If the insertion is at the end of the document, then just add
+        if (selectionStart >= document.getCharacterCount() - 1) {
+            document.add(new ImageNode(image));
+        } else {
+            // Walk up the tree until we find a block
+            Node descendant = document.getDescendantAt(selectionStart);
+            while (!(descendant instanceof Block)) {
+                descendant = descendant.getParent();
+            }
+            Element parent = descendant.getParent();
+            if (parent != null) {
+                int index = parent.indexOf(descendant);
+                parent.insert(new ImageNode(image), index + 1);
+            }
+        }
+
+        // Set the selection start to the character following the insertion
+        setSelection(selectionStart + 1, selectionLength);
+    }
+
+    public void insertComponent(Component component) {
+        if (component == null) {
+            throw new IllegalArgumentException("component is null.");
+        }
+
+        if (document == null || document.getCharacterCount() == 0) {
+            throw new IllegalStateException("document is null or empty.");
         }
 
         if (selectionLength > 0) {
@@ -486,16 +543,32 @@ public class TextPane extends Container
         }
 
         // TODO If the caret is placed in the middle of a text node, split it;
-        // otherwise, insert an ImageNode immediately following the node
+        // otherwise, insert a ComponentNode immediately following the block
         // containing the caret
 
+        // If the insertion is at the end of the document, then just add
+        if (selectionStart >= document.getCharacterCount() - 1) {
+            document.add(new ComponentNode(component));
+        } else {
+            // Walk up the tree until we find a block
+            Node descendant = document.getDescendantAt(selectionStart);
+            while (!(descendant instanceof Block)) {
+                descendant = descendant.getParent();
+            }
+            Element parent = descendant.getParent();
+            if (parent != null) {
+                int index = parent.indexOf(descendant);
+                parent.insert(new ComponentNode(component), index + 1);
+            }
+        }
+
         // Set the selection start to the character following the insertion
         setSelection(selectionStart + 1, selectionLength);
     }
 
     public void insertParagraph() {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         if (selectionLength > 0) {
@@ -547,7 +620,7 @@ public class TextPane extends Container
 
     public void removeText(int offset, int characterCount) {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         if (offset >= 0 && offset < document.getCharacterCount()) {
@@ -588,7 +661,7 @@ public class TextPane extends Container
 
     public void cut() {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         if (selectionLength > 0) {
@@ -617,7 +690,7 @@ public class TextPane extends Container
 
     public void copy() {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         String selectedText = getSelectedText();
@@ -631,7 +704,7 @@ public class TextPane extends Container
 
     public void paste() {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         Manifest clipboardContent = Clipboard.getContent();
@@ -701,19 +774,42 @@ public class TextPane extends Container
         // TODO
     }
 
-    private void addToText(StringBuilder text, Element element) {
-        for (Node node : element) {
-            if (node instanceof TextNode) {
-                text.append(((TextNode) node).getCharacters());
-            } else if (node instanceof ComponentNode) {
-                text.append(((ComponentNode) node).getText());
-            } else if (node instanceof Element) {
-                addToText(text, (Element) node);
+    /**
+     * Add the text from the given element (and its children) to the given buffer,
+     * respecting the range of characters to be included.
+     * @param text The buffer we're building.
+     * @param element The current element in the document.
+     * @param includeSpan The range of text to be included (in document-relative
+     * coordinates).
+     */
+    private void addToText(StringBuilder text, Element element, Span includeSpan) {
+        Span elementSpan = element.getDocumentSpan();
+        Span elementIntersection = elementSpan.intersect(includeSpan);
+        if (elementIntersection != null) {
+            for (Node node : element) {
+                if (node instanceof Element) {
+                    addToText(text, (Element) node, includeSpan);
+                }
+                else {
+                    Span nodeSpan = node.getDocumentSpan();
+                    Span nodeIntersection = nodeSpan.intersect(includeSpan);
+                    if (nodeIntersection != null) {
+                        Span currentSpan = nodeIntersection.offset(-nodeSpan.start);
+                        if (node instanceof TextNode) {
+                            text.append(((TextNode) node).getCharacters(currentSpan));
+                        } else if (node instanceof ComponentNode) {
+                            text.append(((ComponentNode) node).getCharacters(currentSpan));
+                        }
+                        // TODO: anything more that could/should be handled?
+                        // lists for instance???
+                    }
+                }
+            }
+            if (element instanceof Paragraph && elementIntersection.end == elementSpan.end) {
+                // TODO: unclear if this is included in the character count for a paragraph or not
+                // or what that means for the intersection range above
+                text.append('\n');
             }
-            // TODO: anything more that could/should be handled?
-        }
-        if (element instanceof Paragraph) {
-            text.append('\n');
         }
     }
 
@@ -725,15 +821,45 @@ public class TextPane extends Container
      */
     public String getText() {
         Document doc = getDocument();
-        if (doc != null && getCharacterCount() != 0) {
-            StringBuilder text = new StringBuilder(getCharacterCount());
-            addToText(text, doc);
+        int count;
+        if (doc != null && (count = getCharacterCount()) != 0) {
+            StringBuilder text = new StringBuilder(count);
+            addToText(text, doc, new Span(0, count - 1));
             return text.toString();
         }
         return null;
     }
 
     /**
+     * Convenience method to get a portion of the document text into a single string.
+     *
+     * @param beginIndex The 0-based offset where to start retrieving text.
+     * @param endIndex The ending offset + 1 of the text to retrieve.
+     * @return The specified portion of the document text if there is any, or
+     * {@code null} if there is no document.
+     */
+    public String getText(int beginIndex, int endIndex) {
+        if (beginIndex > endIndex) {
+            throw new IllegalArgumentException();
+        }
+
+        if (beginIndex < 0 || endIndex > getCharacterCount()) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        int count = endIndex - beginIndex;
+        if (count == 0) {
+            return "";
+        }
+        Document doc = getDocument();
+        if (doc != null) {
+            StringBuilder text = new StringBuilder(count);
+            addToText(text, doc, new Span(beginIndex, endIndex - 1));
+            return text.toString();
+        }
+        return null;
+    }
+    /**
      * Convenience method to create a text-only document consisting of one
      * paragraph per line of the given text.
      *
@@ -756,20 +882,14 @@ public class TextPane extends Container
             throw new IllegalArgumentException();
         }
 
-        InputStream inputStream = null;
-        try {
-            inputStream = textURL.openStream();
+        try (InputStream inputStream = textURL.openStream()) {
             setText(new InputStreamReader(inputStream));
-        } finally {
-            if (inputStream != null) {
-                inputStream.close();
-            }
         }
     }
 
     public void setText(Reader textReader) throws IOException {
         if (textReader == null) {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("Reader is null");
         }
 
         int tabPosition = 0;
@@ -849,7 +969,7 @@ public class TextPane extends Container
      */
     public void setSelection(int selectionStart, int selectionLength) {
         if (document == null || document.getCharacterCount() == 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null or empty.");
         }
 
         if (selectionLength < 0) {
@@ -896,7 +1016,7 @@ public class TextPane extends Container
      */
     public void selectAll() {
         if (document == null) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("document is null.");
         }
 
         setSelection(0, document.getCharacterCount());