You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2009/03/16 19:43:07 UTC

svn commit: r754974 - in /incubator/pivot/trunk: tutorials/src/pivot/tutorials/stocktracker/ tutorials/src/pivot/tutorials/text/ wtk/src/pivot/wtk/ wtk/src/pivot/wtk/skin/terra/ wtk/src/pivot/wtk/text/

Author: gbrown
Date: Mon Mar 16 18:43:06 2009
New Revision: 754974

URL: http://svn.apache.org/viewvc?rev=754974&view=rev
Log:
Add methods to BrowserApplicationContext to facilitate Pivot/DOM communication; update TextInput APIs for consistency with newer TextArea APIs; ensure that both TextInput and TextArea fire controller events when model data changes.

Added:
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaCharacterListener.java
Modified:
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/stocktracker/StockTracker.java
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/text/Text.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/BrowserApplicationContext.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextArea.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaListener.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaSelectionListener.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextInput.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputCharacterListener.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputListener.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/text/Node.java

Modified: incubator/pivot/trunk/tutorials/src/pivot/tutorials/stocktracker/StockTracker.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/stocktracker/StockTracker.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/stocktracker/StockTracker.java (original)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/stocktracker/StockTracker.java Mon Mar 16 18:43:06 2009
@@ -50,6 +50,7 @@
 import pivot.wtk.TextInput;
 import pivot.wtk.TextInputTextListener;
 import pivot.wtk.Window;
