1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text;
  26 
  27 import sun.awt.SunToolkit;
  28 
  29 import java.io.*;
  30 import java.awt.*;
  31 import java.awt.event.ActionEvent;
  32 import java.text.*;
  33 import javax.swing.Action;
  34 import javax.swing.KeyStroke;
  35 import javax.swing.SwingConstants;
  36 import javax.swing.UIManager;
  37 
  38 /**
  39  * This is the set of things needed by a text component
  40  * to be a reasonably functioning editor for some <em>type</em>
  41  * of text document.  This implementation provides a default
  42  * implementation which treats text as plain text and
  43  * provides a minimal set of actions for a simple editor.
  44  *
  45  * <dl>
  46  * <dt><b>Newlines</b>
  47  * <dd>
  48  * There are two properties which deal with newlines.  The
  49  * system property, <code>line.separator</code>, is defined to be
  50  * platform-dependent, either "\n", "\r", or "\r\n".  There is also
  51  * a property defined in <code>DefaultEditorKit</code>, called
  52  * <a href=#EndOfLineStringProperty><code>EndOfLineStringProperty</code></a>,
  53  * which is defined automatically when a document is loaded, to be
  54  * the first occurrence of any of the newline characters.
  55  * When a document is loaded, <code>EndOfLineStringProperty</code>
  56  * is set appropriately, and when the document is written back out, the
  57  * <code>EndOfLineStringProperty</code> is used.  But while the document
  58  * is in memory, the "\n" character is used to define a
  59  * newline, regardless of how the newline is defined when
  60  * the document is on disk.  Therefore, for searching purposes,
  61  * "\n" should always be used.  When a new document is created,
  62  * and the <code>EndOfLineStringProperty</code> has not been defined,
  63  * it will use the System property when writing out the
  64  * document.
  65  * <p>Note that <code>EndOfLineStringProperty</code> is set
  66  * on the <code>Document</code> using the <code>get/putProperty</code>
  67  * methods.  Subclasses may override this behavior.
  68  *
  69  * </dl>
  70  *
  71  * @author  Timothy Prinzing
  72  */
  73 @SuppressWarnings("serial") // Same-version serialization only
  74 public class DefaultEditorKit extends EditorKit {
  75 
  76     /**
  77      * default constructor for DefaultEditorKit
  78      */
  79     public DefaultEditorKit() {
  80     }
  81 
  82     /**
  83      * Gets the MIME type of the data that this
  84      * kit represents support for.  The default
  85      * is <code>text/plain</code>.
  86      *
  87      * @return the type
  88      */
  89     public String getContentType() {
  90         return "text/plain";
  91     }
  92 
  93     /**
  94      * Fetches a factory that is suitable for producing
  95      * views of any models that are produced by this
  96      * kit.  The default is to have the UI produce the
  97      * factory, so this method has no implementation.
  98      *
  99      * @return the view factory
 100      */
 101     public ViewFactory getViewFactory() {
 102         return null;
 103     }
 104 
 105     /**
 106      * Fetches the set of commands that can be used
 107      * on a text component that is using a model and
 108      * view produced by this kit.
 109      *
 110      * @return the command list
 111      */
 112     public Action[] getActions() {
 113         return defaultActions;
 114     }
 115 
 116     /**
 117      * Fetches a caret that can navigate through views
 118      * produced by the associated ViewFactory.
 119      *
 120      * @return the caret
 121      */
 122     public Caret createCaret() {
 123         return null;
 124     }
 125 
 126     /**
 127      * Creates an uninitialized text storage model (PlainDocument)
 128      * that is appropriate for this type of editor.
 129      *
 130      * @return the model
 131      */
 132     public Document createDefaultDocument() {
 133         return new PlainDocument();
 134     }
 135 
 136     /**
 137      * Inserts content from the given stream which is expected
 138      * to be in a format appropriate for this kind of content
 139      * handler.
 140      *
 141      * @param in  The stream to read from
 142      * @param doc The destination for the insertion.
 143      * @param pos The location in the document to place the
 144      *   content &gt;=0.
 145      * @exception IOException on any I/O error
 146      * @exception BadLocationException if pos represents an invalid
 147      *   location within the document.
 148      */
 149     public void read(InputStream in, Document doc, int pos)
 150         throws IOException, BadLocationException {
 151 
 152         read(new InputStreamReader(in), doc, pos);
 153     }
 154 
 155     /**
 156      * Writes content from a document to the given stream
 157      * in a format appropriate for this kind of content handler.
 158      *
 159      * @param out The stream to write to
 160      * @param doc The source for the write.
 161      * @param pos The location in the document to fetch the
 162      *   content &gt;=0.
 163      * @param len The amount to write out &gt;=0.
 164      * @exception IOException on any I/O error
 165      * @exception BadLocationException if pos represents an invalid
 166      *   location within the document.
 167      */
 168     public void write(OutputStream out, Document doc, int pos, int len)
 169         throws IOException, BadLocationException {
 170         OutputStreamWriter osw = new OutputStreamWriter(out);
 171 
 172         write(osw, doc, pos, len);
 173         osw.flush();
 174     }
 175 
 176     /**
 177      * Gets the input attributes for the pane. This method exists for
 178      * the benefit of StyledEditorKit so that the read method will
 179      * pick up the correct attributes to apply to inserted text.
 180      * This class's implementation simply returns null.
 181      *
 182      * @return null
 183      */
 184     MutableAttributeSet getInputAttributes() {
 185         return null;
 186     }
 187 
 188     /**
 189      * Inserts content from the given stream, which will be
 190      * treated as plain text.
 191      *
 192      * @param in  The stream to read from
 193      * @param doc The destination for the insertion.
 194      * @param pos The location in the document to place the
 195      *   content &gt;=0.
 196      * @exception IOException on any I/O error
 197      * @exception BadLocationException if pos represents an invalid
 198      *   location within the document.
 199      */
 200     public void read(Reader in, Document doc, int pos)
 201         throws IOException, BadLocationException {
 202 
 203         char[] buff = new char[4096];
 204         int nch;
 205         boolean lastWasCR = false;
 206         boolean isCRLF = false;
 207         boolean isCR = false;
 208         int last;
 209         boolean wasEmpty = (doc.getLength() == 0);
 210         AttributeSet attr = getInputAttributes();
 211 
 212         // Read in a block at a time, mapping \r\n to \n, as well as single
 213         // \r's to \n's. If a \r\n is encountered, \r\n will be set as the
 214         // newline string for the document, if \r is encountered it will
 215         // be set as the newline character, otherwise the newline property
 216         // for the document will be removed.
 217         while ((nch = in.read(buff, 0, buff.length)) != -1) {
 218             last = 0;
 219             for(int counter = 0; counter < nch; counter++) {
 220                 switch(buff[counter]) {
 221                 case '\r':
 222                     if (lastWasCR) {
 223                         isCR = true;
 224                         if (counter == 0) {
 225                             doc.insertString(pos, "\n", attr);
 226                             pos++;
 227                         }
 228                         else {
 229                             buff[counter - 1] = '\n';
 230                         }
 231                     }
 232                     else {
 233                         lastWasCR = true;
 234                     }
 235                     break;
 236                 case '\n':
 237                     if (lastWasCR) {
 238                         if (counter > (last + 1)) {
 239                             doc.insertString(pos, new String(buff, last,
 240                                             counter - last - 1), attr);
 241                             pos += (counter - last - 1);
 242                         }
 243                         // else nothing to do, can skip \r, next write will
 244                         // write \n
 245                         lastWasCR = false;
 246                         last = counter;
 247                         isCRLF = true;
 248                     }
 249                     break;
 250                 default:
 251                     if (lastWasCR) {
 252                         isCR = true;
 253                         if (counter == 0) {
 254                             doc.insertString(pos, "\n", attr);
 255                             pos++;
 256                         }
 257                         else {
 258                             buff[counter - 1] = '\n';
 259                         }
 260                         lastWasCR = false;
 261                     }
 262                     break;
 263                 }
 264             }
 265             if (last < nch) {
 266                 if(lastWasCR) {
 267                     if (last < (nch - 1)) {
 268                         doc.insertString(pos, new String(buff, last,
 269                                          nch - last - 1), attr);
 270                         pos += (nch - last - 1);
 271                     }
 272                 }
 273                 else {
 274                     doc.insertString(pos, new String(buff, last,
 275                                      nch - last), attr);
 276                     pos += (nch - last);
 277                 }
 278             }
 279         }
 280         if (lastWasCR) {
 281             doc.insertString(pos, "\n", attr);
 282             isCR = true;
 283         }
 284         if (wasEmpty) {
 285             if (isCRLF) {
 286                 doc.putProperty(EndOfLineStringProperty, "\r\n");
 287             }
 288             else if (isCR) {
 289                 doc.putProperty(EndOfLineStringProperty, "\r");
 290             }
 291             else {
 292                 doc.putProperty(EndOfLineStringProperty, "\n");
 293             }
 294         }
 295     }
 296 
 297     /**
 298      * Writes content from a document to the given stream
 299      * as plain text.
 300      *
 301      * @param out  The stream to write to
 302      * @param doc The source for the write.
 303      * @param pos The location in the document to fetch the
 304      *   content from &gt;=0.
 305      * @param len The amount to write out &gt;=0.
 306      * @exception IOException on any I/O error
 307      * @exception BadLocationException if pos is not within 0 and
 308      *   the length of the document.
 309      */
 310     public void write(Writer out, Document doc, int pos, int len)
 311         throws IOException, BadLocationException {
 312 
 313         if ((pos < 0) || ((pos + len) > doc.getLength())) {
 314             throw new BadLocationException("DefaultEditorKit.write", pos);
 315         }
 316         Segment data = new Segment();
 317         int nleft = len;
 318         int offs = pos;
 319         Object endOfLineProperty = doc.getProperty(EndOfLineStringProperty);
 320         if (endOfLineProperty == null) {
 321             endOfLineProperty = System.lineSeparator();
 322         }
 323         String endOfLine;
 324         if (endOfLineProperty instanceof String) {
 325             endOfLine = (String)endOfLineProperty;
 326         }
 327         else {
 328             endOfLine = null;
 329         }
 330         if (endOfLineProperty != null && !endOfLine.equals("\n")) {
 331             // There is an end of line string that isn't \n, have to iterate
 332             // through and find all \n's and translate to end of line string.
 333             while (nleft > 0) {
 334                 int n = Math.min(nleft, 4096);
 335                 doc.getText(offs, n, data);
 336                 int last = data.offset;
 337                 char[] array = data.array;
 338                 int maxCounter = last + data.count;
 339                 for (int counter = last; counter < maxCounter; counter++) {
 340                     if (array[counter] == '\n') {
 341                         if (counter > last) {
 342                             out.write(array, last, counter - last);
 343                         }
 344                         out.write(endOfLine);
 345                         last = counter + 1;
 346                     }
 347                 }
 348                 if (maxCounter > last) {
 349                     out.write(array, last, maxCounter - last);
 350                 }
 351                 offs += n;
 352                 nleft -= n;
 353             }
 354         }
 355         else {
 356             // Just write out text, will already have \n, no mapping to
 357             // do.
 358             while (nleft > 0) {
 359                 int n = Math.min(nleft, 4096);
 360                 doc.getText(offs, n, data);
 361                 out.write(data.array, data.offset, data.count);
 362                 offs += n;
 363                 nleft -= n;
 364             }
 365         }
 366         out.flush();
 367     }
 368 
 369 
 370     /**
 371      * When reading a document if a CRLF is encountered a property
 372      * with this name is added and the value will be "\r\n".
 373      */
 374     public static final String EndOfLineStringProperty = "__EndOfLine__";
 375 
 376     // --- names of well-known actions ---------------------------
 377 
 378     /**
 379      * Name of the action to place content into the associated
 380      * document.  If there is a selection, it is removed before
 381      * the new content is added.
 382      * @see #getActions
 383      */
 384     public static final String insertContentAction = "insert-content";
 385 
 386     /**
 387      * Name of the action to place a line/paragraph break into
 388      * the document.  If there is a selection, it is removed before
 389      * the break is added.
 390      * @see #getActions
 391      */
 392     public static final String insertBreakAction = "insert-break";
 393 
 394     /**
 395      * Name of the action to place a tab character into
 396      * the document.  If there is a selection, it is removed before
 397      * the tab is added.
 398      * @see #getActions
 399      */
 400     public static final String insertTabAction = "insert-tab";
 401 
 402     /**
 403      * Name of the action to delete the character of content that
 404      * precedes the current caret position.
 405      * @see #getActions
 406      */
 407     public static final String deletePrevCharAction = "delete-previous";
 408 
 409     /**
 410      * Name of the action to delete the character of content that
 411      * follows the current caret position.
 412      * @see #getActions
 413      */
 414     public static final String deleteNextCharAction = "delete-next";
 415 
 416     /**
 417      * Name of the action to delete the word that
 418      * follows the beginning of the selection.
 419      * @see #getActions
 420      * @see JTextComponent#getSelectionStart
 421      * @since 1.6
 422      */
 423     public static final String deleteNextWordAction = "delete-next-word";
 424 
 425     /**
 426      * Name of the action to delete the word that
 427      * precedes the beginning of the selection.
 428      * @see #getActions
 429      * @see JTextComponent#getSelectionStart
 430      * @since 1.6
 431      */
 432     public static final String deletePrevWordAction = "delete-previous-word";
 433 
 434     /**
 435      * Name of the action to set the editor into read-only
 436      * mode.
 437      * @see #getActions
 438      */
 439     public static final String readOnlyAction = "set-read-only";
 440 
 441     /**
 442      * Name of the action to set the editor into writeable
 443      * mode.
 444      * @see #getActions
 445      */
 446     public static final String writableAction = "set-writable";
 447 
 448     /**
 449      * Name of the action to cut the selected region
 450      * and place the contents into the system clipboard.
 451      * @see JTextComponent#cut
 452      * @see #getActions
 453      */
 454     public static final String cutAction = "cut-to-clipboard";
 455 
 456     /**
 457      * Name of the action to copy the selected region
 458      * and place the contents into the system clipboard.
 459      * @see JTextComponent#copy
 460      * @see #getActions
 461      */
 462     public static final String copyAction = "copy-to-clipboard";
 463 
 464     /**
 465      * Name of the action to paste the contents of the
 466      * system clipboard into the selected region, or before the
 467      * caret if nothing is selected.
 468      * @see JTextComponent#paste
 469      * @see #getActions
 470      */
 471     public static final String pasteAction = "paste-from-clipboard";
 472 
 473     /**
 474      * Name of the action to create a beep.
 475      * @see #getActions
 476      */
 477     public static final String beepAction = "beep";
 478 
 479     /**
 480      * Name of the action to page up vertically.
 481      * @see #getActions
 482      */
 483     public static final String pageUpAction = "page-up";
 484 
 485     /**
 486      * Name of the action to page down vertically.
 487      * @see #getActions
 488      */
 489     public static final String pageDownAction = "page-down";
 490 
 491     /**
 492      * Name of the action to page up vertically, and move the
 493      * selection.
 494      * @see #getActions
 495      */
 496     /*public*/ static final String selectionPageUpAction = "selection-page-up";
 497 
 498     /**
 499      * Name of the action to page down vertically, and move the
 500      * selection.
 501      * @see #getActions
 502      */
 503     /*public*/ static final String selectionPageDownAction = "selection-page-down";
 504 
 505     /**
 506      * Name of the action to page left horizontally, and move the
 507      * selection.
 508      * @see #getActions
 509      */
 510     /*public*/ static final String selectionPageLeftAction = "selection-page-left";
 511 
 512     /**
 513      * Name of the action to page right horizontally, and move the
 514      * selection.
 515      * @see #getActions
 516      */
 517     /*public*/ static final String selectionPageRightAction = "selection-page-right";
 518 
 519     /**
 520      * Name of the Action for moving the caret
 521      * logically forward one position.
 522      * @see #getActions
 523      */
 524     public static final String forwardAction = "caret-forward";
 525 
 526     /**
 527      * Name of the Action for moving the caret
 528      * logically backward one position.
 529      * @see #getActions
 530      */
 531     public static final String backwardAction = "caret-backward";
 532 
 533     /**
 534      * Name of the Action for extending the selection
 535      * by moving the caret logically forward one position.
 536      * @see #getActions
 537      */
 538     public static final String selectionForwardAction = "selection-forward";
 539 
 540     /**
 541      * Name of the Action for extending the selection
 542      * by moving the caret logically backward one position.
 543      * @see #getActions
 544      */
 545     public static final String selectionBackwardAction = "selection-backward";
 546 
 547     /**
 548      * Name of the Action for moving the caret
 549      * logically upward one position.
 550      * @see #getActions
 551      */
 552     public static final String upAction = "caret-up";
 553 
 554     /**
 555      * Name of the Action for moving the caret
 556      * logically downward one position.
 557      * @see #getActions
 558      */
 559     public static final String downAction = "caret-down";
 560 
 561     /**
 562      * Name of the Action for moving the caret
 563      * logically upward one position, extending the selection.
 564      * @see #getActions
 565      */
 566     public static final String selectionUpAction = "selection-up";
 567 
 568     /**
 569      * Name of the Action for moving the caret
 570      * logically downward one position, extending the selection.
 571      * @see #getActions
 572      */
 573     public static final String selectionDownAction = "selection-down";
 574 
 575     /**
 576      * Name of the <code>Action</code> for moving the caret
 577      * to the beginning of a word.
 578      * @see #getActions
 579      */
 580     public static final String beginWordAction = "caret-begin-word";
 581 
 582     /**
 583      * Name of the Action for moving the caret
 584      * to the end of a word.
 585      * @see #getActions
 586      */
 587     public static final String endWordAction = "caret-end-word";
 588 
 589     /**
 590      * Name of the <code>Action</code> for moving the caret
 591      * to the beginning of a word, extending the selection.
 592      * @see #getActions
 593      */
 594     public static final String selectionBeginWordAction = "selection-begin-word";
 595 
 596     /**
 597      * Name of the Action for moving the caret
 598      * to the end of a word, extending the selection.
 599      * @see #getActions
 600      */
 601     public static final String selectionEndWordAction = "selection-end-word";
 602 
 603     /**
 604      * Name of the <code>Action</code> for moving the caret to the
 605      * beginning of the previous word.
 606      * @see #getActions
 607      */
 608     public static final String previousWordAction = "caret-previous-word";
 609 
 610     /**
 611      * Name of the <code>Action</code> for moving the caret to the
 612      * beginning of the next word.
 613      * @see #getActions
 614      */
 615     public static final String nextWordAction = "caret-next-word";
 616 
 617     /**
 618      * Name of the <code>Action</code> for moving the selection to the
 619      * beginning of the previous word, extending the selection.
 620      * @see #getActions
 621      */
 622     public static final String selectionPreviousWordAction = "selection-previous-word";
 623 
 624     /**
 625      * Name of the <code>Action</code> for moving the selection to the
 626      * beginning of the next word, extending the selection.
 627      * @see #getActions
 628      */
 629     public static final String selectionNextWordAction = "selection-next-word";
 630 
 631     /**
 632      * Name of the <code>Action</code> for moving the caret
 633      * to the beginning of a line.
 634      * @see #getActions
 635      */
 636     public static final String beginLineAction = "caret-begin-line";
 637 
 638     /**
 639      * Name of the <code>Action</code> for moving the caret
 640      * to the end of a line.
 641      * @see #getActions
 642      */
 643     public static final String endLineAction = "caret-end-line";
 644 
 645     /**
 646      * Name of the <code>Action</code> for moving the caret
 647      * to the beginning of a line, extending the selection.
 648      * @see #getActions
 649      */
 650     public static final String selectionBeginLineAction = "selection-begin-line";
 651 
 652     /**
 653      * Name of the <code>Action</code> for moving the caret
 654      * to the end of a line, extending the selection.
 655      * @see #getActions
 656      */
 657     public static final String selectionEndLineAction = "selection-end-line";
 658 
 659     /**
 660      * Name of the <code>Action</code> for moving the caret
 661      * to the beginning of a paragraph.
 662      * @see #getActions
 663      */
 664     public static final String beginParagraphAction = "caret-begin-paragraph";
 665 
 666     /**
 667      * Name of the <code>Action</code> for moving the caret
 668      * to the end of a paragraph.
 669      * @see #getActions
 670      */
 671     public static final String endParagraphAction = "caret-end-paragraph";
 672 
 673     /**
 674      * Name of the <code>Action</code> for moving the caret
 675      * to the beginning of a paragraph, extending the selection.
 676      * @see #getActions
 677      */
 678     public static final String selectionBeginParagraphAction = "selection-begin-paragraph";
 679 
 680     /**
 681      * Name of the <code>Action</code> for moving the caret
 682      * to the end of a paragraph, extending the selection.
 683      * @see #getActions
 684      */
 685     public static final String selectionEndParagraphAction = "selection-end-paragraph";
 686 
 687     /**
 688      * Name of the <code>Action</code> for moving the caret
 689      * to the beginning of the document.
 690      * @see #getActions
 691      */
 692     public static final String beginAction = "caret-begin";
 693 
 694     /**
 695      * Name of the <code>Action</code> for moving the caret
 696      * to the end of the document.
 697      * @see #getActions
 698      */
 699     public static final String endAction = "caret-end";
 700 
 701     /**
 702      * Name of the <code>Action</code> for moving the caret
 703      * to the beginning of the document.
 704      * @see #getActions
 705      */
 706     public static final String selectionBeginAction = "selection-begin";
 707 
 708     /**
 709      * Name of the Action for moving the caret
 710      * to the end of the document.
 711      * @see #getActions
 712      */
 713     public static final String selectionEndAction = "selection-end";
 714 
 715     /**
 716      * Name of the Action for selecting a word around the caret.
 717      * @see #getActions
 718      */
 719     public static final String selectWordAction = "select-word";
 720 
 721     /**
 722      * Name of the Action for selecting a line around the caret.
 723      * @see #getActions
 724      */
 725     public static final String selectLineAction = "select-line";
 726 
 727     /**
 728      * Name of the Action for selecting a paragraph around the caret.
 729      * @see #getActions
 730      */
 731     public static final String selectParagraphAction = "select-paragraph";
 732 
 733     /**
 734      * Name of the Action for selecting the entire document
 735      * @see #getActions
 736      */
 737     public static final String selectAllAction = "select-all";
 738 
 739     /**
 740      * Name of the Action for removing selection
 741      * @see #getActions
 742      */
 743     /*public*/ static final String unselectAction = "unselect";
 744 
 745     /**
 746      * Name of the Action for toggling the component's orientation.
 747      * @see #getActions
 748      */
 749     /*public*/ static final String toggleComponentOrientationAction
 750         = "toggle-componentOrientation";
 751 
 752     /**
 753      * Name of the action that is executed by default if
 754      * a <em>key typed event</em> is received and there
 755      * is no keymap entry.
 756      * @see #getActions
 757      */
 758     public static final String defaultKeyTypedAction = "default-typed";
 759 
 760     // --- Action implementations ---------------------------------
 761 
 762     private static final Action[] defaultActions = {
 763         new InsertContentAction(), new DeletePrevCharAction(),
 764         new DeleteNextCharAction(), new ReadOnlyAction(),
 765         new DeleteWordAction(deletePrevWordAction),
 766         new DeleteWordAction(deleteNextWordAction),
 767         new WritableAction(), new CutAction(),
 768         new CopyAction(), new PasteAction(),
 769         new VerticalPageAction(pageUpAction, -1, false),
 770         new VerticalPageAction(pageDownAction, 1, false),
 771         new VerticalPageAction(selectionPageUpAction, -1, true),
 772         new VerticalPageAction(selectionPageDownAction, 1, true),
 773         new PageAction(selectionPageLeftAction, true, true),
 774         new PageAction(selectionPageRightAction, false, true),
 775         new InsertBreakAction(), new BeepAction(),
 776         new NextVisualPositionAction(forwardAction, false,
 777                                      SwingConstants.EAST),
 778         new NextVisualPositionAction(backwardAction, false,
 779                                      SwingConstants.WEST),
 780         new NextVisualPositionAction(selectionForwardAction, true,
 781                                      SwingConstants.EAST),
 782         new NextVisualPositionAction(selectionBackwardAction, true,
 783                                      SwingConstants.WEST),
 784         new NextVisualPositionAction(upAction, false,
 785                                      SwingConstants.NORTH),
 786         new NextVisualPositionAction(downAction, false,
 787                                      SwingConstants.SOUTH),
 788         new NextVisualPositionAction(selectionUpAction, true,
 789                                      SwingConstants.NORTH),
 790         new NextVisualPositionAction(selectionDownAction, true,
 791                                      SwingConstants.SOUTH),
 792         new BeginWordAction(beginWordAction, false),
 793         new EndWordAction(endWordAction, false),
 794         new BeginWordAction(selectionBeginWordAction, true),
 795         new EndWordAction(selectionEndWordAction, true),
 796         new PreviousWordAction(previousWordAction, false),
 797         new NextWordAction(nextWordAction, false),
 798         new PreviousWordAction(selectionPreviousWordAction, true),
 799         new NextWordAction(selectionNextWordAction, true),
 800         new BeginLineAction(beginLineAction, false),
 801         new EndLineAction(endLineAction, false),
 802         new BeginLineAction(selectionBeginLineAction, true),
 803         new EndLineAction(selectionEndLineAction, true),
 804         new BeginParagraphAction(beginParagraphAction, false),
 805         new EndParagraphAction(endParagraphAction, false),
 806         new BeginParagraphAction(selectionBeginParagraphAction, true),
 807         new EndParagraphAction(selectionEndParagraphAction, true),
 808         new BeginAction(beginAction, false),
 809         new EndAction(endAction, false),
 810         new BeginAction(selectionBeginAction, true),
 811         new EndAction(selectionEndAction, true),
 812         new DefaultKeyTypedAction(), new InsertTabAction(),
 813         new SelectWordAction(), new SelectLineAction(),
 814         new SelectParagraphAction(), new SelectAllAction(),
 815         new UnselectAction(), new ToggleComponentOrientationAction(),
 816         new DumpModelAction()
 817     };
 818 
 819     /**
 820      * The action that is executed by default if
 821      * a <em>key typed event</em> is received and there
 822      * is no keymap entry.  There is a variation across
 823      * different VM's in what gets sent as a <em>key typed</em>
 824      * event, and this action tries to filter out the undesired
 825      * events.  This filters the control characters and those
 826      * with the ALT modifier.  It allows Control-Alt sequences
 827      * through as these form legitimate unicode characters on
 828      * some PC keyboards.
 829      * <p>
 830      * If the event doesn't get filtered, it will try to insert
 831      * content into the text editor.  The content is fetched
 832      * from the command string of the ActionEvent.  The text
 833      * entry is done through the <code>replaceSelection</code>
 834      * method on the target text component.  This is the
 835      * action that will be fired for most text entry tasks.
 836      * <p>
 837      * <strong>Warning:</strong>
 838      * Serialized objects of this class will not be compatible with
 839      * future Swing releases. The current serialization support is
 840      * appropriate for short term storage or RMI between applications running
 841      * the same version of Swing.  As of 1.4, support for long term storage
 842      * of all JavaBeans&trade;
 843      * has been added to the <code>java.beans</code> package.
 844      * Please see {@link java.beans.XMLEncoder}.
 845      *
 846      * @see DefaultEditorKit#defaultKeyTypedAction
 847      * @see DefaultEditorKit#getActions
 848      * @see Keymap#setDefaultAction
 849      * @see Keymap#getDefaultAction
 850      */
 851     @SuppressWarnings("serial") // Same-version serialization only
 852     public static class DefaultKeyTypedAction extends TextAction {
 853 
 854         /**
 855          * Creates this object with the appropriate identifier.
 856          */
 857         public DefaultKeyTypedAction() {
 858             super(defaultKeyTypedAction);
 859         }
 860 
 861         /**
 862          * The operation to perform when this action is triggered.
 863          *
 864          * @param e the action event
 865          */
 866         public void actionPerformed(ActionEvent e) {
 867             JTextComponent target = getTextComponent(e);
 868             if ((target != null) && (e != null)) {
 869                 if ((! target.isEditable()) || (! target.isEnabled())) {
 870                     return;
 871                 }
 872                 String content = e.getActionCommand();
 873                 int mod = e.getModifiers();
 874                 if ((content != null) && (content.length() > 0)) {
 875                     boolean isPrintableMask = true;
 876                     Toolkit tk = Toolkit.getDefaultToolkit();
 877                     if (tk instanceof SunToolkit) {
 878                         isPrintableMask = ((SunToolkit)tk).isPrintableCharacterModifiersMask(mod);
 879                     }
 880 
 881                     if (isPrintableMask) {
 882                         char c = content.charAt(0);
 883                         if ((c >= 0x20) && (c != 0x7F)) {
 884                             target.replaceSelection(content);
 885                         }
 886                     }
 887                 }
 888             }
 889         }
 890     }
 891 
 892     /**
 893      * Places content into the associated document.
 894      * If there is a selection, it is removed before
 895      * the new content is added.
 896      * <p>
 897      * <strong>Warning:</strong>
 898      * Serialized objects of this class will not be compatible with
 899      * future Swing releases. The current serialization support is
 900      * appropriate for short term storage or RMI between applications running
 901      * the same version of Swing.  As of 1.4, support for long term storage
 902      * of all JavaBeans&trade;
 903      * has been added to the <code>java.beans</code> package.
 904      * Please see {@link java.beans.XMLEncoder}.
 905      *
 906      * @see DefaultEditorKit#insertContentAction
 907      * @see DefaultEditorKit#getActions
 908      */
 909     @SuppressWarnings("serial") // Same-version serialization only
 910     public static class InsertContentAction extends TextAction {
 911 
 912         /**
 913          * Creates this object with the appropriate identifier.
 914          */
 915         public InsertContentAction() {
 916             super(insertContentAction);
 917         }
 918 
 919         /**
 920          * The operation to perform when this action is triggered.
 921          *
 922          * @param e the action event
 923          */
 924         public void actionPerformed(ActionEvent e) {
 925             JTextComponent target = getTextComponent(e);
 926             if ((target != null) && (e != null)) {
 927                 if ((! target.isEditable()) || (! target.isEnabled())) {
 928                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 929                     return;
 930                 }
 931                 String content = e.getActionCommand();
 932                 if (content != null) {
 933                     target.replaceSelection(content);
 934                 } else {
 935                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 936                 }
 937             }
 938         }
 939     }
 940 
 941     /**
 942      * Places a line/paragraph break into the document.
 943      * If there is a selection, it is removed before
 944      * the break is added.
 945      * <p>
 946      * <strong>Warning:</strong>
 947      * Serialized objects of this class will not be compatible with
 948      * future Swing releases. The current serialization support is
 949      * appropriate for short term storage or RMI between applications running
 950      * the same version of Swing.  As of 1.4, support for long term storage
 951      * of all JavaBeans&trade;
 952      * has been added to the <code>java.beans</code> package.
 953      * Please see {@link java.beans.XMLEncoder}.
 954      *
 955      * @see DefaultEditorKit#insertBreakAction
 956      * @see DefaultEditorKit#getActions
 957      */
 958     @SuppressWarnings("serial") // Same-version serialization only
 959     public static class InsertBreakAction extends TextAction {
 960 
 961         /**
 962          * Creates this object with the appropriate identifier.
 963          */
 964         public InsertBreakAction() {
 965             super(insertBreakAction);
 966         }
 967 
 968         /**
 969          * The operation to perform when this action is triggered.
 970          *
 971          * @param e the action event
 972          */
 973         public void actionPerformed(ActionEvent e) {
 974             JTextComponent target = getTextComponent(e);
 975             if (target != null) {
 976                 if ((! target.isEditable()) || (! target.isEnabled())) {
 977                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 978                     return;
 979                 }
 980                 target.replaceSelection("\n");
 981             }
 982         }
 983     }
 984 
 985     /**
 986      * Places a tab character into the document. If there
 987      * is a selection, it is removed before the tab is added.
 988      * <p>
 989      * <strong>Warning:</strong>
 990      * Serialized objects of this class will not be compatible with
 991      * future Swing releases. The current serialization support is
 992      * appropriate for short term storage or RMI between applications running
 993      * the same version of Swing.  As of 1.4, support for long term storage
 994      * of all JavaBeans&trade;
 995      * has been added to the <code>java.beans</code> package.
 996      * Please see {@link java.beans.XMLEncoder}.
 997      *
 998      * @see DefaultEditorKit#insertTabAction
 999      * @see DefaultEditorKit#getActions
1000      */
1001     @SuppressWarnings("serial") // Same-version serialization only
1002     public static class InsertTabAction extends TextAction {
1003 
1004         /**
1005          * Creates this object with the appropriate identifier.
1006          */
1007         public InsertTabAction() {
1008             super(insertTabAction);
1009         }
1010 
1011         /**
1012          * The operation to perform when this action is triggered.
1013          *
1014          * @param e the action event
1015          */
1016         public void actionPerformed(ActionEvent e) {
1017             JTextComponent target = getTextComponent(e);
1018             if (target != null) {
1019                 if ((! target.isEditable()) || (! target.isEnabled())) {
1020                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1021                     return;
1022                 }
1023                 target.replaceSelection("\t");
1024             }
1025         }
1026     }
1027 
1028     /*
1029      * Deletes the character of content that precedes the
1030      * current caret position.
1031      * @see DefaultEditorKit#deletePrevCharAction
1032      * @see DefaultEditorKit#getActions
1033      */
1034     @SuppressWarnings("serial") // Superclass is not serializable across versions
1035     static class DeletePrevCharAction extends TextAction {
1036 
1037         /**
1038          * Creates this object with the appropriate identifier.
1039          */
1040         DeletePrevCharAction() {
1041             super(deletePrevCharAction);
1042         }
1043 
1044         /**
1045          * The operation to perform when this action is triggered.
1046          *
1047          * @param e the action event
1048          */
1049         public void actionPerformed(ActionEvent e) {
1050             JTextComponent target = getTextComponent(e);
1051             boolean beep = true;
1052             if ((target != null) && (target.isEditable())) {
1053                 try {
1054                     Document doc = target.getDocument();
1055                     Caret caret = target.getCaret();
1056                     int dot = caret.getDot();
1057                     int mark = caret.getMark();
1058                     if (dot != mark) {
1059                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1060                         beep = false;
1061                     } else if (dot > 0) {
1062                         int delChars = 1;
1063 
1064                         if (dot > 1) {
1065                             String dotChars = doc.getText(dot - 2, 2);
1066                             char c0 = dotChars.charAt(0);
1067                             char c1 = dotChars.charAt(1);
1068 
1069                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1070                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1071                                 delChars = 2;
1072                             }
1073                         }
1074 
1075                         doc.remove(dot - delChars, delChars);
1076                         beep = false;
1077                     }
1078                 } catch (BadLocationException bl) {
1079                 }
1080             }
1081             if (beep) {
1082                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1083             }
1084         }
1085     }
1086 
1087     /*
1088      * Deletes the character of content that follows the
1089      * current caret position.
1090      * @see DefaultEditorKit#deleteNextCharAction
1091      * @see DefaultEditorKit#getActions
1092      */
1093     @SuppressWarnings("serial") // Superclass is not serializable across versions
1094     static class DeleteNextCharAction extends TextAction {
1095 
1096         /* Create this object with the appropriate identifier. */
1097         DeleteNextCharAction() {
1098             super(deleteNextCharAction);
1099         }
1100 
1101         /** The operation to perform when this action is triggered. */
1102         public void actionPerformed(ActionEvent e) {
1103             JTextComponent target = getTextComponent(e);
1104             boolean beep = true;
1105             if ((target != null) && (target.isEditable())) {
1106                 try {
1107                     Document doc = target.getDocument();
1108                     Caret caret = target.getCaret();
1109                     int dot = caret.getDot();
1110                     int mark = caret.getMark();
1111                     if (dot != mark) {
1112                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1113                         beep = false;
1114                     } else if (dot < doc.getLength()) {
1115                         int delChars = 1;
1116 
1117                         if (dot < doc.getLength() - 1) {
1118                             String dotChars = doc.getText(dot, 2);
1119                             char c0 = dotChars.charAt(0);
1120                             char c1 = dotChars.charAt(1);
1121 
1122                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1123                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1124                                 delChars = 2;
1125                             }
1126                         }
1127 
1128                         doc.remove(dot, delChars);
1129                         beep = false;
1130                     }
1131                 } catch (BadLocationException bl) {
1132                 }
1133             }
1134             if (beep) {
1135                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1136             }
1137         }
1138     }
1139 
1140 
1141     /*
1142      * Deletes the word that precedes/follows the beginning of the selection.
1143      * @see DefaultEditorKit#getActions
1144      */
1145     @SuppressWarnings("serial") // Superclass is not serializable across versions
1146     static class DeleteWordAction extends TextAction {
1147         DeleteWordAction(String name) {
1148             super(name);
1149             assert (name == deletePrevWordAction)
1150                 || (name == deleteNextWordAction);
1151         }
1152         /**
1153          * The operation to perform when this action is triggered.
1154          *
1155          * @param e the action event
1156          */
1157         public void actionPerformed(ActionEvent e) {
1158             final JTextComponent target = getTextComponent(e);
1159             if ((target != null) && (e != null)) {
1160                 if ((! target.isEditable()) || (! target.isEnabled())) {
1161                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1162                     return;
1163                 }
1164                 boolean beep = true;
1165                 try {
1166                     final int start = target.getSelectionStart();
1167                     final Element line =
1168                         Utilities.getParagraphElement(target, start);
1169                     int end;
1170                     if (deleteNextWordAction == getValue(Action.NAME)) {
1171                         end = Utilities.
1172                             getNextWordInParagraph(target, line, start, false);
1173                         if (end == java.text.BreakIterator.DONE) {
1174                             //last word in the paragraph
1175                             final int endOfLine = line.getEndOffset();
1176                             if (start == endOfLine - 1) {
1177                                 //for last position remove last \n
1178                                 end = endOfLine;
1179                             } else {
1180                                 //remove to the end of the paragraph
1181                                 end = endOfLine - 1;
1182                             }
1183                         }
1184                     } else {
1185                         end = Utilities.
1186                             getPrevWordInParagraph(target, line, start);
1187                         if (end == java.text.BreakIterator.DONE) {
1188                             //there is no previous word in the paragraph
1189                             final int startOfLine = line.getStartOffset();
1190                             if (start == startOfLine) {
1191                                 //for first position remove previous \n
1192                                 end = startOfLine - 1;
1193                             } else {
1194                                 //remove to the start of the paragraph
1195                                 end = startOfLine;
1196                             }
1197                         }
1198                     }
1199                     int offs = Math.min(start, end);
1200                     int len = Math.abs(end - start);
1201                     if (offs >= 0) {
1202                         target.getDocument().remove(offs, len);
1203                         beep = false;
1204                     }
1205                 } catch (BadLocationException ignore) {
1206                 }
1207                 if (beep) {
1208                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1209                 }
1210             }
1211         }
1212     }
1213 
1214 
1215     /*
1216      * Sets the editor into read-only mode.
1217      * @see DefaultEditorKit#readOnlyAction
1218      * @see DefaultEditorKit#getActions
1219      */
1220     @SuppressWarnings("serial") // Superclass is not serializable across versions
1221     static class ReadOnlyAction extends TextAction {
1222 
1223         /* Create this object with the appropriate identifier. */
1224         ReadOnlyAction() {
1225             super(readOnlyAction);
1226         }
1227 
1228         /**
1229          * The operation to perform when this action is triggered.
1230          *
1231          * @param e the action event
1232          */
1233         public void actionPerformed(ActionEvent e) {
1234             JTextComponent target = getTextComponent(e);
1235             if (target != null) {
1236                 target.setEditable(false);
1237             }
1238         }
1239     }
1240 
1241     /*
1242      * Sets the editor into writeable mode.
1243      * @see DefaultEditorKit#writableAction
1244      * @see DefaultEditorKit#getActions
1245      */
1246     @SuppressWarnings("serial") // Superclass is not serializable across versions
1247     static class WritableAction extends TextAction {
1248 
1249         /* Create this object with the appropriate identifier. */
1250         WritableAction() {
1251             super(writableAction);
1252         }
1253 
1254         /**
1255          * The operation to perform when this action is triggered.
1256          *
1257          * @param e the action event
1258          */
1259         public void actionPerformed(ActionEvent e) {
1260             JTextComponent target = getTextComponent(e);
1261             if (target != null) {
1262                 target.setEditable(true);
1263             }
1264         }
1265     }
1266 
1267     /**
1268      * Cuts the selected region and place its contents
1269      * into the system clipboard.
1270      * <p>
1271      * <strong>Warning:</strong>
1272      * Serialized objects of this class will not be compatible with
1273      * future Swing releases. The current serialization support is
1274      * appropriate for short term storage or RMI between applications running
1275      * the same version of Swing.  As of 1.4, support for long term storage
1276      * of all JavaBeans&trade;
1277      * has been added to the <code>java.beans</code> package.
1278      * Please see {@link java.beans.XMLEncoder}.
1279      *
1280      * @see DefaultEditorKit#cutAction
1281      * @see DefaultEditorKit#getActions
1282      */
1283     @SuppressWarnings("serial") // Same-version serialization only
1284     public static class CutAction extends TextAction {
1285 
1286         /** Create this object with the appropriate identifier. */
1287         public CutAction() {
1288             super(cutAction);
1289         }
1290 
1291         /**
1292          * The operation to perform when this action is triggered.
1293          *
1294          * @param e the action event
1295          */
1296         public void actionPerformed(ActionEvent e) {
1297             JTextComponent target = getTextComponent(e);
1298             if (target != null) {
1299                 target.cut();
1300             }
1301         }
1302     }
1303 
1304     /**
1305      * Copies the selected region and place its contents
1306      * into the system clipboard.
1307      * <p>
1308      * <strong>Warning:</strong>
1309      * Serialized objects of this class will not be compatible with
1310      * future Swing releases. The current serialization support is
1311      * appropriate for short term storage or RMI between applications running
1312      * the same version of Swing.  As of 1.4, support for long term storage
1313      * of all JavaBeans&trade;
1314      * has been added to the <code>java.beans</code> package.
1315      * Please see {@link java.beans.XMLEncoder}.
1316      *
1317      * @see DefaultEditorKit#copyAction
1318      * @see DefaultEditorKit#getActions
1319      */
1320     @SuppressWarnings("serial") // Same-version serialization only
1321     public static class CopyAction extends TextAction {
1322 
1323         /** Create this object with the appropriate identifier. */
1324         public CopyAction() {
1325             super(copyAction);
1326         }
1327 
1328         /**
1329          * The operation to perform when this action is triggered.
1330          *
1331          * @param e the action event
1332          */
1333         public void actionPerformed(ActionEvent e) {
1334             JTextComponent target = getTextComponent(e);
1335             if (target != null) {
1336                 target.copy();
1337             }
1338         }
1339     }
1340 
1341     /**
1342      * Pastes the contents of the system clipboard into the
1343      * selected region, or before the caret if nothing is
1344      * selected.
1345      * <p>
1346      * <strong>Warning:</strong>
1347      * Serialized objects of this class will not be compatible with
1348      * future Swing releases. The current serialization support is
1349      * appropriate for short term storage or RMI between applications running
1350      * the same version of Swing.  As of 1.4, support for long term storage
1351      * of all JavaBeans&trade;
1352      * has been added to the <code>java.beans</code> package.
1353      * Please see {@link java.beans.XMLEncoder}.
1354      *
1355      * @see DefaultEditorKit#pasteAction
1356      * @see DefaultEditorKit#getActions
1357      */
1358     @SuppressWarnings("serial") // Same-version serialization only
1359     public static class PasteAction extends TextAction {
1360 
1361         /** Create this object with the appropriate identifier. */
1362         public PasteAction() {
1363             super(pasteAction);
1364         }
1365 
1366         /**
1367          * The operation to perform when this action is triggered.
1368          *
1369          * @param e the action event
1370          */
1371         public void actionPerformed(ActionEvent e) {
1372             JTextComponent target = getTextComponent(e);
1373             if (target != null) {
1374                 target.paste();
1375             }
1376         }
1377     }
1378 
1379     /**
1380      * Creates a beep.
1381      * <p>
1382      * <strong>Warning:</strong>
1383      * Serialized objects of this class will not be compatible with
1384      * future Swing releases. The current serialization support is
1385      * appropriate for short term storage or RMI between applications running
1386      * the same version of Swing.  As of 1.4, support for long term storage
1387      * of all JavaBeans&trade;
1388      * has been added to the <code>java.beans</code> package.
1389      * Please see {@link java.beans.XMLEncoder}.
1390      *
1391      * @see DefaultEditorKit#beepAction
1392      * @see DefaultEditorKit#getActions
1393      */
1394     @SuppressWarnings("serial") // Same-version serialization only
1395     public static class BeepAction extends TextAction {
1396 
1397         /** Create this object with the appropriate identifier. */
1398         public BeepAction() {
1399             super(beepAction);
1400         }
1401 
1402         /**
1403          * The operation to perform when this action is triggered.
1404          *
1405          * @param e the action event
1406          */
1407         public void actionPerformed(ActionEvent e) {
1408             JTextComponent target = getTextComponent(e);
1409             UIManager.getLookAndFeel().provideErrorFeedback(target);
1410         }
1411     }
1412 
1413     /**
1414      * Scrolls up/down vertically.  The select version of this action extends
1415      * the selection, instead of simply moving the caret.
1416      *
1417      * @see DefaultEditorKit#pageUpAction
1418      * @see DefaultEditorKit#pageDownAction
1419      * @see DefaultEditorKit#getActions
1420      */
1421     @SuppressWarnings("serial") // Superclass is not serializable across versions
1422     static class VerticalPageAction extends TextAction {
1423 
1424         /** Create this object with the appropriate identifier. */
1425         public VerticalPageAction(String nm, int direction, boolean select) {
1426             super(nm);
1427             this.select = select;
1428             this.direction = direction;
1429         }
1430 
1431         /** The operation to perform when this action is triggered. */
1432         public void actionPerformed(ActionEvent e) {
1433             JTextComponent target = getTextComponent(e);
1434             if (target != null) {
1435                 Rectangle visible = target.getVisibleRect();
1436                 Rectangle newVis = new Rectangle(visible);
1437                 int selectedIndex = target.getCaretPosition();
1438                 int scrollAmount = direction *
1439                         target.getScrollableBlockIncrement(
1440                                   visible, SwingConstants.VERTICAL, direction);
1441                 int initialY = visible.y;
1442                 Caret caret = target.getCaret();
1443                 Point magicPosition = caret.getMagicCaretPosition();
1444 
1445                 if (selectedIndex != -1) {
1446                     try {
1447                         Rectangle dotBounds = target.modelToView(
1448                                                      selectedIndex);
1449                         int x = (magicPosition != null) ? magicPosition.x :
1450                                                           dotBounds.x;
1451                         int h = dotBounds.height;
1452                         if (h > 0) {
1453                             // We want to scroll by a multiple of caret height,
1454                             // rounding towards lower integer
1455                             scrollAmount = scrollAmount / h * h;
1456                         }
1457                         newVis.y = constrainY(target,
1458                                 initialY + scrollAmount, visible.height);
1459 
1460                         int newIndex;
1461 
1462                         if (visible.contains(dotBounds.x, dotBounds.y)) {
1463                             // Dot is currently visible, base the new
1464                             // location off the old, or
1465                             newIndex = target.viewToModel(
1466                                 new Point(x, constrainY(target,
1467                                           dotBounds.y + scrollAmount, 0)));
1468                         }
1469                         else {
1470                             // Dot isn't visible, choose the top or the bottom
1471                             // for the new location.
1472                             if (direction == -1) {
1473                                 newIndex = target.viewToModel(new Point(
1474                                     x, newVis.y));
1475                             }
1476                             else {
1477                                 newIndex = target.viewToModel(new Point(
1478                                     x, newVis.y + visible.height));
1479                             }
1480                         }
1481                         newIndex = constrainOffset(target, newIndex);
1482                         if (newIndex != selectedIndex) {
1483                             // Make sure the new visible location contains
1484                             // the location of dot, otherwise Caret will
1485                             // cause an additional scroll.
1486                             int newY = getAdjustedY(target, newVis, newIndex);
1487 
1488                             if (direction == -1 && newY <= initialY || direction == 1 && newY >= initialY) {
1489                                 // Change index and correct newVis.y only if won't cause scrolling upward
1490                                 newVis.y = newY;
1491 
1492                                 if (select) {
1493                                     target.moveCaretPosition(newIndex);
1494                                 } else {
1495                                     target.setCaretPosition(newIndex);
1496                                 }
1497                             }
1498                         }
1499                     } catch (BadLocationException ble) { }
1500                 } else {
1501                     newVis.y = constrainY(target,
1502                             initialY + scrollAmount, visible.height);
1503                 }
1504                 if (magicPosition != null) {
1505                     caret.setMagicCaretPosition(magicPosition);
1506                 }
1507                 target.scrollRectToVisible(newVis);
1508             }
1509         }
1510 
1511         /**
1512          * Makes sure <code>y</code> is a valid location in
1513          * <code>target</code>.
1514          */
1515         private int constrainY(JTextComponent target, int y, int vis) {
1516             if (y < 0) {
1517                 y = 0;
1518             }
1519             else if (y + vis > target.getHeight()) {
1520                 y = Math.max(0, target.getHeight() - vis);
1521             }
1522             return y;
1523         }
1524 
1525         /**
1526          * Ensures that <code>offset</code> is a valid offset into the
1527          * model for <code>text</code>.
1528          */
1529         private int constrainOffset(JTextComponent text, int offset) {
1530             Document doc = text.getDocument();
1531 
1532             if ((offset != 0) && (offset > doc.getLength())) {
1533                 offset = doc.getLength();
1534             }
1535             if (offset  < 0) {
1536                 offset = 0;
1537             }
1538             return offset;
1539         }
1540 
1541         /**
1542          * Returns adjustsed {@code y} position that indicates the location to scroll to
1543          * after selecting <code>index</code>.
1544          */
1545         private int getAdjustedY(JTextComponent text, Rectangle visible, int index) {
1546             int result = visible.y;
1547 
1548             try {
1549                 Rectangle dotBounds = text.modelToView(index);
1550 
1551                 if (dotBounds.y < visible.y) {
1552                     result = dotBounds.y;
1553                 } else {
1554                     if ((dotBounds.y > visible.y + visible.height) ||
1555                             (dotBounds.y + dotBounds.height > visible.y + visible.height)) {
1556                         result = dotBounds.y + dotBounds.height - visible.height;
1557                     }
1558                 }
1559             } catch (BadLocationException ble) {
1560             }
1561 
1562             return result;
1563         }
1564 
1565         /**
1566          * Adjusts the Rectangle to contain the bounds of the character at
1567          * <code>index</code> in response to a page up.
1568          */
1569         private boolean select;
1570 
1571         /**
1572          * Direction to scroll, 1 is down, -1 is up.
1573          */
1574         private int direction;
1575     }
1576 
1577 
1578     /**
1579      * Pages one view to the left or right.
1580      */
1581     @SuppressWarnings("serial") // Superclass is not serializable across versions
1582     static class PageAction extends TextAction {
1583 
1584         /** Create this object with the appropriate identifier. */
1585         public PageAction(String nm, boolean left, boolean select) {
1586             super(nm);
1587             this.select = select;
1588             this.left = left;
1589         }
1590 
1591         /** The operation to perform when this action is triggered. */
1592         public void actionPerformed(ActionEvent e) {
1593             JTextComponent target = getTextComponent(e);
1594             if (target != null) {
1595                 int selectedIndex;
1596                 Rectangle visible = new Rectangle();
1597                 target.computeVisibleRect(visible);
1598                 if (left) {
1599                     visible.x = Math.max(0, visible.x - visible.width);
1600                 }
1601                 else {
1602                     visible.x += visible.width;
1603                 }
1604 
1605                 selectedIndex = target.getCaretPosition();
1606                 if(selectedIndex != -1) {
1607                     if (left) {
1608                         selectedIndex = target.viewToModel
1609                             (new Point(visible.x, visible.y));
1610                     }
1611                     else {
1612                         selectedIndex = target.viewToModel
1613                             (new Point(visible.x + visible.width - 1,
1614                                        visible.y + visible.height - 1));
1615                     }
1616                     Document doc = target.getDocument();
1617                     if ((selectedIndex != 0) &&
1618                         (selectedIndex  > (doc.getLength()-1))) {
1619                         selectedIndex = doc.getLength()-1;
1620                     }
1621                     else if(selectedIndex  < 0) {
1622                         selectedIndex = 0;
1623                     }
1624                     if (select)
1625                         target.moveCaretPosition(selectedIndex);
1626                     else
1627                         target.setCaretPosition(selectedIndex);
1628                 }
1629             }
1630         }
1631 
1632         private boolean select;
1633         private boolean left;
1634     }
1635 
1636     @SuppressWarnings("serial") // Superclass is not serializable across versions
1637     static class DumpModelAction extends TextAction {
1638 
1639         DumpModelAction() {
1640             super("dump-model");
1641         }
1642 
1643         public void actionPerformed(ActionEvent e) {
1644             JTextComponent target = getTextComponent(e);
1645             if (target != null) {
1646                 Document d = target.getDocument();
1647                 if (d instanceof AbstractDocument) {
1648                     ((AbstractDocument) d).dump(System.err);
1649                 }
1650             }
1651         }
1652     }
1653 
1654     /*
1655      * Action to move the selection by way of the
1656      * getNextVisualPositionFrom method. Constructor indicates direction
1657      * to use.
1658      */
1659     @SuppressWarnings("serial") // Superclass is not serializable across versions
1660     static class NextVisualPositionAction extends TextAction {
1661 
1662         /**
1663          * Create this action with the appropriate identifier.
1664          * @param nm  the name of the action, Action.NAME.
1665          * @param select whether to extend the selection when
1666          *  changing the caret position.
1667          */
1668         NextVisualPositionAction(String nm, boolean select, int direction) {
1669             super(nm);
1670             this.select = select;
1671             this.direction = direction;
1672         }
1673 
1674         /** The operation to perform when this action is triggered. */
1675         public void actionPerformed(ActionEvent e) {
1676             JTextComponent target = getTextComponent(e);
1677             if (target != null) {
1678                 Caret caret = target.getCaret();
1679                 DefaultCaret bidiCaret = (caret instanceof DefaultCaret) ?
1680                                               (DefaultCaret)caret : null;
1681                 int dot = caret.getDot();
1682                 Position.Bias[] bias = new Position.Bias[1];
1683                 Point magicPosition = caret.getMagicCaretPosition();
1684 
1685                 try {
1686                     if(magicPosition == null &&
1687                        (direction == SwingConstants.NORTH ||
1688                         direction == SwingConstants.SOUTH)) {
1689                         Rectangle r = (bidiCaret != null) ?
1690                                 target.getUI().modelToView(target, dot,
1691                                                       bidiCaret.getDotBias()) :
1692                                 target.modelToView(dot);
1693                         magicPosition = new Point(r.x, r.y);
1694                     }
1695 
1696                     NavigationFilter filter = target.getNavigationFilter();
1697 
1698                     if (filter != null) {
1699                         dot = filter.getNextVisualPositionFrom
1700                                      (target, dot, (bidiCaret != null) ?
1701                                       bidiCaret.getDotBias() :
1702                                       Position.Bias.Forward, direction, bias);
1703                     }
1704                     else {
1705                         dot = target.getUI().getNextVisualPositionFrom
1706                                      (target, dot, (bidiCaret != null) ?
1707                                       bidiCaret.getDotBias() :
1708                                       Position.Bias.Forward, direction, bias);
1709                     }
1710                     if(bias[0] == null) {
1711                         bias[0] = Position.Bias.Forward;
1712                     }
1713                     if(bidiCaret != null) {
1714                         if (select) {
1715                             bidiCaret.moveDot(dot, bias[0]);
1716                         } else {
1717                             bidiCaret.setDot(dot, bias[0]);
1718                         }
1719                     }
1720                     else {
1721                         if (select) {
1722                             caret.moveDot(dot);
1723                         } else {
1724                             caret.setDot(dot);
1725                         }
1726                     }
1727                     if(magicPosition != null &&
1728                        (direction == SwingConstants.NORTH ||
1729                         direction == SwingConstants.SOUTH)) {
1730                         target.getCaret().setMagicCaretPosition(magicPosition);
1731                     }
1732                 } catch (BadLocationException ex) {
1733                 }
1734             }
1735         }
1736 
1737         private boolean select;
1738         private int direction;
1739     }
1740 
1741     /*
1742      * Position the caret to the beginning of the word.
1743      * @see DefaultEditorKit#beginWordAction
1744      * @see DefaultEditorKit#selectBeginWordAction
1745      * @see DefaultEditorKit#getActions
1746      */
1747     @SuppressWarnings("serial") // Superclass is not serializable across versions
1748     static class BeginWordAction extends TextAction {
1749 
1750         /**
1751          * Create this action with the appropriate identifier.
1752          * @param nm  the name of the action, Action.NAME.
1753          * @param select whether to extend the selection when
1754          *  changing the caret position.
1755          */
1756         BeginWordAction(String nm, boolean select) {
1757             super(nm);
1758             this.select = select;
1759         }
1760 
1761         /** The operation to perform when this action is triggered. */
1762         public void actionPerformed(ActionEvent e) {
1763             JTextComponent target = getTextComponent(e);
1764             if (target != null) {
1765                 try {
1766                     int offs = target.getCaretPosition();
1767                     int begOffs = Utilities.getWordStart(target, offs);
1768                     if (select) {
1769                         target.moveCaretPosition(begOffs);
1770                     } else {
1771                         target.setCaretPosition(begOffs);
1772                     }
1773                 } catch (BadLocationException bl) {
1774                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1775                 }
1776             }
1777         }
1778 
1779         private boolean select;
1780     }
1781 
1782     /*
1783      * Position the caret to the end of the word.
1784      * @see DefaultEditorKit#endWordAction
1785      * @see DefaultEditorKit#selectEndWordAction
1786      * @see DefaultEditorKit#getActions
1787      */
1788     @SuppressWarnings("serial") // Superclass is not serializable across versions
1789     static class EndWordAction extends TextAction {
1790 
1791         /**
1792          * Create this action with the appropriate identifier.
1793          * @param nm  the name of the action, Action.NAME.
1794          * @param select whether to extend the selection when
1795          *  changing the caret position.
1796          */
1797         EndWordAction(String nm, boolean select) {
1798             super(nm);
1799             this.select = select;
1800         }
1801 
1802         /** The operation to perform when this action is triggered. */
1803         public void actionPerformed(ActionEvent e) {
1804             JTextComponent target = getTextComponent(e);
1805             if (target != null) {
1806                 try {
1807                     int offs = target.getCaretPosition();
1808                     int endOffs = Utilities.getWordEnd(target, offs);
1809                     if (select) {
1810                         target.moveCaretPosition(endOffs);
1811                     } else {
1812                         target.setCaretPosition(endOffs);
1813                     }
1814                 } catch (BadLocationException bl) {
1815                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1816                 }
1817             }
1818         }
1819 
1820         private boolean select;
1821     }
1822 
1823     /*
1824      * Position the caret to the beginning of the previous word.
1825      * @see DefaultEditorKit#previousWordAction
1826      * @see DefaultEditorKit#selectPreviousWordAction
1827      * @see DefaultEditorKit#getActions
1828      */
1829     @SuppressWarnings("serial") // Superclass is not serializable across versions
1830     static class PreviousWordAction extends TextAction {
1831 
1832         /**
1833          * Create this action with the appropriate identifier.
1834          * @param nm  the name of the action, Action.NAME.
1835          * @param select whether to extend the selection when
1836          *  changing the caret position.
1837          */
1838         PreviousWordAction(String nm, boolean select) {
1839             super(nm);
1840             this.select = select;
1841         }
1842 
1843         /** The operation to perform when this action is triggered. */
1844         public void actionPerformed(ActionEvent e) {
1845             JTextComponent target = getTextComponent(e);
1846             if (target != null) {
1847                 int offs = target.getCaretPosition();
1848                 boolean failed = false;
1849                 try {
1850                     Element curPara =
1851                             Utilities.getParagraphElement(target, offs);
1852                     offs = Utilities.getPreviousWord(target, offs);
1853                     if(offs < curPara.getStartOffset()) {
1854                         // we should first move to the end of the
1855                         // previous paragraph (bug #4278839)
1856                         offs = Utilities.getParagraphElement(target, offs).
1857                                 getEndOffset() - 1;
1858                     }
1859                 } catch (BadLocationException bl) {
1860                     if (offs != 0) {
1861                         offs = 0;
1862                     }
1863                     else {
1864                         failed = true;
1865                     }
1866                 }
1867                 if (!failed) {
1868                     if (select) {
1869                         target.moveCaretPosition(offs);
1870                     } else {
1871                         target.setCaretPosition(offs);
1872                     }
1873                 }
1874                 else {
1875                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1876                 }
1877             }
1878         }
1879 
1880         private boolean select;
1881     }
1882 
1883     /*
1884      * Position the caret to the next of the word.
1885      * @see DefaultEditorKit#nextWordAction
1886      * @see DefaultEditorKit#selectNextWordAction
1887      * @see DefaultEditorKit#getActions
1888      */
1889     @SuppressWarnings("serial") // Superclass is not serializable across versions
1890     static class NextWordAction extends TextAction {
1891 
1892         /**
1893          * Create this action with the appropriate identifier.
1894          * @param nm  the name of the action, Action.NAME.
1895          * @param select whether to extend the selection when
1896          *  changing the caret position.
1897          */
1898         NextWordAction(String nm, boolean select) {
1899             super(nm);
1900             this.select = select;
1901         }
1902 
1903         /** The operation to perform when this action is triggered. */
1904         public void actionPerformed(ActionEvent e) {
1905             JTextComponent target = getTextComponent(e);
1906             if (target != null) {
1907                 int offs = target.getCaretPosition();
1908                 boolean failed = false;
1909                 int oldOffs = offs;
1910                 Element curPara =
1911                         Utilities.getParagraphElement(target, offs);
1912                 try {
1913                     offs = Utilities.getNextWord(target, offs);
1914                     if(offs >= curPara.getEndOffset() &&
1915                             oldOffs != curPara.getEndOffset() - 1) {
1916                         // we should first move to the end of current
1917                         // paragraph (bug #4278839)
1918                         offs = curPara.getEndOffset() - 1;
1919                     }
1920                 } catch (BadLocationException bl) {
1921                     int end = target.getDocument().getLength();
1922                     if (offs != end) {
1923                         if(oldOffs != curPara.getEndOffset() - 1) {
1924                             offs = curPara.getEndOffset() - 1;
1925                         } else {
1926                         offs = end;
1927                     }
1928                     }
1929                     else {
1930                         failed = true;
1931                     }
1932                 }
1933                 if (!failed) {
1934                     if (select) {
1935                         target.moveCaretPosition(offs);
1936                     } else {
1937                         target.setCaretPosition(offs);
1938                     }
1939                 }
1940                 else {
1941                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1942                 }
1943             }
1944         }
1945 
1946         private boolean select;
1947     }
1948 
1949     /*
1950      * Position the caret to the beginning of the line.
1951      * @see DefaultEditorKit#beginLineAction
1952      * @see DefaultEditorKit#selectBeginLineAction
1953      * @see DefaultEditorKit#getActions
1954      */
1955     @SuppressWarnings("serial") // Superclass is not serializable across versions
1956     static class BeginLineAction extends TextAction {
1957 
1958         /**
1959          * Create this action with the appropriate identifier.
1960          * @param nm  the name of the action, Action.NAME.
1961          * @param select whether to extend the selection when
1962          *  changing the caret position.
1963          */
1964         BeginLineAction(String nm, boolean select) {
1965             super(nm);
1966             this.select = select;
1967         }
1968 
1969         /** The operation to perform when this action is triggered. */
1970         public void actionPerformed(ActionEvent e) {
1971             JTextComponent target = getTextComponent(e);
1972             if (target != null) {
1973                 try {
1974                     int offs = target.getCaretPosition();
1975                     int begOffs = Utilities.getRowStart(target, offs);
1976                     if (select) {
1977                         target.moveCaretPosition(begOffs);
1978                     } else {
1979                         target.setCaretPosition(begOffs);
1980                     }
1981                 } catch (BadLocationException bl) {
1982                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1983                 }
1984             }
1985         }
1986 
1987         private boolean select;
1988     }
1989 
1990     /*
1991      * Position the caret to the end of the line.
1992      * @see DefaultEditorKit#endLineAction
1993      * @see DefaultEditorKit#selectEndLineAction
1994      * @see DefaultEditorKit#getActions
1995      */
1996     @SuppressWarnings("serial") // Superclass is not serializable across versions
1997     static class EndLineAction extends TextAction {
1998 
1999         /**
2000          * Create this action with the appropriate identifier.
2001          * @param nm  the name of the action, Action.NAME.
2002          * @param select whether to extend the selection when
2003          *  changing the caret position.
2004          */
2005         EndLineAction(String nm, boolean select) {
2006             super(nm);
2007             this.select = select;
2008         }
2009 
2010         /** The operation to perform when this action is triggered. */
2011         public void actionPerformed(ActionEvent e) {
2012             JTextComponent target = getTextComponent(e);
2013             if (target != null) {
2014                 try {
2015                     int offs = target.getCaretPosition();
2016                     int endOffs = Utilities.getRowEnd(target, offs);
2017                     if (select) {
2018                         target.moveCaretPosition(endOffs);
2019                     } else {
2020                         target.setCaretPosition(endOffs);
2021                     }
2022                 } catch (BadLocationException bl) {
2023                     UIManager.getLookAndFeel().provideErrorFeedback(target);
2024                 }
2025             }
2026         }
2027 
2028         private boolean select;
2029     }
2030 
2031     /*
2032      * Position the caret to the beginning of the paragraph.
2033      * @see DefaultEditorKit#beginParagraphAction
2034      * @see DefaultEditorKit#selectBeginParagraphAction
2035      * @see DefaultEditorKit#getActions
2036      */
2037     @SuppressWarnings("serial") // Superclass is not serializable across versions
2038     static class BeginParagraphAction extends TextAction {
2039 
2040         /**
2041          * Create this action with the appropriate identifier.
2042          * @param nm  the name of the action, Action.NAME.
2043          * @param select whether to extend the selection when
2044          *  changing the caret position.
2045          */
2046         BeginParagraphAction(String nm, boolean select) {
2047             super(nm);
2048             this.select = select;
2049         }
2050 
2051         /** The operation to perform when this action is triggered. */
2052         public void actionPerformed(ActionEvent e) {
2053             JTextComponent target = getTextComponent(e);
2054             if (target != null) {
2055                 int offs = target.getCaretPosition();
2056                 Element elem = Utilities.getParagraphElement(target, offs);
2057                 offs = elem.getStartOffset();
2058                 if (select) {
2059                     target.moveCaretPosition(offs);
2060                 } else {
2061                     target.setCaretPosition(offs);
2062                 }
2063             }
2064         }
2065 
2066         private boolean select;
2067     }
2068 
2069     /*
2070      * Position the caret to the end of the paragraph.
2071      * @see DefaultEditorKit#endParagraphAction
2072      * @see DefaultEditorKit#selectEndParagraphAction
2073      * @see DefaultEditorKit#getActions
2074      */
2075     @SuppressWarnings("serial") // Superclass is not serializable across versions
2076     static class EndParagraphAction extends TextAction {
2077 
2078         /**
2079          * Create this action with the appropriate identifier.
2080          * @param nm  the name of the action, Action.NAME.
2081          * @param select whether to extend the selection when
2082          *  changing the caret position.
2083          */
2084         EndParagraphAction(String nm, boolean select) {
2085             super(nm);
2086             this.select = select;
2087         }
2088 
2089         /** The operation to perform when this action is triggered. */
2090         public void actionPerformed(ActionEvent e) {
2091             JTextComponent target = getTextComponent(e);
2092             if (target != null) {
2093                 int offs = target.getCaretPosition();
2094                 Element elem = Utilities.getParagraphElement(target, offs);
2095                 offs = Math.min(target.getDocument().getLength(),
2096                                 elem.getEndOffset());
2097                 if (select) {
2098                     target.moveCaretPosition(offs);
2099                 } else {
2100                     target.setCaretPosition(offs);
2101                 }
2102             }
2103         }
2104 
2105         private boolean select;
2106     }
2107 
2108     /*
2109      * Move the caret to the beginning of the document.
2110      * @see DefaultEditorKit#beginAction
2111      * @see DefaultEditorKit#getActions
2112      */
2113     @SuppressWarnings("serial") // Superclass is not serializable across versions
2114     static class BeginAction extends TextAction {
2115 
2116         /* Create this object with the appropriate identifier. */
2117         BeginAction(String nm, boolean select) {
2118             super(nm);
2119             this.select = select;
2120         }
2121 
2122         /** The operation to perform when this action is triggered. */
2123         public void actionPerformed(ActionEvent e) {
2124             JTextComponent target = getTextComponent(e);
2125             if (target != null) {
2126                 if (select) {
2127                     target.moveCaretPosition(0);
2128                 } else {
2129                     target.setCaretPosition(0);
2130                 }
2131             }
2132         }
2133 
2134         private boolean select;
2135     }
2136 
2137     /*
2138      * Move the caret to the end of the document.
2139      * @see DefaultEditorKit#endAction
2140      * @see DefaultEditorKit#getActions
2141      */
2142     @SuppressWarnings("serial") // Superclass is not serializable across versions
2143     static class EndAction extends TextAction {
2144 
2145         /* Create this object with the appropriate identifier. */
2146         EndAction(String nm, boolean select) {
2147             super(nm);
2148             this.select = select;
2149         }
2150 
2151         /** The operation to perform when this action is triggered. */
2152         public void actionPerformed(ActionEvent e) {
2153             JTextComponent target = getTextComponent(e);
2154             if (target != null) {
2155                 Document doc = target.getDocument();
2156                 int dot = doc.getLength();
2157                 if (select) {
2158                     target.moveCaretPosition(dot);
2159                 } else {
2160                     target.setCaretPosition(dot);
2161                 }
2162             }
2163         }
2164 
2165         private boolean select;
2166     }
2167 
2168     /*
2169      * Select the word around the caret
2170      * @see DefaultEditorKit#endAction
2171      * @see DefaultEditorKit#getActions
2172      */
2173     @SuppressWarnings("serial") // Superclass is not serializable across versions
2174     static class SelectWordAction extends TextAction {
2175 
2176         /**
2177          * Create this action with the appropriate identifier.
2178          * @param nm  the name of the action, Action.NAME.
2179          * @param select whether to extend the selection when
2180          *  changing the caret position.
2181          */
2182         SelectWordAction() {
2183             super(selectWordAction);
2184             start = new BeginWordAction("pigdog", false);
2185             end = new EndWordAction("pigdog", true);
2186         }
2187 
2188         /** The operation to perform when this action is triggered. */
2189         public void actionPerformed(ActionEvent e) {
2190             start.actionPerformed(e);
2191             end.actionPerformed(e);
2192         }
2193 
2194         private Action start;
2195         private Action end;
2196     }
2197 
2198     /*
2199      * Select the line around the caret
2200      * @see DefaultEditorKit#endAction
2201      * @see DefaultEditorKit#getActions
2202      */
2203     @SuppressWarnings("serial") // Superclass is not serializable across versions
2204     static class SelectLineAction extends TextAction {
2205 
2206         /**
2207          * Create this action with the appropriate identifier.
2208          * @param nm  the name of the action, Action.NAME.
2209          * @param select whether to extend the selection when
2210          *  changing the caret position.
2211          */
2212         SelectLineAction() {
2213             super(selectLineAction);
2214             start = new BeginLineAction("pigdog", false);
2215             end = new EndLineAction("pigdog", true);
2216         }
2217 
2218         /** The operation to perform when this action is triggered. */
2219         public void actionPerformed(ActionEvent e) {
2220             start.actionPerformed(e);
2221             end.actionPerformed(e);
2222         }
2223 
2224         private Action start;
2225         private Action end;
2226     }
2227 
2228     /*
2229      * Select the paragraph around the caret
2230      * @see DefaultEditorKit#endAction
2231      * @see DefaultEditorKit#getActions
2232      */
2233     @SuppressWarnings("serial") // Superclass is not serializable across versions
2234     static class SelectParagraphAction extends TextAction {
2235 
2236         /**
2237          * Create this action with the appropriate identifier.
2238          * @param nm  the name of the action, Action.NAME.
2239          * @param select whether to extend the selection when
2240          *  changing the caret position.
2241          */
2242         SelectParagraphAction() {
2243             super(selectParagraphAction);
2244             start = new BeginParagraphAction("pigdog", false);
2245             end = new EndParagraphAction("pigdog", true);
2246         }
2247 
2248         /** The operation to perform when this action is triggered. */
2249         public void actionPerformed(ActionEvent e) {
2250             start.actionPerformed(e);
2251             end.actionPerformed(e);
2252         }
2253 
2254         private Action start;
2255         private Action end;
2256     }
2257 
2258     /*
2259      * Select the entire document
2260      * @see DefaultEditorKit#endAction
2261      * @see DefaultEditorKit#getActions
2262      */
2263     @SuppressWarnings("serial") // Superclass is not serializable across versions
2264     static class SelectAllAction extends TextAction {
2265 
2266         /**
2267          * Create this action with the appropriate identifier.
2268          * @param nm  the name of the action, Action.NAME.
2269          * @param select whether to extend the selection when
2270          *  changing the caret position.
2271          */
2272         SelectAllAction() {
2273             super(selectAllAction);
2274         }
2275 
2276         /** The operation to perform when this action is triggered. */
2277         public void actionPerformed(ActionEvent e) {
2278             JTextComponent target = getTextComponent(e);
2279             if (target != null) {
2280                 Document doc = target.getDocument();
2281                 target.setCaretPosition(0);
2282                 target.moveCaretPosition(doc.getLength());
2283             }
2284         }
2285 
2286     }
2287 
2288     /*
2289      * Remove the selection, if any.
2290      * @see DefaultEditorKit#unselectAction
2291      * @see DefaultEditorKit#getActions
2292      */
2293     @SuppressWarnings("serial") // Superclass is not serializable across versions
2294     static class UnselectAction extends TextAction {
2295 
2296         /**
2297          * Create this action with the appropriate identifier.
2298          */
2299         UnselectAction() {
2300             super(unselectAction);
2301         }
2302 
2303         /** The operation to perform when this action is triggered. */
2304         public void actionPerformed(ActionEvent e) {
2305             JTextComponent target = getTextComponent(e);
2306             if (target != null) {
2307                 target.setCaretPosition(target.getCaretPosition());
2308             }
2309         }
2310 
2311     }
2312 
2313     /*
2314      * Toggles the ComponentOrientation of the text component.
2315      * @see DefaultEditorKit#toggleComponentOrientationAction
2316      * @see DefaultEditorKit#getActions
2317      */
2318     @SuppressWarnings("serial") // Superclass is not serializable across versions
2319     static class ToggleComponentOrientationAction extends TextAction {
2320 
2321         /**
2322          * Create this action with the appropriate identifier.
2323          */
2324         ToggleComponentOrientationAction() {
2325             super(toggleComponentOrientationAction);
2326         }
2327 
2328         /** The operation to perform when this action is triggered. */
2329         public void actionPerformed(ActionEvent e) {
2330             JTextComponent target = getTextComponent(e);
2331             if (target != null) {
2332                 ComponentOrientation last = target.getComponentOrientation();
2333                 ComponentOrientation next;
2334                 if( last == ComponentOrientation.RIGHT_TO_LEFT )
2335                     next = ComponentOrientation.LEFT_TO_RIGHT;
2336                 else
2337                     next = ComponentOrientation.RIGHT_TO_LEFT;
2338                 target.setComponentOrientation(next);
2339                 target.repaint();
2340             }
2341         }
2342     }
2343 
2344 }