jdk/src/java.desktop/share/classes/javax/swing/text/DefaultEditorKit.java
changeset 25859 3317bb8137f4
parent 23697 e556a715949f
child 42216 621af0ebf6c4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/DefaultEditorKit.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,2344 @@
+/*
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javax.swing.text;
+
+import sun.awt.SunToolkit;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.text.*;
+import javax.swing.Action;
+import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+
+/**
+ * This is the set of things needed by a text component
+ * to be a reasonably functioning editor for some <em>type</em>
+ * of text document.  This implementation provides a default
+ * implementation which treats text as plain text and
+ * provides a minimal set of actions for a simple editor.
+ *
+ * <dl>
+ * <dt><b>Newlines</b>
+ * <dd>
+ * There are two properties which deal with newlines.  The
+ * system property, <code>line.separator</code>, is defined to be
+ * platform-dependent, either "\n", "\r", or "\r\n".  There is also
+ * a property defined in <code>DefaultEditorKit</code>, called
+ * <a href=#EndOfLineStringProperty><code>EndOfLineStringProperty</code></a>,
+ * which is defined automatically when a document is loaded, to be
+ * the first occurrence of any of the newline characters.
+ * When a document is loaded, <code>EndOfLineStringProperty</code>
+ * is set appropriately, and when the document is written back out, the
+ * <code>EndOfLineStringProperty</code> is used.  But while the document
+ * is in memory, the "\n" character is used to define a
+ * newline, regardless of how the newline is defined when
+ * the document is on disk.  Therefore, for searching purposes,
+ * "\n" should always be used.  When a new document is created,
+ * and the <code>EndOfLineStringProperty</code> has not been defined,
+ * it will use the System property when writing out the
+ * document.
+ * <p>Note that <code>EndOfLineStringProperty</code> is set
+ * on the <code>Document</code> using the <code>get/putProperty</code>
+ * methods.  Subclasses may override this behavior.
+ *
+ * </dl>
+ *
+ * @author  Timothy Prinzing
+ */
+@SuppressWarnings("serial") // Same-version serialization only
+public class DefaultEditorKit extends EditorKit {
+
+    /**
+     * default constructor for DefaultEditorKit
+     */
+    public DefaultEditorKit() {
+    }
+
+    /**
+     * Gets the MIME type of the data that this
+     * kit represents support for.  The default
+     * is <code>text/plain</code>.
+     *
+     * @return the type
+     */
+    public String getContentType() {
+        return "text/plain";
+    }
+
+    /**
+     * Fetches a factory that is suitable for producing
+     * views of any models that are produced by this
+     * kit.  The default is to have the UI produce the
+     * factory, so this method has no implementation.
+     *
+     * @return the view factory
+     */
+    public ViewFactory getViewFactory() {
+        return null;
+    }
+
+    /**
+     * Fetches the set of commands that can be used
+     * on a text component that is using a model and
+     * view produced by this kit.
+     *
+     * @return the command list
+     */
+    public Action[] getActions() {
+        return defaultActions;
+    }
+
+    /**
+     * Fetches a caret that can navigate through views
+     * produced by the associated ViewFactory.
+     *
+     * @return the caret
+     */
+    public Caret createCaret() {
+        return null;
+    }
+
+    /**
+     * Creates an uninitialized text storage model (PlainDocument)
+     * that is appropriate for this type of editor.
+     *
+     * @return the model
+     */
+    public Document createDefaultDocument() {
+        return new PlainDocument();
+    }
+
+    /**
+     * Inserts content from the given stream which is expected
+     * to be in a format appropriate for this kind of content
+     * handler.
+     *
+     * @param in  The stream to read from
+     * @param doc The destination for the insertion.
+     * @param pos The location in the document to place the
+     *   content &gt;=0.
+     * @exception IOException on any I/O error
+     * @exception BadLocationException if pos represents an invalid
+     *   location within the document.
+     */
+    public void read(InputStream in, Document doc, int pos)
+        throws IOException, BadLocationException {
+
+        read(new InputStreamReader(in), doc, pos);
+    }
+
+    /**
+     * Writes content from a document to the given stream
+     * in a format appropriate for this kind of content handler.
+     *
+     * @param out The stream to write to
+     * @param doc The source for the write.
+     * @param pos The location in the document to fetch the
+     *   content &gt;=0.
+     * @param len The amount to write out &gt;=0.
+     * @exception IOException on any I/O error
+     * @exception BadLocationException if pos represents an invalid
+     *   location within the document.
+     */
+    public void write(OutputStream out, Document doc, int pos, int len)
+        throws IOException, BadLocationException {
+        OutputStreamWriter osw = new OutputStreamWriter(out);
+
+        write(osw, doc, pos, len);
+        osw.flush();
+    }
+
+    /**
+     * Gets the input attributes for the pane. This method exists for
+     * the benefit of StyledEditorKit so that the read method will
+     * pick up the correct attributes to apply to inserted text.
+     * This class's implementation simply returns null.
+     *
+     * @return null
+     */
+    MutableAttributeSet getInputAttributes() {
+        return null;
+    }
+
+    /**
+     * Inserts content from the given stream, which will be
+     * treated as plain text.
+     *
+     * @param in  The stream to read from
+     * @param doc The destination for the insertion.
+     * @param pos The location in the document to place the
+     *   content &gt;=0.
+     * @exception IOException on any I/O error
+     * @exception BadLocationException if pos represents an invalid
+     *   location within the document.
+     */
+    public void read(Reader in, Document doc, int pos)
+        throws IOException, BadLocationException {
+
+        char[] buff = new char[4096];
+        int nch;
+        boolean lastWasCR = false;
+        boolean isCRLF = false;
+        boolean isCR = false;
+        int last;
+        boolean wasEmpty = (doc.getLength() == 0);
+        AttributeSet attr = getInputAttributes();
+
+        // Read in a block at a time, mapping \r\n to \n, as well as single
+        // \r's to \n's. If a \r\n is encountered, \r\n will be set as the
+        // newline string for the document, if \r is encountered it will
+        // be set as the newline character, otherwise the newline property
+        // for the document will be removed.
+        while ((nch = in.read(buff, 0, buff.length)) != -1) {
+            last = 0;
+            for(int counter = 0; counter < nch; counter++) {
+                switch(buff[counter]) {
+                case '\r':
+                    if (lastWasCR) {
+                        isCR = true;
+                        if (counter == 0) {
+                            doc.insertString(pos, "\n", attr);
+                            pos++;
+                        }
+                        else {
+                            buff[counter - 1] = '\n';
+                        }
+                    }
+                    else {
+                        lastWasCR = true;
+                    }
+                    break;
+                case '\n':
+                    if (lastWasCR) {
+                        if (counter > (last + 1)) {
+                            doc.insertString(pos, new String(buff, last,
+                                            counter - last - 1), attr);
+                            pos += (counter - last - 1);
+                        }
+                        // else nothing to do, can skip \r, next write will
+                        // write \n
+                        lastWasCR = false;
+                        last = counter;
+                        isCRLF = true;
+                    }
+                    break;
+                default:
+                    if (lastWasCR) {
+                        isCR = true;
+                        if (counter == 0) {
+                            doc.insertString(pos, "\n", attr);
+                            pos++;
+                        }
+                        else {
+                            buff[counter - 1] = '\n';
+                        }
+                        lastWasCR = false;
+                    }
+                    break;
+                }
+            }
+            if (last < nch) {
+                if(lastWasCR) {
+                    if (last < (nch - 1)) {
+                        doc.insertString(pos, new String(buff, last,
+                                         nch - last - 1), attr);
+                        pos += (nch - last - 1);
+                    }
+                }
+                else {
+                    doc.insertString(pos, new String(buff, last,
+                                     nch - last), attr);
+                    pos += (nch - last);
+                }
+            }
+        }
+        if (lastWasCR) {
+            doc.insertString(pos, "\n", attr);
+            isCR = true;
+        }
+        if (wasEmpty) {
+            if (isCRLF) {
+                doc.putProperty(EndOfLineStringProperty, "\r\n");
+            }
+            else if (isCR) {
+                doc.putProperty(EndOfLineStringProperty, "\r");
+            }
+            else {
+                doc.putProperty(EndOfLineStringProperty, "\n");
+            }
+        }
+    }
+
+    /**
+     * Writes content from a document to the given stream
+     * as plain text.
+     *
+     * @param out  The stream to write to
+     * @param doc The source for the write.
+     * @param pos The location in the document to fetch the
+     *   content from &gt;=0.
+     * @param len The amount to write out &gt;=0.
+     * @exception IOException on any I/O error
+     * @exception BadLocationException if pos is not within 0 and
+     *   the length of the document.
+     */
+    public void write(Writer out, Document doc, int pos, int len)
+        throws IOException, BadLocationException {
+
+        if ((pos < 0) || ((pos + len) > doc.getLength())) {
+            throw new BadLocationException("DefaultEditorKit.write", pos);
+        }
+        Segment data = new Segment();
+        int nleft = len;
+        int offs = pos;
+        Object endOfLineProperty = doc.getProperty(EndOfLineStringProperty);
+        if (endOfLineProperty == null) {
+            endOfLineProperty = System.lineSeparator();
+        }
+        String endOfLine;
+        if (endOfLineProperty instanceof String) {
+            endOfLine = (String)endOfLineProperty;
+        }
+        else {
+            endOfLine = null;
+        }
+        if (endOfLineProperty != null && !endOfLine.equals("\n")) {
+            // There is an end of line string that isn't \n, have to iterate
+            // through and find all \n's and translate to end of line string.
+            while (nleft > 0) {
+                int n = Math.min(nleft, 4096);
+                doc.getText(offs, n, data);
+                int last = data.offset;
+                char[] array = data.array;
+                int maxCounter = last + data.count;
+                for (int counter = last; counter < maxCounter; counter++) {
+                    if (array[counter] == '\n') {
+                        if (counter > last) {
+                            out.write(array, last, counter - last);
+                        }
+                        out.write(endOfLine);
+                        last = counter + 1;
+                    }
+                }
+                if (maxCounter > last) {
+                    out.write(array, last, maxCounter - last);
+                }
+                offs += n;
+                nleft -= n;
+            }
+        }
+        else {
+            // Just write out text, will already have \n, no mapping to
+            // do.
+            while (nleft > 0) {
+                int n = Math.min(nleft, 4096);
+                doc.getText(offs, n, data);
+                out.write(data.array, data.offset, data.count);
+                offs += n;
+                nleft -= n;
+            }
+        }
+        out.flush();
+    }
+
+
+    /**
+     * When reading a document if a CRLF is encountered a property
+     * with this name is added and the value will be "\r\n".
+     */
+    public static final String EndOfLineStringProperty = "__EndOfLine__";
+
+    // --- names of well-known actions ---------------------------
+
+    /**
+     * Name of the action to place content into the associated
+     * document.  If there is a selection, it is removed before
+     * the new content is added.
+     * @see #getActions
+     */
+    public static final String insertContentAction = "insert-content";
+
+    /**
+     * Name of the action to place a line/paragraph break into
+     * the document.  If there is a selection, it is removed before
+     * the break is added.
+     * @see #getActions
+     */
+    public static final String insertBreakAction = "insert-break";
+
+    /**
+     * Name of the action to place a tab character into
+     * the document.  If there is a selection, it is removed before
+     * the tab is added.
+     * @see #getActions
+     */
+    public static final String insertTabAction = "insert-tab";
+
+    /**
+     * Name of the action to delete the character of content that
+     * precedes the current caret position.
+     * @see #getActions
+     */
+    public static final String deletePrevCharAction = "delete-previous";
+
+    /**
+     * Name of the action to delete the character of content that
+     * follows the current caret position.
+     * @see #getActions
+     */
+    public static final String deleteNextCharAction = "delete-next";
+
+    /**
+     * Name of the action to delete the word that
+     * follows the beginning of the selection.
+     * @see #getActions
+     * @see JTextComponent#getSelectionStart
+     * @since 1.6
+     */
+    public static final String deleteNextWordAction = "delete-next-word";
+
+    /**
+     * Name of the action to delete the word that
+     * precedes the beginning of the selection.
+     * @see #getActions
+     * @see JTextComponent#getSelectionStart
+     * @since 1.6
+     */
+    public static final String deletePrevWordAction = "delete-previous-word";
+
+    /**
+     * Name of the action to set the editor into read-only
+     * mode.
+     * @see #getActions
+     */
+    public static final String readOnlyAction = "set-read-only";
+
+    /**
+     * Name of the action to set the editor into writeable
+     * mode.
+     * @see #getActions
+     */
+    public static final String writableAction = "set-writable";
+
+    /**
+     * Name of the action to cut the selected region
+     * and place the contents into the system clipboard.
+     * @see JTextComponent#cut
+     * @see #getActions
+     */
+    public static final String cutAction = "cut-to-clipboard";
+
+    /**
+     * Name of the action to copy the selected region
+     * and place the contents into the system clipboard.
+     * @see JTextComponent#copy
+     * @see #getActions
+     */
+    public static final String copyAction = "copy-to-clipboard";
+
+    /**
+     * Name of the action to paste the contents of the
+     * system clipboard into the selected region, or before the
+     * caret if nothing is selected.
+     * @see JTextComponent#paste
+     * @see #getActions
+     */
+    public static final String pasteAction = "paste-from-clipboard";
+
+    /**
+     * Name of the action to create a beep.
+     * @see #getActions
+     */
+    public static final String beepAction = "beep";
+
+    /**
+     * Name of the action to page up vertically.
+     * @see #getActions
+     */
+    public static final String pageUpAction = "page-up";
+
+    /**
+     * Name of the action to page down vertically.
+     * @see #getActions
+     */
+    public static final String pageDownAction = "page-down";
+
+    /**
+     * Name of the action to page up vertically, and move the
+     * selection.
+     * @see #getActions
+     */
+    /*public*/ static final String selectionPageUpAction = "selection-page-up";
+
+    /**
+     * Name of the action to page down vertically, and move the
+     * selection.
+     * @see #getActions
+     */
+    /*public*/ static final String selectionPageDownAction = "selection-page-down";
+
+    /**
+     * Name of the action to page left horizontally, and move the
+     * selection.
+     * @see #getActions
+     */
+    /*public*/ static final String selectionPageLeftAction = "selection-page-left";
+
+    /**
+     * Name of the action to page right horizontally, and move the
+     * selection.
+     * @see #getActions
+     */
+    /*public*/ static final String selectionPageRightAction = "selection-page-right";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically forward one position.
+     * @see #getActions
+     */
+    public static final String forwardAction = "caret-forward";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically backward one position.
+     * @see #getActions
+     */
+    public static final String backwardAction = "caret-backward";
+
+    /**
+     * Name of the Action for extending the selection
+     * by moving the caret logically forward one position.
+     * @see #getActions
+     */
+    public static final String selectionForwardAction = "selection-forward";
+
+    /**
+     * Name of the Action for extending the selection
+     * by moving the caret logically backward one position.
+     * @see #getActions
+     */
+    public static final String selectionBackwardAction = "selection-backward";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically upward one position.
+     * @see #getActions
+     */
+    public static final String upAction = "caret-up";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically downward one position.
+     * @see #getActions
+     */
+    public static final String downAction = "caret-down";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically upward one position, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionUpAction = "selection-up";
+
+    /**
+     * Name of the Action for moving the caret
+     * logically downward one position, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionDownAction = "selection-down";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a word.
+     * @see #getActions
+     */
+    public static final String beginWordAction = "caret-begin-word";
+
+    /**
+     * Name of the Action for moving the caret
+     * to the end of a word.
+     * @see #getActions
+     */
+    public static final String endWordAction = "caret-end-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a word, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionBeginWordAction = "selection-begin-word";
+
+    /**
+     * Name of the Action for moving the caret
+     * to the end of a word, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionEndWordAction = "selection-end-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret to the
+     * beginning of the previous word.
+     * @see #getActions
+     */
+    public static final String previousWordAction = "caret-previous-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret to the
+     * beginning of the next word.
+     * @see #getActions
+     */
+    public static final String nextWordAction = "caret-next-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the selection to the
+     * beginning of the previous word, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionPreviousWordAction = "selection-previous-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the selection to the
+     * beginning of the next word, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionNextWordAction = "selection-next-word";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a line.
+     * @see #getActions
+     */
+    public static final String beginLineAction = "caret-begin-line";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the end of a line.
+     * @see #getActions
+     */
+    public static final String endLineAction = "caret-end-line";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a line, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionBeginLineAction = "selection-begin-line";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the end of a line, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionEndLineAction = "selection-end-line";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a paragraph.
+     * @see #getActions
+     */
+    public static final String beginParagraphAction = "caret-begin-paragraph";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the end of a paragraph.
+     * @see #getActions
+     */
+    public static final String endParagraphAction = "caret-end-paragraph";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of a paragraph, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionBeginParagraphAction = "selection-begin-paragraph";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the end of a paragraph, extending the selection.
+     * @see #getActions
+     */
+    public static final String selectionEndParagraphAction = "selection-end-paragraph";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of the document.
+     * @see #getActions
+     */
+    public static final String beginAction = "caret-begin";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the end of the document.
+     * @see #getActions
+     */
+    public static final String endAction = "caret-end";
+
+    /**
+     * Name of the <code>Action</code> for moving the caret
+     * to the beginning of the document.
+     * @see #getActions
+     */
+    public static final String selectionBeginAction = "selection-begin";
+
+    /**
+     * Name of the Action for moving the caret
+     * to the end of the document.
+     * @see #getActions
+     */
+    public static final String selectionEndAction = "selection-end";
+
+    /**
+     * Name of the Action for selecting a word around the caret.
+     * @see #getActions
+     */
+    public static final String selectWordAction = "select-word";
+
+    /**
+     * Name of the Action for selecting a line around the caret.
+     * @see #getActions
+     */
+    public static final String selectLineAction = "select-line";
+
+    /**
+     * Name of the Action for selecting a paragraph around the caret.
+     * @see #getActions
+     */
+    public static final String selectParagraphAction = "select-paragraph";
+
+    /**
+     * Name of the Action for selecting the entire document
+     * @see #getActions
+     */
+    public static final String selectAllAction = "select-all";
+
+    /**
+     * Name of the Action for removing selection
+     * @see #getActions
+     */
+    /*public*/ static final String unselectAction = "unselect";
+
+    /**
+     * Name of the Action for toggling the component's orientation.
+     * @see #getActions
+     */
+    /*public*/ static final String toggleComponentOrientationAction
+        = "toggle-componentOrientation";
+
+    /**
+     * Name of the action that is executed by default if
+     * a <em>key typed event</em> is received and there
+     * is no keymap entry.
+     * @see #getActions
+     */
+    public static final String defaultKeyTypedAction = "default-typed";
+
+    // --- Action implementations ---------------------------------
+
+    private static final Action[] defaultActions = {
+        new InsertContentAction(), new DeletePrevCharAction(),
+        new DeleteNextCharAction(), new ReadOnlyAction(),
+        new DeleteWordAction(deletePrevWordAction),
+        new DeleteWordAction(deleteNextWordAction),
+        new WritableAction(), new CutAction(),
+        new CopyAction(), new PasteAction(),
+        new VerticalPageAction(pageUpAction, -1, false),
+        new VerticalPageAction(pageDownAction, 1, false),
+        new VerticalPageAction(selectionPageUpAction, -1, true),
+        new VerticalPageAction(selectionPageDownAction, 1, true),
+        new PageAction(selectionPageLeftAction, true, true),
+        new PageAction(selectionPageRightAction, false, true),
+        new InsertBreakAction(), new BeepAction(),
+        new NextVisualPositionAction(forwardAction, false,
+                                     SwingConstants.EAST),
+        new NextVisualPositionAction(backwardAction, false,
+                                     SwingConstants.WEST),
+        new NextVisualPositionAction(selectionForwardAction, true,
+                                     SwingConstants.EAST),
+        new NextVisualPositionAction(selectionBackwardAction, true,
+                                     SwingConstants.WEST),
+        new NextVisualPositionAction(upAction, false,
+                                     SwingConstants.NORTH),
+        new NextVisualPositionAction(downAction, false,
+                                     SwingConstants.SOUTH),
+        new NextVisualPositionAction(selectionUpAction, true,
+                                     SwingConstants.NORTH),
+        new NextVisualPositionAction(selectionDownAction, true,
+                                     SwingConstants.SOUTH),
+        new BeginWordAction(beginWordAction, false),
+        new EndWordAction(endWordAction, false),
+        new BeginWordAction(selectionBeginWordAction, true),
+        new EndWordAction(selectionEndWordAction, true),
+        new PreviousWordAction(previousWordAction, false),
+        new NextWordAction(nextWordAction, false),
+        new PreviousWordAction(selectionPreviousWordAction, true),
+        new NextWordAction(selectionNextWordAction, true),
+        new BeginLineAction(beginLineAction, false),
+        new EndLineAction(endLineAction, false),
+        new BeginLineAction(selectionBeginLineAction, true),
+        new EndLineAction(selectionEndLineAction, true),
+        new BeginParagraphAction(beginParagraphAction, false),
+        new EndParagraphAction(endParagraphAction, false),
+        new BeginParagraphAction(selectionBeginParagraphAction, true),
+        new EndParagraphAction(selectionEndParagraphAction, true),
+        new BeginAction(beginAction, false),
+        new EndAction(endAction, false),
+        new BeginAction(selectionBeginAction, true),
+        new EndAction(selectionEndAction, true),
+        new DefaultKeyTypedAction(), new InsertTabAction(),
+        new SelectWordAction(), new SelectLineAction(),
+        new SelectParagraphAction(), new SelectAllAction(),
+        new UnselectAction(), new ToggleComponentOrientationAction(),
+        new DumpModelAction()
+    };
+
+    /**
+     * The action that is executed by default if
+     * a <em>key typed event</em> is received and there
+     * is no keymap entry.  There is a variation across
+     * different VM's in what gets sent as a <em>key typed</em>
+     * event, and this action tries to filter out the undesired
+     * events.  This filters the control characters and those
+     * with the ALT modifier.  It allows Control-Alt sequences
+     * through as these form legitimate unicode characters on
+     * some PC keyboards.
+     * <p>
+     * If the event doesn't get filtered, it will try to insert
+     * content into the text editor.  The content is fetched
+     * from the command string of the ActionEvent.  The text
+     * entry is done through the <code>replaceSelection</code>
+     * method on the target text component.  This is the
+     * action that will be fired for most text entry tasks.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#defaultKeyTypedAction
+     * @see DefaultEditorKit#getActions
+     * @see Keymap#setDefaultAction
+     * @see Keymap#getDefaultAction
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class DefaultKeyTypedAction extends TextAction {
+
+        /**
+         * Creates this object with the appropriate identifier.
+         */
+        public DefaultKeyTypedAction() {
+            super(defaultKeyTypedAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if ((target != null) && (e != null)) {
+                if ((! target.isEditable()) || (! target.isEnabled())) {
+                    return;
+                }
+                String content = e.getActionCommand();
+                int mod = e.getModifiers();
+                if ((content != null) && (content.length() > 0)) {
+                    boolean isPrintableMask = true;
+                    Toolkit tk = Toolkit.getDefaultToolkit();
+                    if (tk instanceof SunToolkit) {
+                        isPrintableMask = ((SunToolkit)tk).isPrintableCharacterModifiersMask(mod);
+                    }
+
+                    if (isPrintableMask) {
+                        char c = content.charAt(0);
+                        if ((c >= 0x20) && (c != 0x7F)) {
+                            target.replaceSelection(content);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Places content into the associated document.
+     * If there is a selection, it is removed before
+     * the new content is added.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#insertContentAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class InsertContentAction extends TextAction {
+
+        /**
+         * Creates this object with the appropriate identifier.
+         */
+        public InsertContentAction() {
+            super(insertContentAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if ((target != null) && (e != null)) {
+                if ((! target.isEditable()) || (! target.isEnabled())) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                    return;
+                }
+                String content = e.getActionCommand();
+                if (content != null) {
+                    target.replaceSelection(content);
+                } else {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+    }
+
+    /**
+     * Places a line/paragraph break into the document.
+     * If there is a selection, it is removed before
+     * the break is added.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#insertBreakAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class InsertBreakAction extends TextAction {
+
+        /**
+         * Creates this object with the appropriate identifier.
+         */
+        public InsertBreakAction() {
+            super(insertBreakAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                if ((! target.isEditable()) || (! target.isEnabled())) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                    return;
+                }
+                target.replaceSelection("\n");
+            }
+        }
+    }
+
+    /**
+     * Places a tab character into the document. If there
+     * is a selection, it is removed before the tab is added.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#insertTabAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class InsertTabAction extends TextAction {
+
+        /**
+         * Creates this object with the appropriate identifier.
+         */
+        public InsertTabAction() {
+            super(insertTabAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                if ((! target.isEditable()) || (! target.isEnabled())) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                    return;
+                }
+                target.replaceSelection("\t");
+            }
+        }
+    }
+
+    /*
+     * Deletes the character of content that precedes the
+     * current caret position.
+     * @see DefaultEditorKit#deletePrevCharAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class DeletePrevCharAction extends TextAction {
+
+        /**
+         * Creates this object with the appropriate identifier.
+         */
+        DeletePrevCharAction() {
+            super(deletePrevCharAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            boolean beep = true;
+            if ((target != null) && (target.isEditable())) {
+                try {
+                    Document doc = target.getDocument();
+                    Caret caret = target.getCaret();
+                    int dot = caret.getDot();
+                    int mark = caret.getMark();
+                    if (dot != mark) {
+                        doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
+                        beep = false;
+                    } else if (dot > 0) {
+                        int delChars = 1;
+
+                        if (dot > 1) {
+                            String dotChars = doc.getText(dot - 2, 2);
+                            char c0 = dotChars.charAt(0);
+                            char c1 = dotChars.charAt(1);
+
+                            if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
+                                c1 >= '\uDC00' && c1 <= '\uDFFF') {
+                                delChars = 2;
+                            }
+                        }
+
+                        doc.remove(dot - delChars, delChars);
+                        beep = false;
+                    }
+                } catch (BadLocationException bl) {
+                }
+            }
+            if (beep) {
+                UIManager.getLookAndFeel().provideErrorFeedback(target);
+            }
+        }
+    }
+
+    /*
+     * Deletes the character of content that follows the
+     * current caret position.
+     * @see DefaultEditorKit#deleteNextCharAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class DeleteNextCharAction extends TextAction {
+
+        /* Create this object with the appropriate identifier. */
+        DeleteNextCharAction() {
+            super(deleteNextCharAction);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            boolean beep = true;
+            if ((target != null) && (target.isEditable())) {
+                try {
+                    Document doc = target.getDocument();
+                    Caret caret = target.getCaret();
+                    int dot = caret.getDot();
+                    int mark = caret.getMark();
+                    if (dot != mark) {
+                        doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
+                        beep = false;
+                    } else if (dot < doc.getLength()) {
+                        int delChars = 1;
+
+                        if (dot < doc.getLength() - 1) {
+                            String dotChars = doc.getText(dot, 2);
+                            char c0 = dotChars.charAt(0);
+                            char c1 = dotChars.charAt(1);
+
+                            if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
+                                c1 >= '\uDC00' && c1 <= '\uDFFF') {
+                                delChars = 2;
+                            }
+                        }
+
+                        doc.remove(dot, delChars);
+                        beep = false;
+                    }
+                } catch (BadLocationException bl) {
+                }
+            }
+            if (beep) {
+                UIManager.getLookAndFeel().provideErrorFeedback(target);
+            }
+        }
+    }
+
+
+    /*
+     * Deletes the word that precedes/follows the beginning of the selection.
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class DeleteWordAction extends TextAction {
+        DeleteWordAction(String name) {
+            super(name);
+            assert (name == deletePrevWordAction)
+                || (name == deleteNextWordAction);
+        }
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            final JTextComponent target = getTextComponent(e);
+            if ((target != null) && (e != null)) {
+                if ((! target.isEditable()) || (! target.isEnabled())) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                    return;
+                }
+                boolean beep = true;
+                try {
+                    final int start = target.getSelectionStart();
+                    final Element line =
+                        Utilities.getParagraphElement(target, start);
+                    int end;
+                    if (deleteNextWordAction == getValue(Action.NAME)) {
+                        end = Utilities.
+                            getNextWordInParagraph(target, line, start, false);
+                        if (end == java.text.BreakIterator.DONE) {
+                            //last word in the paragraph
+                            final int endOfLine = line.getEndOffset();
+                            if (start == endOfLine - 1) {
+                                //for last position remove last \n
+                                end = endOfLine;
+                            } else {
+                                //remove to the end of the paragraph
+                                end = endOfLine - 1;
+                            }
+                        }
+                    } else {
+                        end = Utilities.
+                            getPrevWordInParagraph(target, line, start);
+                        if (end == java.text.BreakIterator.DONE) {
+                            //there is no previous word in the paragraph
+                            final int startOfLine = line.getStartOffset();
+                            if (start == startOfLine) {
+                                //for first position remove previous \n
+                                end = startOfLine - 1;
+                            } else {
+                                //remove to the start of the paragraph
+                                end = startOfLine;
+                            }
+                        }
+                    }
+                    int offs = Math.min(start, end);
+                    int len = Math.abs(end - start);
+                    if (offs >= 0) {
+                        target.getDocument().remove(offs, len);
+                        beep = false;
+                    }
+                } catch (BadLocationException ignore) {
+                }
+                if (beep) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+    }
+
+
+    /*
+     * Sets the editor into read-only mode.
+     * @see DefaultEditorKit#readOnlyAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class ReadOnlyAction extends TextAction {
+
+        /* Create this object with the appropriate identifier. */
+        ReadOnlyAction() {
+            super(readOnlyAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.setEditable(false);
+            }
+        }
+    }
+
+    /*
+     * Sets the editor into writeable mode.
+     * @see DefaultEditorKit#writableAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class WritableAction extends TextAction {
+
+        /* Create this object with the appropriate identifier. */
+        WritableAction() {
+            super(writableAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.setEditable(true);
+            }
+        }
+    }
+
+    /**
+     * Cuts the selected region and place its contents
+     * into the system clipboard.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#cutAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class CutAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public CutAction() {
+            super(cutAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.cut();
+            }
+        }
+    }
+
+    /**
+     * Copies the selected region and place its contents
+     * into the system clipboard.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#copyAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class CopyAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public CopyAction() {
+            super(copyAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.copy();
+            }
+        }
+    }
+
+    /**
+     * Pastes the contents of the system clipboard into the
+     * selected region, or before the caret if nothing is
+     * selected.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#pasteAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class PasteAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public PasteAction() {
+            super(pasteAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.paste();
+            }
+        }
+    }
+
+    /**
+     * Creates a beep.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans&trade;
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     *
+     * @see DefaultEditorKit#beepAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Same-version serialization only
+    public static class BeepAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public BeepAction() {
+            super(beepAction);
+        }
+
+        /**
+         * The operation to perform when this action is triggered.
+         *
+         * @param e the action event
+         */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            UIManager.getLookAndFeel().provideErrorFeedback(target);
+        }
+    }
+
+    /**
+     * Scrolls up/down vertically.  The select version of this action extends
+     * the selection, instead of simply moving the caret.
+     *
+     * @see DefaultEditorKit#pageUpAction
+     * @see DefaultEditorKit#pageDownAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class VerticalPageAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public VerticalPageAction(String nm, int direction, boolean select) {
+            super(nm);
+            this.select = select;
+            this.direction = direction;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                Rectangle visible = target.getVisibleRect();
+                Rectangle newVis = new Rectangle(visible);
+                int selectedIndex = target.getCaretPosition();
+                int scrollAmount = direction *
+                        target.getScrollableBlockIncrement(
+                                  visible, SwingConstants.VERTICAL, direction);
+                int initialY = visible.y;
+                Caret caret = target.getCaret();
+                Point magicPosition = caret.getMagicCaretPosition();
+
+                if (selectedIndex != -1) {
+                    try {
+                        Rectangle dotBounds = target.modelToView(
+                                                     selectedIndex);
+                        int x = (magicPosition != null) ? magicPosition.x :
+                                                          dotBounds.x;
+                        int h = dotBounds.height;
+                        if (h > 0) {
+                            // We want to scroll by a multiple of caret height,
+                            // rounding towards lower integer
+                            scrollAmount = scrollAmount / h * h;
+                        }
+                        newVis.y = constrainY(target,
+                                initialY + scrollAmount, visible.height);
+
+                        int newIndex;
+
+                        if (visible.contains(dotBounds.x, dotBounds.y)) {
+                            // Dot is currently visible, base the new
+                            // location off the old, or
+                            newIndex = target.viewToModel(
+                                new Point(x, constrainY(target,
+                                          dotBounds.y + scrollAmount, 0)));
+                        }
+                        else {
+                            // Dot isn't visible, choose the top or the bottom
+                            // for the new location.
+                            if (direction == -1) {
+                                newIndex = target.viewToModel(new Point(
+                                    x, newVis.y));
+                            }
+                            else {
+                                newIndex = target.viewToModel(new Point(
+                                    x, newVis.y + visible.height));
+                            }
+                        }
+                        newIndex = constrainOffset(target, newIndex);
+                        if (newIndex != selectedIndex) {
+                            // Make sure the new visible location contains
+                            // the location of dot, otherwise Caret will
+                            // cause an additional scroll.
+                            int newY = getAdjustedY(target, newVis, newIndex);
+
+                            if (direction == -1 && newY <= initialY || direction == 1 && newY >= initialY) {
+                                // Change index and correct newVis.y only if won't cause scrolling upward
+                                newVis.y = newY;
+
+                                if (select) {
+                                    target.moveCaretPosition(newIndex);
+                                } else {
+                                    target.setCaretPosition(newIndex);
+                                }
+                            }
+                        }
+                    } catch (BadLocationException ble) { }
+                } else {
+                    newVis.y = constrainY(target,
+                            initialY + scrollAmount, visible.height);
+                }
+                if (magicPosition != null) {
+                    caret.setMagicCaretPosition(magicPosition);
+                }
+                target.scrollRectToVisible(newVis);
+            }
+        }
+
+        /**
+         * Makes sure <code>y</code> is a valid location in
+         * <code>target</code>.
+         */
+        private int constrainY(JTextComponent target, int y, int vis) {
+            if (y < 0) {
+                y = 0;
+            }
+            else if (y + vis > target.getHeight()) {
+                y = Math.max(0, target.getHeight() - vis);
+            }
+            return y;
+        }
+
+        /**
+         * Ensures that <code>offset</code> is a valid offset into the
+         * model for <code>text</code>.
+         */
+        private int constrainOffset(JTextComponent text, int offset) {
+            Document doc = text.getDocument();
+
+            if ((offset != 0) && (offset > doc.getLength())) {
+                offset = doc.getLength();
+            }
+            if (offset  < 0) {
+                offset = 0;
+            }
+            return offset;
+        }
+
+        /**
+         * Returns adjustsed {@code y} position that indicates the location to scroll to
+         * after selecting <code>index</code>.
+         */
+        private int getAdjustedY(JTextComponent text, Rectangle visible, int index) {
+            int result = visible.y;
+
+            try {
+                Rectangle dotBounds = text.modelToView(index);
+
+                if (dotBounds.y < visible.y) {
+                    result = dotBounds.y;
+                } else {
+                    if ((dotBounds.y > visible.y + visible.height) ||
+                            (dotBounds.y + dotBounds.height > visible.y + visible.height)) {
+                        result = dotBounds.y + dotBounds.height - visible.height;
+                    }
+                }
+            } catch (BadLocationException ble) {
+            }
+
+            return result;
+        }
+
+        /**
+         * Adjusts the Rectangle to contain the bounds of the character at
+         * <code>index</code> in response to a page up.
+         */
+        private boolean select;
+
+        /**
+         * Direction to scroll, 1 is down, -1 is up.
+         */
+        private int direction;
+    }
+
+
+    /**
+     * Pages one view to the left or right.
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class PageAction extends TextAction {
+
+        /** Create this object with the appropriate identifier. */
+        public PageAction(String nm, boolean left, boolean select) {
+            super(nm);
+            this.select = select;
+            this.left = left;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                int selectedIndex;
+                Rectangle visible = new Rectangle();
+                target.computeVisibleRect(visible);
+                if (left) {
+                    visible.x = Math.max(0, visible.x - visible.width);
+                }
+                else {
+                    visible.x += visible.width;
+                }
+
+                selectedIndex = target.getCaretPosition();
+                if(selectedIndex != -1) {
+                    if (left) {
+                        selectedIndex = target.viewToModel
+                            (new Point(visible.x, visible.y));
+                    }
+                    else {
+                        selectedIndex = target.viewToModel
+                            (new Point(visible.x + visible.width - 1,
+                                       visible.y + visible.height - 1));
+                    }
+                    Document doc = target.getDocument();
+                    if ((selectedIndex != 0) &&
+                        (selectedIndex  > (doc.getLength()-1))) {
+                        selectedIndex = doc.getLength()-1;
+                    }
+                    else if(selectedIndex  < 0) {
+                        selectedIndex = 0;
+                    }
+                    if (select)
+                        target.moveCaretPosition(selectedIndex);
+                    else
+                        target.setCaretPosition(selectedIndex);
+                }
+            }
+        }
+
+        private boolean select;
+        private boolean left;
+    }
+
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class DumpModelAction extends TextAction {
+
+        DumpModelAction() {
+            super("dump-model");
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                Document d = target.getDocument();
+                if (d instanceof AbstractDocument) {
+                    ((AbstractDocument) d).dump(System.err);
+                }
+            }
+        }
+    }
+
+    /*
+     * Action to move the selection by way of the
+     * getNextVisualPositionFrom method. Constructor indicates direction
+     * to use.
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class NextVisualPositionAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        NextVisualPositionAction(String nm, boolean select, int direction) {
+            super(nm);
+            this.select = select;
+            this.direction = direction;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                Caret caret = target.getCaret();
+                DefaultCaret bidiCaret = (caret instanceof DefaultCaret) ?
+                                              (DefaultCaret)caret : null;
+                int dot = caret.getDot();
+                Position.Bias[] bias = new Position.Bias[1];
+                Point magicPosition = caret.getMagicCaretPosition();
+
+                try {
+                    if(magicPosition == null &&
+                       (direction == SwingConstants.NORTH ||
+                        direction == SwingConstants.SOUTH)) {
+                        Rectangle r = (bidiCaret != null) ?
+                                target.getUI().modelToView(target, dot,
+                                                      bidiCaret.getDotBias()) :
+                                target.modelToView(dot);
+                        magicPosition = new Point(r.x, r.y);
+                    }
+
+                    NavigationFilter filter = target.getNavigationFilter();
+
+                    if (filter != null) {
+                        dot = filter.getNextVisualPositionFrom
+                                     (target, dot, (bidiCaret != null) ?
+                                      bidiCaret.getDotBias() :
+                                      Position.Bias.Forward, direction, bias);
+                    }
+                    else {
+                        dot = target.getUI().getNextVisualPositionFrom
+                                     (target, dot, (bidiCaret != null) ?
+                                      bidiCaret.getDotBias() :
+                                      Position.Bias.Forward, direction, bias);
+                    }
+                    if(bias[0] == null) {
+                        bias[0] = Position.Bias.Forward;
+                    }
+                    if(bidiCaret != null) {
+                        if (select) {
+                            bidiCaret.moveDot(dot, bias[0]);
+                        } else {
+                            bidiCaret.setDot(dot, bias[0]);
+                        }
+                    }
+                    else {
+                        if (select) {
+                            caret.moveDot(dot);
+                        } else {
+                            caret.setDot(dot);
+                        }
+                    }
+                    if(magicPosition != null &&
+                       (direction == SwingConstants.NORTH ||
+                        direction == SwingConstants.SOUTH)) {
+                        target.getCaret().setMagicCaretPosition(magicPosition);
+                    }
+                } catch (BadLocationException ex) {
+                }
+            }
+        }
+
+        private boolean select;
+        private int direction;
+    }
+
+    /*
+     * Position the caret to the beginning of the word.
+     * @see DefaultEditorKit#beginWordAction
+     * @see DefaultEditorKit#selectBeginWordAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class BeginWordAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        BeginWordAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                try {
+                    int offs = target.getCaretPosition();
+                    int begOffs = Utilities.getWordStart(target, offs);
+                    if (select) {
+                        target.moveCaretPosition(begOffs);
+                    } else {
+                        target.setCaretPosition(begOffs);
+                    }
+                } catch (BadLocationException bl) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the end of the word.
+     * @see DefaultEditorKit#endWordAction
+     * @see DefaultEditorKit#selectEndWordAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class EndWordAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        EndWordAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                try {
+                    int offs = target.getCaretPosition();
+                    int endOffs = Utilities.getWordEnd(target, offs);
+                    if (select) {
+                        target.moveCaretPosition(endOffs);
+                    } else {
+                        target.setCaretPosition(endOffs);
+                    }
+                } catch (BadLocationException bl) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the beginning of the previous word.
+     * @see DefaultEditorKit#previousWordAction
+     * @see DefaultEditorKit#selectPreviousWordAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class PreviousWordAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        PreviousWordAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                int offs = target.getCaretPosition();
+                boolean failed = false;
+                try {
+                    Element curPara =
+                            Utilities.getParagraphElement(target, offs);
+                    offs = Utilities.getPreviousWord(target, offs);
+                    if(offs < curPara.getStartOffset()) {
+                        // we should first move to the end of the
+                        // previous paragraph (bug #4278839)
+                        offs = Utilities.getParagraphElement(target, offs).
+                                getEndOffset() - 1;
+                    }
+                } catch (BadLocationException bl) {
+                    if (offs != 0) {
+                        offs = 0;
+                    }
+                    else {
+                        failed = true;
+                    }
+                }
+                if (!failed) {
+                    if (select) {
+                        target.moveCaretPosition(offs);
+                    } else {
+                        target.setCaretPosition(offs);
+                    }
+                }
+                else {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the next of the word.
+     * @see DefaultEditorKit#nextWordAction
+     * @see DefaultEditorKit#selectNextWordAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class NextWordAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        NextWordAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                int offs = target.getCaretPosition();
+                boolean failed = false;
+                int oldOffs = offs;
+                Element curPara =
+                        Utilities.getParagraphElement(target, offs);
+                try {
+                    offs = Utilities.getNextWord(target, offs);
+                    if(offs >= curPara.getEndOffset() &&
+                            oldOffs != curPara.getEndOffset() - 1) {
+                        // we should first move to the end of current
+                        // paragraph (bug #4278839)
+                        offs = curPara.getEndOffset() - 1;
+                    }
+                } catch (BadLocationException bl) {
+                    int end = target.getDocument().getLength();
+                    if (offs != end) {
+                        if(oldOffs != curPara.getEndOffset() - 1) {
+                            offs = curPara.getEndOffset() - 1;
+                        } else {
+                        offs = end;
+                    }
+                    }
+                    else {
+                        failed = true;
+                    }
+                }
+                if (!failed) {
+                    if (select) {
+                        target.moveCaretPosition(offs);
+                    } else {
+                        target.setCaretPosition(offs);
+                    }
+                }
+                else {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the beginning of the line.
+     * @see DefaultEditorKit#beginLineAction
+     * @see DefaultEditorKit#selectBeginLineAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class BeginLineAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        BeginLineAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                try {
+                    int offs = target.getCaretPosition();
+                    int begOffs = Utilities.getRowStart(target, offs);
+                    if (select) {
+                        target.moveCaretPosition(begOffs);
+                    } else {
+                        target.setCaretPosition(begOffs);
+                    }
+                } catch (BadLocationException bl) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the end of the line.
+     * @see DefaultEditorKit#endLineAction
+     * @see DefaultEditorKit#selectEndLineAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class EndLineAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        EndLineAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                try {
+                    int offs = target.getCaretPosition();
+                    int endOffs = Utilities.getRowEnd(target, offs);
+                    if (select) {
+                        target.moveCaretPosition(endOffs);
+                    } else {
+                        target.setCaretPosition(endOffs);
+                    }
+                } catch (BadLocationException bl) {
+                    UIManager.getLookAndFeel().provideErrorFeedback(target);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the beginning of the paragraph.
+     * @see DefaultEditorKit#beginParagraphAction
+     * @see DefaultEditorKit#selectBeginParagraphAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class BeginParagraphAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        BeginParagraphAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                int offs = target.getCaretPosition();
+                Element elem = Utilities.getParagraphElement(target, offs);
+                offs = elem.getStartOffset();
+                if (select) {
+                    target.moveCaretPosition(offs);
+                } else {
+                    target.setCaretPosition(offs);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Position the caret to the end of the paragraph.
+     * @see DefaultEditorKit#endParagraphAction
+     * @see DefaultEditorKit#selectEndParagraphAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class EndParagraphAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        EndParagraphAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                int offs = target.getCaretPosition();
+                Element elem = Utilities.getParagraphElement(target, offs);
+                offs = Math.min(target.getDocument().getLength(),
+                                elem.getEndOffset());
+                if (select) {
+                    target.moveCaretPosition(offs);
+                } else {
+                    target.setCaretPosition(offs);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Move the caret to the beginning of the document.
+     * @see DefaultEditorKit#beginAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class BeginAction extends TextAction {
+
+        /* Create this object with the appropriate identifier. */
+        BeginAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                if (select) {
+                    target.moveCaretPosition(0);
+                } else {
+                    target.setCaretPosition(0);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Move the caret to the end of the document.
+     * @see DefaultEditorKit#endAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class EndAction extends TextAction {
+
+        /* Create this object with the appropriate identifier. */
+        EndAction(String nm, boolean select) {
+            super(nm);
+            this.select = select;
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                Document doc = target.getDocument();
+                int dot = doc.getLength();
+                if (select) {
+                    target.moveCaretPosition(dot);
+                } else {
+                    target.setCaretPosition(dot);
+                }
+            }
+        }
+
+        private boolean select;
+    }
+
+    /*
+     * Select the word around the caret
+     * @see DefaultEditorKit#endAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class SelectWordAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        SelectWordAction() {
+            super(selectWordAction);
+            start = new BeginWordAction("pigdog", false);
+            end = new EndWordAction("pigdog", true);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            start.actionPerformed(e);
+            end.actionPerformed(e);
+        }
+
+        private Action start;
+        private Action end;
+    }
+
+    /*
+     * Select the line around the caret
+     * @see DefaultEditorKit#endAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class SelectLineAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        SelectLineAction() {
+            super(selectLineAction);
+            start = new BeginLineAction("pigdog", false);
+            end = new EndLineAction("pigdog", true);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            start.actionPerformed(e);
+            end.actionPerformed(e);
+        }
+
+        private Action start;
+        private Action end;
+    }
+
+    /*
+     * Select the paragraph around the caret
+     * @see DefaultEditorKit#endAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class SelectParagraphAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        SelectParagraphAction() {
+            super(selectParagraphAction);
+            start = new BeginParagraphAction("pigdog", false);
+            end = new EndParagraphAction("pigdog", true);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            start.actionPerformed(e);
+            end.actionPerformed(e);
+        }
+
+        private Action start;
+        private Action end;
+    }
+
+    /*
+     * Select the entire document
+     * @see DefaultEditorKit#endAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class SelectAllAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         * @param nm  the name of the action, Action.NAME.
+         * @param select whether to extend the selection when
+         *  changing the caret position.
+         */
+        SelectAllAction() {
+            super(selectAllAction);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                Document doc = target.getDocument();
+                target.setCaretPosition(0);
+                target.moveCaretPosition(doc.getLength());
+            }
+        }
+
+    }
+
+    /*
+     * Remove the selection, if any.
+     * @see DefaultEditorKit#unselectAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class UnselectAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         */
+        UnselectAction() {
+            super(unselectAction);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                target.setCaretPosition(target.getCaretPosition());
+            }
+        }
+
+    }
+
+    /*
+     * Toggles the ComponentOrientation of the text component.
+     * @see DefaultEditorKit#toggleComponentOrientationAction
+     * @see DefaultEditorKit#getActions
+     */
+    @SuppressWarnings("serial") // Superclass is not serializable across versions
+    static class ToggleComponentOrientationAction extends TextAction {
+
+        /**
+         * Create this action with the appropriate identifier.
+         */
+        ToggleComponentOrientationAction() {
+            super(toggleComponentOrientationAction);
+        }
+
+        /** The operation to perform when this action is triggered. */
+        public void actionPerformed(ActionEvent e) {
+            JTextComponent target = getTextComponent(e);
+            if (target != null) {
+                ComponentOrientation last = target.getComponentOrientation();
+                ComponentOrientation next;
+                if( last == ComponentOrientation.RIGHT_TO_LEFT )
+                    next = ComponentOrientation.LEFT_TO_RIGHT;
+                else
+                    next = ComponentOrientation.RIGHT_TO_LEFT;
+                target.setComponentOrientation(next);
+                target.repaint();
+            }
+        }
+    }
+
+}