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