+import pivot.wtk.text.TextNode;
 import pivot.wtkx.WTKXSerializer;
 
 public class StockTracker implements Application {
@@ -141,7 +142,8 @@
         symbolTextInput = (TextInput)wtkxSerializer.getObjectByName("symbolTextInput");
         symbolTextInput.getTextInputTextListeners().add(new TextInputTextListener() {
             public void textChanged(TextInput textInput) {
-                addSymbolButton.setEnabled(textInput.getCharacterCount() > 0);
+                TextNode textNode = textInput.getTextNode();
+                addSymbolButton.setEnabled(textNode.getCharacterCount() > 0);
             }
         });
 

Modified: incubator/pivot/trunk/tutorials/src/pivot/tutorials/text/Text.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/text/Text.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/text/Text.java (original)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/text/Text.java Mon Mar 16 18:43:06 2009
@@ -96,10 +96,8 @@
         TextInput stateTextInput =
             (TextInput)wtkxSerializer.getObjectByName("stateTextInput");
 
-        stateTextInput.getTextInputCharacterListeners().add(new
-            TextInputCharacterListener() {
-            public void charactersInserted(TextInput textInput,
-                int index, int count) {
+        stateTextInput.getTextInputCharacterListeners().add(new TextInputCharacterListener() {
+            public void charactersInserted(TextInput textInput, int index, int count) {
                 String text = textInput.getText();
 
                 int i = Sequence.Search.binarySearch(states, text,
@@ -127,11 +125,7 @@
                 }
             }
 
-            public void charactersRemoved(TextInput textInput, int index,
-                String characters) {
-            }
-
-            public void charactersReset(TextInput textInput) {
+            public void charactersRemoved(TextInput textInput, int index, int count) {
             }
         });
 

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/BrowserApplicationContext.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/BrowserApplicationContext.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/BrowserApplicationContext.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/BrowserApplicationContext.java Mon Mar 16 18:43:06 2009
@@ -21,6 +21,8 @@
 import java.net.URL;
 import java.net.URLDecoder;
 
+import netscape.javascript.JSObject;
+
 import pivot.collections.Dictionary;
 import pivot.collections.HashMap;
 
@@ -184,6 +186,14 @@
 
         private static final long serialVersionUID = 0;
 
+        public HostApplet() {
+            hostApplet = this;
+        }
+
+        public Application getApplication() {
+            return application;
+        }
+
         @Override
         public void init() {
             InitCallback initCallback = new InitCallback();
@@ -233,4 +243,22 @@
             paint(graphics);
         }
     }
+
+    private static HostApplet hostApplet = null;
+
+    /**
+     * Evaluates the specified script in the browser's JavaScript page
+     * context and returns the result.
+     * <p>
+     * NOTE This feature requires that the applet run in its own JVM; see JDK
+     * documentation on the <tt>separate_jvm</tt> applet parameter.
+     */
+    public static Object eval(String script) {
+        try {
+            JSObject window = JSObject.getWindow(hostApplet);
+            return window.eval(script);
+        } catch (Throwable throwable) {
+            throw new UnsupportedOperationException(throwable);
+        }
+    }
 }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextArea.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextArea.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextArea.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextArea.java Mon Mar 16 18:43:06 2009
@@ -90,6 +90,21 @@
         }
     }
 
+    private static class TextAreaCharacterListenerList extends ListenerList<TextAreaCharacterListener>
+        implements TextAreaCharacterListener {
+        public void charactersInserted(TextArea textArea, int index, int count) {
+            for (TextAreaCharacterListener listener : this) {
+                listener.charactersInserted(textArea, index, count);
+            }
+        }
+
+        public void charactersRemoved(TextArea textArea, int index, int count) {
+            for (TextAreaCharacterListener listener : this) {
+                listener.charactersRemoved(textArea, index, count);
+            }
+        }
+    }
+
     private static class TextAreaSelectionListenerList extends ListenerList<TextAreaSelectionListener>
         implements TextAreaSelectionListener {
         public void selectionChanged(TextArea textArea,
@@ -115,9 +130,6 @@
         }
 
         public void rangeInserted(Node node, int offset, int characterCount) {
-            int previousSelectionStart = selectionStart;
-            int previousSelectionLength = selectionLength;
-
             if (selectionStart + selectionLength > offset) {
                 if (selectionStart > offset) {
                     selectionStart += characterCount;
@@ -126,17 +138,10 @@
                 }
             }
 
-            if (previousSelectionStart != selectionStart
-                || previousSelectionLength != selectionLength) {
-                textAreaSelectionListeners.selectionChanged(TextArea.this,
-                    previousSelectionStart, previousSelectionLength);
-            }
+            textAreaCharacterListeners.charactersInserted(TextArea.this, offset, characterCount);
         }
 
         public void rangeRemoved(Node node, int offset, int characterCount) {
-            int previousSelectionStart = selectionStart;
-            int previousSelectionLength = selectionLength;
-
             if (selectionStart + selectionLength > offset) {
                 if (selectionStart > offset) {
                     selectionStart -= characterCount;
@@ -145,15 +150,12 @@
                 }
             }
 
-            if (previousSelectionStart != selectionStart
-                || previousSelectionLength != selectionLength) {
-                textAreaSelectionListeners.selectionChanged(TextArea.this,
-                    previousSelectionStart, previousSelectionLength);
-            }
+            textAreaCharacterListeners.charactersRemoved(TextArea.this, offset, characterCount);
         }
     };
 
     private TextAreaListenerList textAreaListeners = new TextAreaListenerList();
+    private TextAreaCharacterListenerList textAreaCharacterListeners = new TextAreaCharacterListenerList();
     private TextAreaSelectionListenerList textAreaSelectionListeners = new TextAreaSelectionListenerList();
 
     public TextArea() {
@@ -240,8 +242,6 @@
 
     /**
      * Returns the text area's editable flag.
-     *
-     * @return
      */
     public boolean isEditable() {
         return editable;
@@ -251,8 +251,6 @@
      * Sets the text area's editable flag.
      *
      * @param editable
-     *
-     * @return
      */
     public void setEditable(boolean editable) {
         if (this.editable != editable) {
@@ -565,6 +563,10 @@
         return textAreaListeners;
     }
 
+    public ListenerList<TextAreaCharacterListener> getTextAreaCharacterListeners() {
+        return textAreaCharacterListeners;
+    }
+
     public ListenerList<TextAreaSelectionListener> getTextAreaSelectionListeners() {
         return textAreaSelectionListeners;
     }

Added: incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaCharacterListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaCharacterListener.java?rev=754974&view=auto
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaCharacterListener.java (added)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaCharacterListener.java Mon Mar 16 18:43:06 2009
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk;
+
+/**
+ * Text area character listener interface.
+ *
+ * @author gbrown
+ */
+public interface TextAreaCharacterListener {
+    /**
+     * Called when characters have been inserted into a text area.
+     *
+     * @param textArea
+     * @param index
+     * @param count
+     */
+    public void charactersInserted(TextArea textArea, int index, int count);
+
+    /**
+     * Called when characters have been removed from a text area.
+     *
+     * @param textArea
+     * @param index
+     * @param count
+     */
+    public void charactersRemoved(TextArea textArea, int index, int count);
+}

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaListener.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaListener.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaListener.java Mon Mar 16 18:43:06 2009
@@ -41,7 +41,7 @@
     /**
      * Called when a text area's text key has changed.
      *
-     * @param textInput
+     * @param textArea
      * @param previousTextKey
      */
     public void textKeyChanged(TextArea textArea, String previousTextKey);

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaSelectionListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaSelectionListener.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaSelectionListener.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextAreaSelectionListener.java Mon Mar 16 18:43:06 2009
@@ -24,7 +24,7 @@
     /**
      * Called when a text area's selection state has changed.
      *
-     * @param textInput
+     * @param textArea
      * @param previousSelectionStart
      * @param previousSelectionLength
      */

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextInput.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextInput.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextInput.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextInput.java Mon Mar 16 18:43:06 2009
@@ -15,8 +15,14 @@
  */
 package pivot.wtk;
 
+import java.io.IOException;
+
 import pivot.collections.Dictionary;
 import pivot.util.ListenerList;
+import pivot.wtk.text.Element;
+import pivot.wtk.text.Node;
+import pivot.wtk.text.NodeListener;
+import pivot.wtk.text.TextNode;
 
 /**
  * A component that allows a user to enter a single line of unformatted text.
@@ -31,6 +37,11 @@
      */
     private static class TextInputListenerList extends ListenerList<TextInputListener>
         implements TextInputListener {
+        public void textNodeChanged(TextInput textInput, TextNode previousTextNode) {
+            for (TextInputListener listener : this) {
+                listener.textNodeChanged(textInput, previousTextNode);
+            }
+        }
         public void textSizeChanged(TextInput textInput, int previousTextSize) {
             for (TextInputListener listener : this) {
                 listener.textSizeChanged(textInput, previousTextSize);
@@ -89,15 +100,9 @@
             }
         }
 
-        public void charactersRemoved(TextInput textInput, int index, String characters) {
-            for (TextInputCharacterListener listener : this) {
-                listener.charactersRemoved(textInput, index, characters);
-            }
-        }
-
-        public void charactersReset(TextInput textInput) {
+        public void charactersRemoved(TextInput textInput, int index, int count) {
             for (TextInputCharacterListener listener : this) {
-                listener.charactersReset(textInput);
+                listener.charactersRemoved(textInput, index, count);
             }
         }
     }
@@ -118,7 +123,8 @@
         }
     }
 
-    private StringBuilder textBuilder = new StringBuilder();
+    private TextNode textNode = null;
+
     private int selectionStart = 0;
     private int selectionLength = 0;
     private int textSize = DEFAULT_TEXT_SIZE;
@@ -127,6 +133,40 @@
     private String prompt = null;
     private String textKey = null;
 
+    private NodeListener textNodeListener = new NodeListener() {
+        public void parentChanged(Node node, Element previousParent) {
+        }
+
+        public void offsetChanged(Node node, int previousOffset) {
+        }
+
+        public void rangeInserted(Node node, int offset, int characterCount) {
+            if (selectionStart + selectionLength > offset) {
+                if (selectionStart > offset) {
+                    selectionStart += characterCount;
+                } else {
+                    selectionLength += characterCount;
+                }
+            }
+
+            textInputCharacterListeners.charactersInserted(TextInput.this, offset, characterCount);
+            textInputTextListeners.textChanged(TextInput.this);
+        }
+
+        public void rangeRemoved(Node node, int offset, int characterCount) {
+            if (selectionStart + selectionLength > offset) {
+                if (selectionStart > offset) {
+                    selectionStart -= characterCount;
+                } else {
+                    selectionLength -= characterCount;
+                }
+            }
+
+            textInputCharacterListeners.charactersRemoved(TextInput.this, offset, characterCount);
+            textInputTextListeners.textChanged(TextInput.this);
+        }
+    };
+
     private TextInputListenerList textInputListeners = new TextInputListenerList();
     private TextInputTextListenerList textInputTextListeners = new TextInputTextListenerList();
     private TextInputCharacterListenerList textInputCharacterListeners = new TextInputCharacterListenerList();
@@ -134,55 +174,56 @@
 
     private static final int DEFAULT_TEXT_SIZE = 20;
 
-    /**
-     * Creates a text input that is initially empty.
-     */
     public TextInput() {
-        this("");
+        setTextNode(new TextNode());
+        installSkin(TextInput.class);
     }
 
-    /**
-     * Creates a text input that is initialized with the given text.
-     *
-     * @param text
-     * The initial text content of the text input.
-     */
-    public TextInput(String text) {
-        setText(text);
-        installSkin(TextInput.class);
+    public TextNode getTextNode() {
+        return textNode;
+    }
+
+    public void setTextNode(TextNode textNode) {
+        if (textNode == null) {
+            throw new IllegalArgumentException("textNode is null.");
+        }
+
+        if (textNode.getCharacterCount() > maximumLength) {
+            throw new IllegalArgumentException("Text length is greater than maximum length.");
+        }
+
+        TextNode previousTextNode = this.textNode;
+
+        if (previousTextNode != textNode) {
+            if (previousTextNode != null) {
+                previousTextNode.getNodeListeners().remove(textNodeListener);
+            }
+
+            if (textNode != null) {
+                textNode.getNodeListeners().add(textNodeListener);
+            }
+
+            // Clear the selection
+            selectionStart = 0;
+            selectionLength = 0;
+
+            this.textNode = textNode;
+
+            textInputListeners.textNodeChanged(this, previousTextNode);
+            textInputTextListeners.textChanged(this);
+        }
     }
 
-    /**
-     * Returns the text content of the text input.
-     *
-     * @return
-     * A new string containing a copy of the text input's content.
-     */
     public String getText() {
-        return textBuilder.toString();
+        return textNode.getText();
     }
 
-    /**
-     * Sets the text content of the text input. Clears any existing selection.
-     *
-     * param text
-     * The new content of the text input.
-     */
     public void setText(String text) {
         if (text == null) {
             throw new IllegalArgumentException("text is null.");
         }
 
-        if (text.length() > maximumLength) {
-            throw new IllegalArgumentException("text length is greater than maximum length.");
-        }
-
-        setSelection(0, 0);
-
-        textBuilder = new StringBuilder(text);
-
-        textInputCharacterListeners.charactersReset(this);
-        textInputTextListeners.textChanged(this);
+        setTextNode(new TextNode(text));
     }
 
     /**
@@ -213,121 +254,108 @@
      */
     public void insertText(String text, int index) {
         if (index < 0
-            || index > textBuilder.length()) {
+            || index > textNode.getCharacterCount()) {
             throw new IndexOutOfBoundsException();
         }
 
-        if (textBuilder.length() + text.length() > maximumLength) {
+        if (textNode.getCharacterCount() + text.length() > maximumLength) {
             throw new IllegalArgumentException("Insertion of text would exceed maximum length.");
         }
 
-        // Insert the text
-        textBuilder.insert(index, text);
-
-        // Update the selection
-        int previousSelectionStart = selectionStart;
-        int previousSelectionLength = selectionLength;
-
-        int count = text.length();
-
-        if (selectionStart + selectionLength >= index) {
-            if (selectionStart >= index) {
-                selectionStart += count;
-            } else {
-                selectionLength += count;
-            }
+        if (selectionLength > 0) {
+            // TODO Make this part of the undoable action (for all such
+            // actions)
+            textNode.removeRange(selectionStart, selectionLength);
         }
 
-        // Notify listeners
-        textInputCharacterListeners.charactersInserted(this, index, count);
-        textInputTextListeners.textChanged(this);
-
-        if (previousSelectionStart != selectionStart
-            || previousSelectionLength != selectionLength) {
-            textInputSelectionListeners.selectionChanged(this,
-                previousSelectionStart, previousSelectionLength);
-        }
+        // Insert the text and update the selection
+        textNode.insertText(text, index);
+        setSelection(selectionStart + text.length(), selectionLength);
     }
 
-    /**
-     * Removes a range of characters from the text input's content.
-     *
-     * @param index
-     * The index of the first character to remove.
-     *
-     * @param count
-     * The number of characters to remove.
-     *
-     * @return
-     * A string containing the text that was removed.
-     */
-    public String removeText(int index, int count) {
-        if (index < 0
-            || index + count > textBuilder.length()) {
-            throw new IndexOutOfBoundsException();
+    public void delete(Direction direction) {
+        if (direction == null) {
+            throw new IllegalArgumentException("direction is null.");
         }
 
-        // Determine the range of indexes to remove; the interval is defined
-        // as [start, end)
-        int start = index;
-        int end = index + count;
-
-        String removed = textBuilder.substring(start, end);
-        textBuilder.delete(start, end);
+        if (selectionLength > 0) {
+            // TODO Make this part of the undoable action (for all such
+            // actions)
+            textNode.removeRange(selectionStart, selectionLength);
+        } else {
+            int offset = selectionStart;
 
-        // Update selection
-        int previousSelectionStart = selectionStart;
-        int previousSelectionLength = selectionLength;
+            if (direction == Direction.BACKWARD) {
+                offset--;
+            }
 
-        // The selection interval is defined as [selectionStart, selectionEnd]
-        int selectionEnd = selectionStart + selectionLength - 1;
+            if (offset >= 0
+                && offset < textNode.getCharacterCount()) {
+                textNode.removeRange(offset, 1);
+            }
+        }
+    }
 
-        if (selectionEnd >= start) {
-            selectionStart = Math.min(start, selectionStart);
-            selectionEnd = Math.max(end - 1, selectionEnd) - count;
+    public void cut() {
+        // Delete any selected text and put it on the clipboard
+        if (selectionLength > 0) {
+            TextNode removedRange =
+                (TextNode)textNode.removeRange(selectionStart, selectionLength);
 
-            selectionLength = selectionEnd - selectionStart + 1;
+            LocalManifest clipboardContent = new LocalManifest();
+            clipboardContent.putText(removedRange.getText());
+            Clipboard.setContent(clipboardContent);
         }
+    }
 
-        textInputCharacterListeners.charactersRemoved(this, index, removed);
-        textInputTextListeners.textChanged(this);
+    public void copy() {
+        // Copy selection to clipboard
+        String selectedText = getSelectedText();
 
-        if (previousSelectionStart != selectionStart
-            || previousSelectionLength != selectionLength) {
-            textInputSelectionListeners.selectionChanged(this,
-                previousSelectionStart, previousSelectionLength);
+        if (selectedText != null) {
+            LocalManifest clipboardContent = new LocalManifest();
+            clipboardContent.putText(selectedText);
+            Clipboard.setContent(clipboardContent);
         }
-
-        return removed;
     }
 
-    /**
-     * Returns the character at the given index.
-     *
-     * @param index
-     * The index of the character to return.
-     *
-     * @return
-     * The character at the given index.
-     */
-    public char getCharacter(int index) {
-        if (index < 0
-            || index >= textBuilder.length()) {
-            throw new IndexOutOfBoundsException();
+    public void paste() {
+        Manifest clipboardContent = Clipboard.getContent();
+
+        if (clipboardContent != null
+            && clipboardContent.containsText()) {
+            // Paste the string representation of the content
+            String text = null;
+            try {
+                text = clipboardContent.getText();
+            } catch(IOException exception) {
+                // No-op
+            }
+
+            if (text != null) {
+                if ((text.length() + textNode.getCharacterCount()) > maximumLength) {
+                    ApplicationContext.beep();
+                } else {
+                    // Remove any existing selection
+                    if (selectionLength > 0) {
+                        // TODO Make this part of the undoable action (for all such
+                        // actions)
+                        textNode.removeRange(selectionStart, selectionLength);
+                    }
+
+                    // Insert the clipboard contents
+                    insertText(text, selectionStart);
+                }
+            }
         }
+    }
 
-        return textBuilder.charAt(index);
+    public void undo() {
+        // TODO
     }
 
-    /**
-     * Returns the total number of characters in the text input's text
-     * content.
-     *
-     * @return
-     * The length of the text input's content.
-     */
-    public int getCharacterCount() {
-        return textBuilder.length();
+    public void redo() {
+        // TODO
     }
 
     /**
@@ -366,7 +394,7 @@
         }
 
         if (selectionStart < 0
-            || selectionStart + selectionLength > textBuilder.length()) {
+            || selectionStart + selectionLength > textNode.getCharacterCount()) {
             throw new IndexOutOfBoundsException();
         }
 
@@ -406,8 +434,9 @@
         String selectedText = null;
 
         if (selectionLength > 0) {
-            selectedText = textBuilder.substring(selectionStart,
+            TextNode selectedRange = (TextNode)textNode.getRange(selectionStart,
                 selectionStart + selectionLength);
+            selectedText = selectedRange.getText();
         }
 
         return selectedText;
@@ -467,8 +496,9 @@
 
         if (previousMaximumLength != maximumLength) {
             // Truncate the text, if necessary
-            if (textBuilder.length() > maximumLength) {
-                removeText(maximumLength, textBuilder.length() - maximumLength);
+            int characterCount = textNode.getCharacterCount();
+            if (characterCount > maximumLength) {
+                textNode.removeText(maximumLength, characterCount - maximumLength);
             }
 
             this.maximumLength = maximumLength;
@@ -587,16 +617,16 @@
     }
 
     /**
-     * Returns the text input selection listener list.
+     * Returns the text input character listener list.
      */
-    public ListenerList<TextInputSelectionListener> getTextInputSelectionListeners() {
-        return textInputSelectionListeners;
+    public ListenerList<TextInputCharacterListener> getTextInputCharacterListeners() {
+        return textInputCharacterListeners;
     }
 
     /**
-     * Returns the text input character listener list.
+     * Returns the text input selection listener list.
      */
-    public ListenerList<TextInputCharacterListener> getTextInputCharacterListeners() {
-        return textInputCharacterListeners;
+    public ListenerList<TextInputSelectionListener> getTextInputSelectionListeners() {
+        return textInputSelectionListeners;
     }
 }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputCharacterListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputCharacterListener.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputCharacterListener.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputCharacterListener.java Mon Mar 16 18:43:06 2009
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 VMware, Inc.
+ * Copyright (c) 2009 VMware, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,14 +35,7 @@
      *
      * @param textInput
      * @param index
-     * @param characters
-     */
-    public void charactersRemoved(TextInput textInput, int index, String characters);
-
-    /**
-     * Called when a text input's character state has been reset.
-     *
-     * @param textInput
+     * @param count
      */
-    public void charactersReset(TextInput textInput);
+    public void charactersRemoved(TextInput textInput, int index, int count);
 }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputListener.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputListener.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/TextInputListener.java Mon Mar 16 18:43:06 2009
@@ -15,6 +15,8 @@
  */
 package pivot.wtk;
 
+import pivot.wtk.text.TextNode;
+
 /**
  * Text input listener interface.
  *
@@ -22,6 +24,13 @@
  */
 public interface TextInputListener {
     /**
+     * Called when a text input's text node has changed.
+     * @param textInput
+     * @param previousTextNode
+     */
+    public void textNodeChanged(TextInput textInput, TextNode previousTextNode);
+
+    /**
      * Called when a text input's text size has changed.
      *
      * @param textInput

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTextInputSkin.java Mon Mar 16 18:43:06 2009
@@ -26,21 +26,19 @@
 import java.awt.font.TextHitInfo;
 import java.awt.font.TextLayout;
 import java.awt.geom.Rectangle2D;
-import java.io.IOException;
 import java.text.AttributedCharacterIterator;
 
 import pivot.collections.Dictionary;
 import pivot.wtk.ApplicationContext;
-import pivot.wtk.Clipboard;
 import pivot.wtk.Component;
 import pivot.wtk.ComponentMouseButtonListener;
 import pivot.wtk.ComponentMouseListener;
 import pivot.wtk.Cursor;
 import pivot.wtk.Dimensions;
+import pivot.wtk.Direction;
 import pivot.wtk.Display;
 import pivot.wtk.Insets;
 import pivot.wtk.Keyboard;
-import pivot.wtk.LocalManifest;
 import pivot.wtk.Mouse;
 import pivot.wtk.Platform;
 import pivot.wtk.TextInput;
@@ -48,8 +46,8 @@
 import pivot.wtk.TextInputCharacterListener;
 import pivot.wtk.TextInputSelectionListener;
 import pivot.wtk.Theme;
-import pivot.wtk.Manifest;
 import pivot.wtk.skin.ComponentSkin;
+import pivot.wtk.text.TextNode;
 
 /**
  * Text input skin.
@@ -65,6 +63,8 @@
 
             public void run() {
                 TextInput textInput = (TextInput)getComponent();
+                TextNode textNode = textInput.getTextNode();
+
                 int selectionStart = textInput.getSelectionStart();
                 int selectionLength = textInput.getSelectionLength();
 
@@ -76,7 +76,7 @@
                     }
                 } else {
                     // Add the next character to the selection
-                    if (selectionStart + selectionLength < textInput.getCharacterCount()) {
+                    if (selectionStart + selectionLength < textNode.getCharacterCount()) {
                         selectionLength++;
                     }
                 }
@@ -363,6 +363,7 @@
         super.install(component);
 
         TextInput textInput = (TextInput)component;
+        textInput.getTextInputListeners().add(this);
         textInput.getTextInputCharacterListeners().add(this);
         textInput.getTextInputSelectionListeners().add(this);
 
@@ -374,6 +375,7 @@
     @Override
     public void uninstall() {
         TextInput textInput = (TextInput)getComponent();
+        textInput.getTextInputListeners().remove(this);
         textInput.getTextInputCharacterListeners().remove(this);
         textInput.getTextInputSelectionListeners().remove(this);
 
@@ -942,7 +944,8 @@
         if (button == Mouse.Button.LEFT
             && count > 1) {
             TextInput textInput = (TextInput)getComponent();
-            textInput.setSelection(0, textInput.getCharacterCount());
+            TextNode textNode = textInput.getTextNode();
+            textInput.setSelection(0, textNode.getCharacterCount());
         }
 
         return super.mouseClick(component, button, x, y, count);
@@ -957,16 +960,10 @@
         if (character > 0x1F
             && character != 0x7F) {
             TextInput textInput = (TextInput)getComponent();
+            TextNode textNode = textInput.getTextNode();
 
-            int selectionStart = textInput.getSelectionStart();
-            int selectionLength = textInput.getSelectionLength();
-
-            if (selectionLength > 0) {
-                textInput.removeText(selectionStart, selectionLength);
-            }
-
-            if (textInput.getCharacterCount() < textInput.getMaximumLength()) {
-                textInput.insertText(character, selectionStart);
+            if (textNode.getCharacterCount() < textInput.getMaximumLength()) {
+                textInput.insertText(character, textInput.getSelectionStart());
             } else {
                 ApplicationContext.beep();
             }
@@ -980,34 +977,12 @@
         boolean consumed = false;
 
         TextInput textInput = (TextInput)getComponent();
+        TextNode textNode = textInput.getTextNode();
 
         if (keyCode == Keyboard.KeyCode.DELETE) {
-            int selectionStart = textInput.getSelectionStart();
-            int selectionLength = textInput.getSelectionLength();
-
-            if (selectionLength > 0) {
-                // Delete any selected text
-                textInput.removeText(selectionStart, selectionLength);
-            } else {
-                // Delete the character after the caret (if any)
-                if (selectionStart < textInput.getCharacterCount()) {
-                    textInput.removeText(selectionStart, 1);
-                }
-            }
+            textInput.delete(Direction.FORWARD);
         } else if (keyCode == Keyboard.KeyCode.BACKSPACE) {
-            int selectionLength = textInput.getSelectionLength();
-
-            if (selectionLength > 0) {
-                // Delete any selected text
-                textInput.removeText(textInput.getSelectionStart(), selectionLength);
-            } else {
-                // Delete the character before the caret (if any)
-                int selectionStart = textInput.getSelectionStart();
-
-                if (selectionStart > 0) {
-                    textInput.removeText(selectionStart - 1, 1);
-                }
-            }
+            textInput.delete(Direction.BACKWARD);
         } else if (keyCode == Keyboard.KeyCode.LEFT) {
             int selectionStart = textInput.getSelectionStart();
             int selectionLength = textInput.getSelectionLength();
@@ -1047,16 +1022,16 @@
             if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)
                 && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
                 // Add all subsequent text to the selection
-                selectionLength = textInput.getCharacterCount() - selectionStart;
+                selectionLength = textNode.getCharacterCount() - selectionStart;
             } else if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
                 // Add the next character to the selection
-                if (selectionStart + selectionLength < textInput.getCharacterCount()) {
+                if (selectionStart + selectionLength < textNode.getCharacterCount()) {
                     selectionLength++;
                 }
             } else if (Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
                 // Clear the selection and move the caret to the end of
                 // the text
-                selectionStart = textInput.getCharacterCount();
+                selectionStart = textNode.getCharacterCount();
                 selectionLength = 0;
             } else {
                 // Clear the selection and move the caret forward by one
@@ -1064,7 +1039,7 @@
                 selectionStart += selectionLength;
 
                 if (selectionLength == 0
-                    && selectionStart < textInput.getCharacterCount()) {
+                    && selectionStart < textNode.getCharacterCount()) {
                     selectionStart++;
                 }
 
@@ -1075,70 +1050,24 @@
         } else if (keyCode == Keyboard.KeyCode.A
             && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
             // Select all
-            textInput.setSelection(0, textInput.getCharacterCount());
+            textInput.setSelection(0, textNode.getCharacterCount());
         } else if (keyCode == Keyboard.KeyCode.X
             && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
-            // "Cut"
             if (textInput.isPassword()) {
                 ApplicationContext.beep();
             } else {
-                // Delete any selected text and put it on the clipboard
-                int selectionLength = textInput.getSelectionLength();
-                if (selectionLength > 0) {
-                    String removedText = textInput.removeText(textInput.getSelectionStart(),
-                        selectionLength);
-                    LocalManifest clipboardContent = new LocalManifest();
-                    clipboardContent.putText(removedText);
-                    Clipboard.setContent(clipboardContent);
-                }
+                textInput.cut();
             }
         } else if (keyCode == Keyboard.KeyCode.C
             && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
-            // "Copy"
             if (textInput.isPassword()) {
                 ApplicationContext.beep();
             } else {
-                // Copy selection to clipboard
-                String selectedText = textInput.getSelectedText();
-                if (selectedText != null) {
-                    LocalManifest clipboardContent = new LocalManifest();
-                    clipboardContent.putText(selectedText);
-                    Clipboard.setContent(clipboardContent);
-                }
+                textInput.copy();
             }
         } else if (keyCode == Keyboard.KeyCode.V
             && Keyboard.isPressed(Keyboard.Modifier.CTRL)) {
-            // "Paste"
-            Manifest clipboardContent = Clipboard.getContent();
-
-            if (clipboardContent != null
-                && clipboardContent.containsText()) {
-                // Paste the string representation of the content
-                String text = null;
-                try {
-                    text = clipboardContent.getText();
-                } catch(IOException exception) {
-                    // No-op
-                }
-
-                if (text != null) {
-                    if ((text.length()
-                        + textInput.getCharacterCount()) > textInput.getMaximumLength()) {
-                        ApplicationContext.beep();
-                    } else {
-                        // Remove any existing selection
-                        int selectionLength = textInput.getSelectionLength();
-                        if (selectionLength > 0) {
-                            textInput.removeText(textInput.getSelectionStart(),
-                                selectionLength);
-                        }
-
-                        // Insert the clipboard contents
-                        int selectionStart = textInput.getSelectionStart();
-                        textInput.insertText(text, selectionStart);
-                    }
-                }
-            }
+            textInput.paste();
         } else {
             consumed = super.keyPressed(component, keyCode, keyLocation);
         }
@@ -1159,13 +1088,14 @@
         super.focusedChanged(component, temporary);
 
         TextInput textInput = (TextInput)getComponent();
+        TextNode textNode = textInput.getTextNode();
 
         if (component.isFocused()) {
             showCaret(textInput.getSelectionLength() == 0);
 
             if (!temporary
                 && mouseSelectionHandler == null) {
-                textInput.setSelection(0, textInput.getCharacterCount());
+                textInput.setSelection(0, textNode.getCharacterCount());
             }
         } else {
             if (!temporary) {
@@ -1180,6 +1110,10 @@
     }
 
     // Text input events
+    public void textNodeChanged(TextInput textInput, TextNode previousTextNode) {
+        updateSelection();
+    }
+
     public void textSizeChanged(TextInput textInput, int previousTextSize) {
         invalidateComponent();
     }
@@ -1202,10 +1136,10 @@
 
     // Text input character events
     public void charactersInserted(TextInput textInput, int index, int count) {
-        repaintComponent();
+        updateSelection();
     }
 
-    public void charactersRemoved(TextInput textInput, int index, String characters) {
+    public void charactersRemoved(TextInput textInput, int index, int count) {
         String text = getText();
         Rectangle2D textBounds = font.getStringBounds(text, fontRenderContext);
 
@@ -1218,16 +1152,16 @@
             scrollLeft = Math.max(textWidth + (padding.left + padding.right + 2) - width, 0);
         }
 
-        repaintComponent();
+        updateSelection();
     }
 
-    public void charactersReset(TextInput textInput) {
-        repaintComponent();
+    // Text input selection events
+    public void selectionChanged(TextInput textInput, int previousSelectionStart,
+        int previousSelectionLength) {
+        updateSelection();
     }
 
-    // Text input selection events
-    public void selectionChanged(TextInput textInput,
-        int previousSelectionStart, int previousSelectionLength) {
+    private void updateSelection() {
         // Update the selection bounding box
         String text = getText();
 
@@ -1240,6 +1174,8 @@
             text = " ";
         }
 
+        TextInput textInput = (TextInput)getComponent();
+
         int selectionStart = textInput.getSelectionStart();
         int selectionLength = textInput.getSelectionLength();
 
@@ -1274,8 +1210,7 @@
                 Rectangle2D logicalHighlightBounds = logicalHighlightShape.getBounds();
                 int logicalHighlightLeft = (int)logicalHighlightBounds.getX();
 
-                if (logicalHighlightLeft - scrollLeft < 0
-                    && previousSelectionStart > selectionStart) {
+                if (logicalHighlightLeft - scrollLeft < 0) {
                     // Ensure that the left edge of the highlight is visible
                     scrollLeft = logicalHighlightLeft;
                 } else {
@@ -1291,7 +1226,7 @@
         }
 
         showCaret(textInput.isFocused()
-            && selectionLength == 0);
+            && textInput.getSelectionLength() == 0);
 
         repaintComponent();
     }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/text/Node.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/text/Node.java?rev=754974&r1=754973&r2=754974&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/text/Node.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/text/Node.java Mon Mar 16 18:43:06 2009
@@ -96,8 +96,6 @@
 
     /**
      * Returns the node's offset within the document.
-     *
-     * @return
      */
     public int getDocumentOffset() {
         Element parent = getParent();