1 /*
   2  * Copyright (c) 1997, 2017, 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.clone();
 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                     char c = content.charAt(0);
 882                     if ((isPrintableMask && (c >= 0x20) && (c != 0x7F)) ||
 883                         (!isPrintableMask && (c >= 0x200C) && (c <= 0x200D))) {
 884                         target.replaceSelection(content);
 885                     }
 886                 }
 887             }
 888         }
 889     }
 890 
 891     /**
 892      * Places content into the associated document.
 893      * If there is a selection, it is removed before
 894      * the new content is added.
 895      * <p>
 896      * <strong>Warning:</strong>
 897      * Serialized objects of this class will not be compatible with
 898      * future Swing releases. The current serialization support is
 899      * appropriate for short term storage or RMI between applications running
 900      * the same version of Swing.  As of 1.4, support for long term storage
 901      * of all JavaBeans&trade;
 902      * has been added to the <code>java.beans</code> package.
 903      * Please see {@link java.beans.XMLEncoder}.
 904      *
 905      * @see DefaultEditorKit#insertContentAction
 906      * @see DefaultEditorKit#getActions
 907      */
 908     @SuppressWarnings("serial") // Same-version serialization only
 909     public static class InsertContentAction extends TextAction {
 910 
 911         /**
 912          * Creates this object with the appropriate identifier.
 913          */
 914         public InsertContentAction() {
 915             super(insertContentAction);
 916         }
 917 
 918         /**
 919          * The operation to perform when this action is triggered.
 920          *
 921          * @param e the action event
 922          */
 923         public void actionPerformed(ActionEvent e) {
 924             JTextComponent target = getTextComponent(e);
 925             if ((target != null) && (e != null)) {
 926                 if ((! target.isEditable()) || (! target.isEnabled())) {
 927                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 928                     return;
 929                 }
 930                 String content = e.getActionCommand();
 931                 if (content != null) {
 932                     target.replaceSelection(content);
 933                 } else {
 934                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 935                 }
 936             }
 937         }
 938     }
 939 
 940     /**
 941      * Places a line/paragraph break into the document.
 942      * If there is a selection, it is removed before
 943      * the break is added.
 944      * <p>
 945      * <strong>Warning:</strong>
 946      * Serialized objects of this class will not be compatible with
 947      * future Swing releases. The current serialization support is
 948      * appropriate for short term storage or RMI between applications running
 949      * the same version of Swing.  As of 1.4, support for long term storage
 950      * of all JavaBeans&trade;
 951      * has been added to the <code>java.beans</code> package.
 952      * Please see {@link java.beans.XMLEncoder}.
 953      *
 954      * @see DefaultEditorKit#insertBreakAction
 955      * @see DefaultEditorKit#getActions
 956      */
 957     @SuppressWarnings("serial") // Same-version serialization only
 958     public static class InsertBreakAction extends TextAction {
 959 
 960         /**
 961          * Creates this object with the appropriate identifier.
 962          */
 963         public InsertBreakAction() {
 964             super(insertBreakAction);
 965         }
 966 
 967         /**
 968          * The operation to perform when this action is triggered.
 969          *
 970          * @param e the action event
 971          */
 972         public void actionPerformed(ActionEvent e) {
 973             JTextComponent target = getTextComponent(e);
 974             if (target != null) {
 975                 if ((! target.isEditable()) || (! target.isEnabled())) {
 976                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 977                     return;
 978                 }
 979                 target.replaceSelection("\n");
 980             }
 981         }
 982     }
 983 
 984     /**
 985      * Places a tab character into the document. If there
 986      * is a selection, it is removed before the tab is added.
 987      * <p>
 988      * <strong>Warning:</strong>
 989      * Serialized objects of this class will not be compatible with
 990      * future Swing releases. The current serialization support is
 991      * appropriate for short term storage or RMI between applications running
 992      * the same version of Swing.  As of 1.4, support for long term storage
 993      * of all JavaBeans&trade;
 994      * has been added to the <code>java.beans</code> package.
 995      * Please see {@link java.beans.XMLEncoder}.
 996      *
 997      * @see DefaultEditorKit#insertTabAction
 998      * @see DefaultEditorKit#getActions
 999      */
1000     @SuppressWarnings("serial") // Same-version serialization only
1001     public static class InsertTabAction extends TextAction {
1002 
1003         /**
1004          * Creates this object with the appropriate identifier.
1005          */
1006         public InsertTabAction() {
1007             super(insertTabAction);
1008         }
1009 
1010         /**
1011          * The operation to perform when this action is triggered.
1012          *
1013          * @param e the action event
1014          */
1015         public void actionPerformed(ActionEvent e) {
1016             JTextComponent target = getTextComponent(e);
1017             if (target != null) {
1018                 if ((! target.isEditable()) || (! target.isEnabled())) {
1019                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1020                     return;
1021                 }
1022                 target.replaceSelection("\t");
1023             }
1024         }
1025     }
1026 
1027     /*
1028      * Deletes the character of content that precedes the
1029      * current caret position.
1030      * @see DefaultEditorKit#deletePrevCharAction
1031      * @see DefaultEditorKit#getActions
1032      */
1033     @SuppressWarnings("serial") // Superclass is not serializable across versions
1034     static class DeletePrevCharAction extends TextAction {
1035 
1036         /**
1037          * Creates this object with the appropriate identifier.
1038          */
1039         DeletePrevCharAction() {
1040             super(deletePrevCharAction);
1041         }
1042 
1043         /**
1044          * The operation to perform when this action is triggered.
1045          *
1046          * @param e the action event
1047          */
1048         public void actionPerformed(ActionEvent e) {
1049             JTextComponent target = getTextComponent(e);
1050             boolean beep = true;
1051             if ((target != null) && (target.isEditable())) {
1052                 try {
1053                     Document doc = target.getDocument();
1054                     Caret caret = target.getCaret();
1055                     int dot = caret.getDot();
1056                     int mark = caret.getMark();
1057                     if (dot != mark) {
1058                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1059                         beep = false;
1060                     } else if (dot > 0) {
1061                         int delChars = 1;
1062 
1063                         if (dot > 1) {
1064                             String dotChars = doc.getText(dot - 2, 2);
1065                             char c0 = dotChars.charAt(0);
1066                             char c1 = dotChars.charAt(1);
1067 
1068                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1069                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1070                                 delChars = 2;
1071                             }
1072                         }
1073 
1074                         doc.remove(dot - delChars, delChars);
1075                         beep = false;
1076                     }
1077                 } catch (BadLocationException bl) {
1078                 }
1079             }
1080             if (beep) {
1081                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1082             }
1083         }
1084     }
1085 
1086     /*
1087      * Deletes the character of content that follows the
1088      * current caret position.
1089      * @see DefaultEditorKit#deleteNextCharAction
1090      * @see DefaultEditorKit#getActions
1091      */
1092     @SuppressWarnings("serial") // Superclass is not serializable across versions
1093     static class DeleteNextCharAction extends TextAction {
1094 
1095         /* Create this object with the appropriate identifier. */
1096         DeleteNextCharAction() {
1097             super(deleteNextCharAction);
1098         }
1099 
1100         /** The operation to perform when this action is triggered. */
1101         public void actionPerformed(ActionEvent e) {
1102             JTextComponent target = getTextComponent(e);
1103             boolean beep = true;
1104             if ((target != null) && (target.isEditable())) {
1105                 try {
1106                     Document doc = target.getDocument();
1107                     Caret caret = target.getCaret();
1108                     int dot = caret.getDot();
1109                     int mark = caret.getMark();
1110                     if (dot != mark) {
1111                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1112                         beep = false;
1113                     } else if (dot < doc.getLength()) {
1114                         int delChars = 1;
1115 
1116                         if (dot < doc.getLength() - 1) {
1117                             String dotChars = doc.getText(dot, 2);
1118                             char c0 = dotChars.charAt(0);
1119                             char c1 = dotChars.charAt(1);
1120 
1121                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1122                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1123                                 delChars = 2;
1124                             }
1125                         }
1126 
1127                         doc.remove(dot, delChars);
1128                         beep = false;
1129                     }
1130                 } catch (BadLocationException bl) {
1131                 }
1132             }
1133             if (beep) {
1134                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1135             }
1136         }
1137     }
1138 
1139 
1140     /*
1141      * Deletes the word that precedes/follows the beginning of the selection.
1142      * @see DefaultEditorKit#getActions
1143      */
1144     @SuppressWarnings("serial") // Superclass is not serializable across versions
1145     static class DeleteWordAction extends TextAction {
1146         DeleteWordAction(String name) {
1147             super(name);
1148             assert (name == deletePrevWordAction)
1149                 || (name == deleteNextWordAction);
1150         }
1151         /**
1152          * The operation to perform when this action is triggered.
1153          *
1154          * @param e the action event
1155          */
1156         public void actionPerformed(ActionEvent e) {
1157             final JTextComponent target = getTextComponent(e);
1158             if ((target != null) && (e != null)) {
1159                 if ((! target.isEditable()) || (! target.isEnabled())) {
1160                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1161                     return;
1162                 }
1163                 boolean beep = true;
1164                 try {
1165                     final int start = target.getSelectionStart();
1166                     final Element line =
1167                         Utilities.getParagraphElement(target, start);
1168                     int end;
1169                     if (deleteNextWordAction == getValue(Action.NAME)) {
1170                         end = Utilities.
1171                             getNextWordInParagraph(target, line, start, false);
1172                         if (end == java.text.BreakIterator.DONE) {
1173                             //last word in the paragraph
1174                             final int endOfLine = line.getEndOffset();
1175                             if (start == endOfLine - 1) {
1176                                 //for last position remove last \n
1177                                 end = endOfLine;
1178                             } else {
1179                                 //remove to the end of the paragraph
1180                                 end = endOfLine - 1;
1181                             }
1182                         }
1183                     } else {
1184                         end = Utilities.
1185                             getPrevWordInParagraph(target, line, start);
1186                         if (end == java.text.BreakIterator.DONE) {
1187                             //there is no previous word in the paragraph
1188                             final int startOfLine = line.getStartOffset();
1189                             if (start == startOfLine) {
1190                                 //for first position remove previous \n
1191                                 end = startOfLine - 1;
1192                             } else {
1193                                 //remove to the start of the paragraph
1194                                 end = startOfLine;
1195                             }
1196                         }
1197                     }
1198                     int offs = Math.min(start, end);
1199                     int len = Math.abs(end - start);
1200                     if (offs >= 0) {
1201                         target.getDocument().remove(offs, len);
1202                         beep = false;
1203                     }
1204                 } catch (BadLocationException ignore) {
1205                 }
1206                 if (beep) {
1207                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1208                 }
1209             }
1210         }
1211     }
1212 
1213 
1214     /*
1215      * Sets the editor into read-only mode.
1216      * @see DefaultEditorKit#readOnlyAction
1217      * @see DefaultEditorKit#getActions
1218      */
1219     @SuppressWarnings("serial") // Superclass is not serializable across versions
1220     static class ReadOnlyAction extends TextAction {
1221 
1222         /* Create this object with the appropriate identifier. */
1223         ReadOnlyAction() {
1224             super(readOnlyAction);
1225         }
1226 
1227         /**
1228          * The operation to perform when this action is triggered.
1229          *
1230          * @param e the action event
1231          */
1232         public void actionPerformed(ActionEvent e) {
1233             JTextComponent target = getTextComponent(e);
1234             if (target != null) {
1235                 target.setEditable(false);
1236             }
1237         }
1238     }
1239 
1240     /*
1241      * Sets the editor into writeable mode.
1242      * @see DefaultEditorKit#writableAction
1243      * @see DefaultEditorKit#getActions
1244      */
1245     @SuppressWarnings("serial") // Superclass is not serializable across versions
1246     static class WritableAction extends TextAction {
1247 
1248         /* Create this object with the appropriate identifier. */
1249         WritableAction() {
1250             super(writableAction);
1251         }
1252 
1253         /**
1254          * The operation to perform when this action is triggered.
1255          *
1256          * @param e the action event
1257          */
1258         public void actionPerformed(ActionEvent e) {
1259             JTextComponent target = getTextComponent(e);
1260             if (target != null) {
1261                 target.setEditable(true);
1262             }
1263         }
1264     }
1265 
1266     /**
1267      * Cuts the selected region and place its contents
1268      * into the system clipboard.
1269      * <p>
1270      * <strong>Warning:</strong>
1271      * Serialized objects of this class will not be compatible with
1272      * future Swing releases. The current serialization support is
1273      * appropriate for short term storage or RMI between applications running
1274      * the same version of Swing.  As of 1.4, support for long term storage
1275      * of all JavaBeans&trade;
1276      * has been added to the <code>java.beans</code> package.
1277      * Please see {@link java.beans.XMLEncoder}.
1278      *
1279      * @see DefaultEditorKit#cutAction
1280      * @see DefaultEditorKit#getActions
1281      */
1282     @SuppressWarnings("serial") // Same-version serialization only
1283     public static class CutAction extends TextAction {
1284 
1285         /** Create this object with the appropriate identifier. */
1286         public CutAction() {
1287             super(cutAction);
1288         }
1289 
1290         /**
1291          * The operation to perform when this action is triggered.
1292          *
1293          * @param e the action event
1294          */
1295         public void actionPerformed(ActionEvent e) {
1296             JTextComponent target = getTextComponent(e);
1297             if (target != null) {
1298                 target.cut();
1299             }
1300         }
1301     }
1302 
1303     /**
1304      * Copies the selected region and place its contents
1305      * into the system clipboard.
1306      * <p>
1307      * <strong>Warning:</strong>
1308      * Serialized objects of this class will not be compatible with
1309      * future Swing releases. The current serialization support is
1310      * appropriate for short term storage or RMI between applications running
1311      * the same version of Swing.  As of 1.4, support for long term storage
1312      * of all JavaBeans&trade;
1313      * has been added to the <code>java.beans</code> package.
1314      * Please see {@link java.beans.XMLEncoder}.
1315      *
1316      * @see DefaultEditorKit#copyAction
1317      * @see DefaultEditorKit#getActions
1318      */
1319     @SuppressWarnings("serial") // Same-version serialization only
1320     public static class CopyAction extends TextAction {
1321 
1322         /** Create this object with the appropriate identifier. */
1323         public CopyAction() {
1324             super(copyAction);
1325         }
1326 
1327         /**
1328          * The operation to perform when this action is triggered.
1329          *
1330          * @param e the action event
1331          */
1332         public void actionPerformed(ActionEvent e) {
1333             JTextComponent target = getTextComponent(e);
1334             if (target != null) {
1335                 target.copy();
1336             }
1337         }
1338     }
1339 
1340     /**
1341      * Pastes the contents of the system clipboard into the
1342      * selected region, or before the caret if nothing is
1343      * selected.
1344      * <p>
1345      * <strong>Warning:</strong>
1346      * Serialized objects of this class will not be compatible with
1347      * future Swing releases. The current serialization support is
1348      * appropriate for short term storage or RMI between applications running
1349      * the same version of Swing.  As of 1.4, support for long term storage
1350      * of all JavaBeans&trade;
1351      * has been added to the <code>java.beans</code> package.
1352      * Please see {@link java.beans.XMLEncoder}.
1353      *
1354      * @see DefaultEditorKit#pasteAction
1355      * @see DefaultEditorKit#getActions
1356      */
1357     @SuppressWarnings("serial") // Same-version serialization only
1358     public static class PasteAction extends TextAction {
1359 
1360         /** Create this object with the appropriate identifier. */
1361         public PasteAction() {
1362             super(pasteAction);
1363         }
1364 
1365         /**
1366          * The operation to perform when this action is triggered.
1367          *
1368          * @param e the action event
1369          */
1370         public void actionPerformed(ActionEvent e) {
1371             JTextComponent target = getTextComponent(e);
1372             if (target != null) {
1373                 target.paste();
1374             }
1375         }
1376     }
1377 
1378     /**
1379      * Creates a beep.
1380      * <p>
1381      * <strong>Warning:</strong>
1382      * Serialized objects of this class will not be compatible with
1383      * future Swing releases. The current serialization support is
1384      * appropriate for short term storage or RMI between applications running
1385      * the same version of Swing.  As of 1.4, support for long term storage
1386      * of all JavaBeans&trade;
1387      * has been added to the <code>java.beans</code> package.
1388      * Please see {@link java.beans.XMLEncoder}.
1389      *
1390      * @see DefaultEditorKit#beepAction
1391      * @see DefaultEditorKit#getActions
1392      */
1393     @SuppressWarnings("serial") // Same-version serialization only
1394     public static class BeepAction extends TextAction {
1395 
1396         /** Create this object with the appropriate identifier. */
1397         public BeepAction() {
1398             super(beepAction);
1399         }
1400 
1401         /**
1402          * The operation to perform when this action is triggered.
1403          *
1404          * @param e the action event
1405          */
1406         public void actionPerformed(ActionEvent e) {
1407             JTextComponent target = getTextComponent(e);
1408             UIManager.getLookAndFeel().provideErrorFeedback(target);
1409         }
1410     }
1411 
1412     /**
1413      * Scrolls up/down vertically.  The select version of this action extends
1414      * the selection, instead of simply moving the caret.
1415      *
1416      * @see DefaultEditorKit#pageUpAction
1417      * @see DefaultEditorKit#pageDownAction
1418      * @see DefaultEditorKit#getActions
1419      */
1420     @SuppressWarnings("serial") // Superclass is not serializable across versions
1421     static class VerticalPageAction extends TextAction {
1422 
1423         /** Create this object with the appropriate identifier. */
1424         public VerticalPageAction(String nm, int direction, boolean select) {
1425             super(nm);
1426             this.select = select;
1427             this.direction = direction;
1428         }
1429 
1430         /** The operation to perform when this action is triggered. */
1431         @SuppressWarnings("deprecation")
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                         } else {
1499                             // If the caret index is same as the visible offset
1500                             // then correct newVis.y so that it won't cause
1501                             // unnecessary scrolling upward/downward when
1502                             // page-down/page-up is received after ctrl-END/ctrl-HOME
1503                             if (direction == -1 && newVis.y <= initialY ||
1504                                 direction == 1 && newVis.y >= initialY) {
1505                                 newVis.y = initialY;
1506                             }
1507                         }
1508                     } catch (BadLocationException ble) { }
1509                 } else {
1510                     newVis.y = constrainY(target,
1511                             initialY + scrollAmount, visible.height);
1512                 }
1513                 if (magicPosition != null) {
1514                     caret.setMagicCaretPosition(magicPosition);
1515                 }
1516                 target.scrollRectToVisible(newVis);
1517             }
1518         }
1519 
1520         /**
1521          * Makes sure <code>y</code> is a valid location in
1522          * <code>target</code>.
1523          */
1524         private int constrainY(JTextComponent target, int y, int vis) {
1525             if (y < 0) {
1526                 y = 0;
1527             }
1528             else if (y + vis > target.getHeight()) {
1529                 y = Math.max(0, target.getHeight() - vis);
1530             }
1531             return y;
1532         }
1533 
1534         /**
1535          * Ensures that <code>offset</code> is a valid offset into the
1536          * model for <code>text</code>.
1537          */
1538         private int constrainOffset(JTextComponent text, int offset) {
1539             Document doc = text.getDocument();
1540 
1541             if ((offset != 0) && (offset > doc.getLength())) {
1542                 offset = doc.getLength();
1543             }
1544             if (offset  < 0) {
1545                 offset = 0;
1546             }
1547             return offset;
1548         }
1549 
1550         /**
1551          * Returns adjustsed {@code y} position that indicates the location to scroll to
1552          * after selecting <code>index</code>.
1553          */
1554         @SuppressWarnings("deprecation")
1555         private int getAdjustedY(JTextComponent text, Rectangle visible, int index) {
1556             int result = visible.y;
1557 
1558             try {
1559                 Rectangle dotBounds = text.modelToView(index);
1560 
1561                 if (dotBounds.y < visible.y) {
1562                     result = dotBounds.y;
1563                 } else {
1564                     if ((dotBounds.y > visible.y + visible.height) ||
1565                             (dotBounds.y + dotBounds.height > visible.y + visible.height)) {
1566                         result = dotBounds.y + dotBounds.height - visible.height;
1567                     }
1568                 }
1569             } catch (BadLocationException ble) {
1570             }
1571 
1572             return result;
1573         }
1574 
1575         /**
1576          * Adjusts the Rectangle to contain the bounds of the character at
1577          * <code>index</code> in response to a page up.
1578          */
1579         private boolean select;
1580 
1581         /**
1582          * Direction to scroll, 1 is down, -1 is up.
1583          */
1584         private int direction;
1585     }
1586 
1587 
1588     /**
1589      * Pages one view to the left or right.
1590      */
1591     @SuppressWarnings("serial") // Superclass is not serializable across versions
1592     static class PageAction extends TextAction {
1593 
1594         /** Create this object with the appropriate identifier. */
1595         public PageAction(String nm, boolean left, boolean select) {
1596             super(nm);
1597             this.select = select;
1598             this.left = left;
1599         }
1600 
1601         /** The operation to perform when this action is triggered. */
1602         @SuppressWarnings("deprecation")
1603         public void actionPerformed(ActionEvent e) {
1604             JTextComponent target = getTextComponent(e);
1605             if (target != null) {
1606                 int selectedIndex;
1607                 Rectangle visible = new Rectangle();
1608                 target.computeVisibleRect(visible);
1609                 if (left) {
1610                     visible.x = Math.max(0, visible.x - visible.width);
1611                 }
1612                 else {
1613                     visible.x += visible.width;
1614                 }
1615 
1616                 selectedIndex = target.getCaretPosition();
1617                 if(selectedIndex != -1) {
1618                     if (left) {
1619                         selectedIndex = target.viewToModel
1620                             (new Point(visible.x, visible.y));
1621                     }
1622                     else {
1623                         selectedIndex = target.viewToModel
1624                             (new Point(visible.x + visible.width - 1,
1625                                        visible.y + visible.height - 1));
1626                     }
1627                     Document doc = target.getDocument();
1628                     if ((selectedIndex != 0) &&
1629                         (selectedIndex  > (doc.getLength()-1))) {
1630                         selectedIndex = doc.getLength()-1;
1631                     }
1632                     else if(selectedIndex  < 0) {
1633                         selectedIndex = 0;
1634                     }
1635                     if (select)
1636                         target.moveCaretPosition(selectedIndex);
1637                     else
1638                         target.setCaretPosition(selectedIndex);
1639                 }
1640             }
1641         }
1642 
1643         private boolean select;
1644         private boolean left;
1645     }
1646 
1647     @SuppressWarnings("serial") // Superclass is not serializable across versions
1648     static class DumpModelAction extends TextAction {
1649 
1650         DumpModelAction() {
1651             super("dump-model");
1652         }
1653 
1654         public void actionPerformed(ActionEvent e) {
1655             JTextComponent target = getTextComponent(e);
1656             if (target != null) {
1657                 Document d = target.getDocument();
1658                 if (d instanceof AbstractDocument) {
1659                     ((AbstractDocument) d).dump(System.err);
1660                 }
1661             }
1662         }
1663     }
1664 
1665     /*
1666      * Action to move the selection by way of the
1667      * getNextVisualPositionFrom method. Constructor indicates direction
1668      * to use.
1669      */
1670     @SuppressWarnings("serial") // Superclass is not serializable across versions
1671     static class NextVisualPositionAction extends TextAction {
1672 
1673         /**
1674          * Create this action with the appropriate identifier.
1675          * @param nm  the name of the action, Action.NAME.
1676          * @param select whether to extend the selection when
1677          *  changing the caret position.
1678          */
1679         NextVisualPositionAction(String nm, boolean select, int direction) {
1680             super(nm);
1681             this.select = select;
1682             this.direction = direction;
1683         }
1684 
1685         /** The operation to perform when this action is triggered. */
1686         @SuppressWarnings("deprecation")
1687         public void actionPerformed(ActionEvent e) {
1688             JTextComponent target = getTextComponent(e);
1689             if (target != null) {
1690                 Caret caret = target.getCaret();
1691                 DefaultCaret bidiCaret = (caret instanceof DefaultCaret) ?
1692                                               (DefaultCaret)caret : null;
1693                 int dot = caret.getDot();
1694                 Position.Bias[] bias = new Position.Bias[1];
1695                 Point magicPosition = caret.getMagicCaretPosition();
1696 
1697                 try {
1698                     if(magicPosition == null &&
1699                        (direction == SwingConstants.NORTH ||
1700                         direction == SwingConstants.SOUTH)) {
1701                         Rectangle r = (bidiCaret != null) ?
1702                                 target.getUI().modelToView(target, dot,
1703                                                       bidiCaret.getDotBias()) :
1704                                 target.modelToView(dot);
1705                         magicPosition = new Point(r.x, r.y);
1706                     }
1707 
1708                     NavigationFilter filter = target.getNavigationFilter();
1709 
1710                     if (filter != null) {
1711                         dot = filter.getNextVisualPositionFrom
1712                                      (target, dot, (bidiCaret != null) ?
1713                                       bidiCaret.getDotBias() :
1714                                       Position.Bias.Forward, direction, bias);
1715                     }
1716                     else {
1717                         dot = target.getUI().getNextVisualPositionFrom
1718                                      (target, dot, (bidiCaret != null) ?
1719                                       bidiCaret.getDotBias() :
1720                                       Position.Bias.Forward, direction, bias);
1721                     }
1722                     if(bias[0] == null) {
1723                         bias[0] = Position.Bias.Forward;
1724                     }
1725                     if(bidiCaret != null) {
1726                         if (select) {
1727                             bidiCaret.moveDot(dot, bias[0]);
1728                         } else {
1729                             bidiCaret.setDot(dot, bias[0]);
1730                         }
1731                     }
1732                     else {
1733                         if (select) {
1734                             caret.moveDot(dot);
1735                         } else {
1736                             caret.setDot(dot);
1737                         }
1738                     }
1739                     if(magicPosition != null &&
1740                        (direction == SwingConstants.NORTH ||
1741                         direction == SwingConstants.SOUTH)) {
1742                         target.getCaret().setMagicCaretPosition(magicPosition);
1743                     }
1744                 } catch (BadLocationException ex) {
1745                 }
1746             }
1747         }
1748 
1749         private boolean select;
1750         private int direction;
1751     }
1752 
1753     /*
1754      * Position the caret to the beginning of the word.
1755      * @see DefaultEditorKit#beginWordAction
1756      * @see DefaultEditorKit#selectBeginWordAction
1757      * @see DefaultEditorKit#getActions
1758      */
1759     @SuppressWarnings("serial") // Superclass is not serializable across versions
1760     static class BeginWordAction extends TextAction {
1761 
1762         /**
1763          * Create this action with the appropriate identifier.
1764          * @param nm  the name of the action, Action.NAME.
1765          * @param select whether to extend the selection when
1766          *  changing the caret position.
1767          */
1768         BeginWordAction(String nm, boolean select) {
1769             super(nm);
1770             this.select = select;
1771         }
1772 
1773         /** The operation to perform when this action is triggered. */
1774         public void actionPerformed(ActionEvent e) {
1775             JTextComponent target = getTextComponent(e);
1776             if (target != null) {
1777                 try {
1778                     int offs = target.getCaretPosition();
1779                     int begOffs = Utilities.getWordStart(target, offs);
1780                     if (select) {
1781                         target.moveCaretPosition(begOffs);
1782                     } else {
1783                         target.setCaretPosition(begOffs);
1784                     }
1785                 } catch (BadLocationException bl) {
1786                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1787                 }
1788             }
1789         }
1790 
1791         private boolean select;
1792     }
1793 
1794     /*
1795      * Position the caret to the end of the word.
1796      * @see DefaultEditorKit#endWordAction
1797      * @see DefaultEditorKit#selectEndWordAction
1798      * @see DefaultEditorKit#getActions
1799      */
1800     @SuppressWarnings("serial") // Superclass is not serializable across versions
1801     static class EndWordAction extends TextAction {
1802 
1803         /**
1804          * Create this action with the appropriate identifier.
1805          * @param nm  the name of the action, Action.NAME.
1806          * @param select whether to extend the selection when
1807          *  changing the caret position.
1808          */
1809         EndWordAction(String nm, boolean select) {
1810             super(nm);
1811             this.select = select;
1812         }
1813 
1814         /** The operation to perform when this action is triggered. */
1815         public void actionPerformed(ActionEvent e) {
1816             JTextComponent target = getTextComponent(e);
1817             if (target != null) {
1818                 try {
1819                     int offs = target.getCaretPosition();
1820                     int endOffs = Utilities.getWordEnd(target, offs);
1821                     if (select) {
1822                         target.moveCaretPosition(endOffs);
1823                     } else {
1824                         target.setCaretPosition(endOffs);
1825                     }
1826                 } catch (BadLocationException bl) {
1827                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1828                 }
1829             }
1830         }
1831 
1832         private boolean select;
1833     }
1834 
1835     /*
1836      * Position the caret to the beginning of the previous word.
1837      * @see DefaultEditorKit#previousWordAction
1838      * @see DefaultEditorKit#selectPreviousWordAction
1839      * @see DefaultEditorKit#getActions
1840      */
1841     @SuppressWarnings("serial") // Superclass is not serializable across versions
1842     static class PreviousWordAction extends TextAction {
1843 
1844         /**
1845          * Create this action with the appropriate identifier.
1846          * @param nm  the name of the action, Action.NAME.
1847          * @param select whether to extend the selection when
1848          *  changing the caret position.
1849          */
1850         PreviousWordAction(String nm, boolean select) {
1851             super(nm);
1852             this.select = select;
1853         }
1854 
1855         /** The operation to perform when this action is triggered. */
1856         public void actionPerformed(ActionEvent e) {
1857             JTextComponent target = getTextComponent(e);
1858             if (target != null) {
1859                 int offs = target.getCaretPosition();
1860                 boolean failed = false;
1861                 try {
1862                     Element curPara =
1863                             Utilities.getParagraphElement(target, offs);
1864                     offs = Utilities.getPreviousWord(target, offs);
1865                     if(offs < curPara.getStartOffset()) {
1866                         // we should first move to the end of the
1867                         // previous paragraph (bug #4278839)
1868                         offs = Utilities.getParagraphElement(target, offs).
1869                                 getEndOffset() - 1;
1870                     }
1871                 } catch (BadLocationException bl) {
1872                     if (offs != 0) {
1873                         offs = 0;
1874                     }
1875                     else {
1876                         failed = true;
1877                     }
1878                 }
1879                 if (!failed) {
1880                     if (select) {
1881                         target.moveCaretPosition(offs);
1882                     } else {
1883                         target.setCaretPosition(offs);
1884                     }
1885                 }
1886                 else {
1887                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1888                 }
1889             }
1890         }
1891 
1892         private boolean select;
1893     }
1894 
1895     /*
1896      * Position the caret to the next of the word.
1897      * @see DefaultEditorKit#nextWordAction
1898      * @see DefaultEditorKit#selectNextWordAction
1899      * @see DefaultEditorKit#getActions
1900      */
1901     @SuppressWarnings("serial") // Superclass is not serializable across versions
1902     static class NextWordAction extends TextAction {
1903 
1904         /**
1905          * Create this action with the appropriate identifier.
1906          * @param nm  the name of the action, Action.NAME.
1907          * @param select whether to extend the selection when
1908          *  changing the caret position.
1909          */
1910         NextWordAction(String nm, boolean select) {
1911             super(nm);
1912             this.select = select;
1913         }
1914 
1915         /** The operation to perform when this action is triggered. */
1916         public void actionPerformed(ActionEvent e) {
1917             JTextComponent target = getTextComponent(e);
1918             if (target != null) {
1919                 int offs = target.getCaretPosition();
1920                 boolean failed = false;
1921                 int oldOffs = offs;
1922                 Element curPara =
1923                         Utilities.getParagraphElement(target, offs);
1924                 try {
1925                     offs = Utilities.getNextWord(target, offs);
1926                     if(offs >= curPara.getEndOffset() &&
1927                             oldOffs != curPara.getEndOffset() - 1) {
1928                         // we should first move to the end of current
1929                         // paragraph (bug #4278839)
1930                         offs = curPara.getEndOffset() - 1;
1931                     }
1932                 } catch (BadLocationException bl) {
1933                     int end = target.getDocument().getLength();
1934                     if (offs != end) {
1935                         if(oldOffs != curPara.getEndOffset() - 1) {
1936                             offs = curPara.getEndOffset() - 1;
1937                         } else {
1938                         offs = end;
1939                     }
1940                     }
1941                     else {
1942                         failed = true;
1943                     }
1944                 }
1945                 if (!failed) {
1946                     if (select) {
1947                         target.moveCaretPosition(offs);
1948                     } else {
1949                         target.setCaretPosition(offs);
1950                     }
1951                 }
1952                 else {
1953                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1954                 }
1955             }
1956         }
1957 
1958         private boolean select;
1959     }
1960 
1961     /*
1962      * Position the caret to the beginning of the line.
1963      * @see DefaultEditorKit#beginLineAction
1964      * @see DefaultEditorKit#selectBeginLineAction
1965      * @see DefaultEditorKit#getActions
1966      */
1967     @SuppressWarnings("serial") // Superclass is not serializable across versions
1968     static class BeginLineAction extends TextAction {
1969 
1970         /**
1971          * Create this action with the appropriate identifier.
1972          * @param nm  the name of the action, Action.NAME.
1973          * @param select whether to extend the selection when
1974          *  changing the caret position.
1975          */
1976         BeginLineAction(String nm, boolean select) {
1977             super(nm);
1978             this.select = select;
1979         }
1980 
1981         /** The operation to perform when this action is triggered. */
1982         public void actionPerformed(ActionEvent e) {
1983             JTextComponent target = getTextComponent(e);
1984             if (target != null) {
1985                 try {
1986                     int offs = target.getCaretPosition();
1987                     int begOffs = Utilities.getRowStart(target, offs);
1988                     if (select) {
1989                         target.moveCaretPosition(begOffs);
1990                     } else {
1991                         target.setCaretPosition(begOffs);
1992                     }
1993                 } catch (BadLocationException bl) {
1994                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1995                 }
1996             }
1997         }
1998 
1999         private boolean select;
2000     }
2001 
2002     /*
2003      * Position the caret to the end of the line.
2004      * @see DefaultEditorKit#endLineAction
2005      * @see DefaultEditorKit#selectEndLineAction
2006      * @see DefaultEditorKit#getActions
2007      */
2008     @SuppressWarnings("serial") // Superclass is not serializable across versions
2009     static class EndLineAction extends TextAction {
2010 
2011         /**
2012          * Create this action with the appropriate identifier.
2013          * @param nm  the name of the action, Action.NAME.
2014          * @param select whether to extend the selection when
2015          *  changing the caret position.
2016          */
2017         EndLineAction(String nm, boolean select) {
2018             super(nm);
2019             this.select = select;
2020         }
2021 
2022         /** The operation to perform when this action is triggered. */
2023         public void actionPerformed(ActionEvent e) {
2024             JTextComponent target = getTextComponent(e);
2025             if (target != null) {
2026                 try {
2027                     int offs = target.getCaretPosition();
2028                     int endOffs = Utilities.getRowEnd(target, offs);
2029                     if (select) {
2030                         target.moveCaretPosition(endOffs);
2031                     } else {
2032                         target.setCaretPosition(endOffs);
2033                     }
2034                 } catch (BadLocationException bl) {
2035                     UIManager.getLookAndFeel().provideErrorFeedback(target);
2036                 }
2037             }
2038         }
2039 
2040         private boolean select;
2041     }
2042 
2043     /*
2044      * Position the caret to the beginning of the paragraph.
2045      * @see DefaultEditorKit#beginParagraphAction
2046      * @see DefaultEditorKit#selectBeginParagraphAction
2047      * @see DefaultEditorKit#getActions
2048      */
2049     @SuppressWarnings("serial") // Superclass is not serializable across versions
2050     static class BeginParagraphAction extends TextAction {
2051 
2052         /**
2053          * Create this action with the appropriate identifier.
2054          * @param nm  the name of the action, Action.NAME.
2055          * @param select whether to extend the selection when
2056          *  changing the caret position.
2057          */
2058         BeginParagraphAction(String nm, boolean select) {
2059             super(nm);
2060             this.select = select;
2061         }
2062 
2063         /** The operation to perform when this action is triggered. */
2064         public void actionPerformed(ActionEvent e) {
2065             JTextComponent target = getTextComponent(e);
2066             if (target != null) {
2067                 int offs = target.getCaretPosition();
2068                 Element elem = Utilities.getParagraphElement(target, offs);
2069                 offs = elem.getStartOffset();
2070                 if (select) {
2071                     target.moveCaretPosition(offs);
2072                 } else {
2073                     target.setCaretPosition(offs);
2074                 }
2075             }
2076         }
2077 
2078         private boolean select;
2079     }
2080 
2081     /*
2082      * Position the caret to the end of the paragraph.
2083      * @see DefaultEditorKit#endParagraphAction
2084      * @see DefaultEditorKit#selectEndParagraphAction
2085      * @see DefaultEditorKit#getActions
2086      */
2087     @SuppressWarnings("serial") // Superclass is not serializable across versions
2088     static class EndParagraphAction extends TextAction {
2089 
2090         /**
2091          * Create this action with the appropriate identifier.
2092          * @param nm  the name of the action, Action.NAME.
2093          * @param select whether to extend the selection when
2094          *  changing the caret position.
2095          */
2096         EndParagraphAction(String nm, boolean select) {
2097             super(nm);
2098             this.select = select;
2099         }
2100 
2101         /** The operation to perform when this action is triggered. */
2102         public void actionPerformed(ActionEvent e) {
2103             JTextComponent target = getTextComponent(e);
2104             if (target != null) {
2105                 int offs = target.getCaretPosition();
2106                 Element elem = Utilities.getParagraphElement(target, offs);
2107                 offs = Math.min(target.getDocument().getLength(),
2108                                 elem.getEndOffset());
2109                 if (select) {
2110                     target.moveCaretPosition(offs);
2111                 } else {
2112                     target.setCaretPosition(offs);
2113                 }
2114             }
2115         }
2116 
2117         private boolean select;
2118     }
2119 
2120     /*
2121      * Move the caret to the beginning of the document.
2122      * @see DefaultEditorKit#beginAction
2123      * @see DefaultEditorKit#getActions
2124      */
2125     @SuppressWarnings("serial") // Superclass is not serializable across versions
2126     static class BeginAction extends TextAction {
2127 
2128         /* Create this object with the appropriate identifier. */
2129         BeginAction(String nm, boolean select) {
2130             super(nm);
2131             this.select = select;
2132         }
2133 
2134         /** The operation to perform when this action is triggered. */
2135         public void actionPerformed(ActionEvent e) {
2136             JTextComponent target = getTextComponent(e);
2137             if (target != null) {
2138                 if (select) {
2139                     target.moveCaretPosition(0);
2140                 } else {
2141                     target.setCaretPosition(0);
2142                 }
2143             }
2144         }
2145 
2146         private boolean select;
2147     }
2148 
2149     /*
2150      * Move the caret to the end of the document.
2151      * @see DefaultEditorKit#endAction
2152      * @see DefaultEditorKit#getActions
2153      */
2154     @SuppressWarnings("serial") // Superclass is not serializable across versions
2155     static class EndAction extends TextAction {
2156 
2157         /* Create this object with the appropriate identifier. */
2158         EndAction(String nm, boolean select) {
2159             super(nm);
2160             this.select = select;
2161         }
2162 
2163         /** The operation to perform when this action is triggered. */
2164         public void actionPerformed(ActionEvent e) {
2165             JTextComponent target = getTextComponent(e);
2166             if (target != null) {
2167                 Document doc = target.getDocument();
2168                 int dot = doc.getLength();
2169                 if (select) {
2170                     target.moveCaretPosition(dot);
2171                 } else {
2172                     target.setCaretPosition(dot);
2173                 }
2174             }
2175         }
2176 
2177         private boolean select;
2178     }
2179 
2180     /*
2181      * Select the word around the caret
2182      * @see DefaultEditorKit#endAction
2183      * @see DefaultEditorKit#getActions
2184      */
2185     @SuppressWarnings("serial") // Superclass is not serializable across versions
2186     static class SelectWordAction extends TextAction {
2187 
2188         /**
2189          * Create this action with the appropriate identifier.
2190          */
2191         SelectWordAction() {
2192             super(selectWordAction);
2193             start = new BeginWordAction("pigdog", false);
2194             end = new EndWordAction("pigdog", true);
2195         }
2196 
2197         /** The operation to perform when this action is triggered. */
2198         public void actionPerformed(ActionEvent e) {
2199             start.actionPerformed(e);
2200             end.actionPerformed(e);
2201         }
2202 
2203         private Action start;
2204         private Action end;
2205     }
2206 
2207     /*
2208      * Select the line around the caret
2209      * @see DefaultEditorKit#endAction
2210      * @see DefaultEditorKit#getActions
2211      */
2212     @SuppressWarnings("serial") // Superclass is not serializable across versions
2213     static class SelectLineAction extends TextAction {
2214 
2215         /**
2216          * Create this action with the appropriate identifier.
2217          */
2218         SelectLineAction() {
2219             super(selectLineAction);
2220             start = new BeginLineAction("pigdog", false);
2221             end = new EndLineAction("pigdog", true);
2222         }
2223 
2224         /** The operation to perform when this action is triggered. */
2225         public void actionPerformed(ActionEvent e) {
2226             start.actionPerformed(e);
2227             end.actionPerformed(e);
2228         }
2229 
2230         private Action start;
2231         private Action end;
2232     }
2233 
2234     /*
2235      * Select the paragraph around the caret
2236      * @see DefaultEditorKit#endAction
2237      * @see DefaultEditorKit#getActions
2238      */
2239     @SuppressWarnings("serial") // Superclass is not serializable across versions
2240     static class SelectParagraphAction extends TextAction {
2241 
2242         /**
2243          * Create this action with the appropriate identifier.
2244          */
2245         SelectParagraphAction() {
2246             super(selectParagraphAction);
2247             start = new BeginParagraphAction("pigdog", false);
2248             end = new EndParagraphAction("pigdog", true);
2249         }
2250 
2251         /** The operation to perform when this action is triggered. */
2252         public void actionPerformed(ActionEvent e) {
2253             start.actionPerformed(e);
2254             end.actionPerformed(e);
2255         }
2256 
2257         private Action start;
2258         private Action end;
2259     }
2260 
2261     /*
2262      * Select the entire document
2263      * @see DefaultEditorKit#endAction
2264      * @see DefaultEditorKit#getActions
2265      */
2266     @SuppressWarnings("serial") // Superclass is not serializable across versions
2267     static class SelectAllAction extends TextAction {
2268 
2269         /**
2270          * Create this action with the appropriate identifier.
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 }