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;
 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                         }
1500                     } catch (BadLocationException ble) { }
1501                 } else {
1502                     newVis.y = constrainY(target,
1503                             initialY + scrollAmount, visible.height);
1504                 }
1505                 if (magicPosition != null) {
1506                     caret.setMagicCaretPosition(magicPosition);
1507                 }
1508                 target.scrollRectToVisible(newVis);
1509             }
1510         }
1511 
1512         /**
1513          * Makes sure <code>y</code> is a valid location in
1514          * <code>target</code>.
1515          */
1516         private int constrainY(JTextComponent target, int y, int vis) {
1517             if (y < 0) {
1518                 y = 0;
1519             }
1520             else if (y + vis > target.getHeight()) {
1521                 y = Math.max(0, target.getHeight() - vis);
1522             }
1523             return y;
1524         }
1525 
1526         /**
1527          * Ensures that <code>offset</code> is a valid offset into the
1528          * model for <code>text</code>.
1529          */
1530         private int constrainOffset(JTextComponent text, int offset) {
1531             Document doc = text.getDocument();
1532 
1533             if ((offset != 0) && (offset > doc.getLength())) {
1534                 offset = doc.getLength();
1535             }
1536             if (offset  < 0) {
1537                 offset = 0;
1538             }
1539             return offset;
1540         }
1541 
1542         /**
1543          * Returns adjustsed {@code y} position that indicates the location to scroll to
1544          * after selecting <code>index</code>.
1545          */
1546         @SuppressWarnings("deprecation")
1547         private int getAdjustedY(JTextComponent text, Rectangle visible, int index) {
1548             int result = visible.y;
1549 
1550             try {
1551                 Rectangle dotBounds = text.modelToView(index);
1552 
1553                 if (dotBounds.y < visible.y) {
1554                     result = dotBounds.y;
1555                 } else {
1556                     if ((dotBounds.y > visible.y + visible.height) ||
1557                             (dotBounds.y + dotBounds.height > visible.y + visible.height)) {
1558                         result = dotBounds.y + dotBounds.height - visible.height;
1559                     }
1560                 }
1561             } catch (BadLocationException ble) {
1562             }
1563 
1564             return result;
1565         }
1566 
1567         /**
1568          * Adjusts the Rectangle to contain the bounds of the character at
1569          * <code>index</code> in response to a page up.
1570          */
1571         private boolean select;
1572 
1573         /**
1574          * Direction to scroll, 1 is down, -1 is up.
1575          */
1576         private int direction;
1577     }
1578 
1579 
1580     /**
1581      * Pages one view to the left or right.
1582      */
1583     @SuppressWarnings("serial") // Superclass is not serializable across versions
1584     static class PageAction extends TextAction {
1585 
1586         /** Create this object with the appropriate identifier. */
1587         public PageAction(String nm, boolean left, boolean select) {
1588             super(nm);
1589             this.select = select;
1590             this.left = left;
1591         }
1592 
1593         /** The operation to perform when this action is triggered. */
1594         @SuppressWarnings("deprecation")
1595         public void actionPerformed(ActionEvent e) {
1596             JTextComponent target = getTextComponent(e);
1597             if (target != null) {
1598                 int selectedIndex;
1599                 Rectangle visible = new Rectangle();
1600                 target.computeVisibleRect(visible);
1601                 if (left) {
1602                     visible.x = Math.max(0, visible.x - visible.width);
1603                 }
1604                 else {
1605                     visible.x += visible.width;
1606                 }
1607 
1608                 selectedIndex = target.getCaretPosition();
1609                 if(selectedIndex != -1) {
1610                     if (left) {
1611                         selectedIndex = target.viewToModel
1612                             (new Point(visible.x, visible.y));
1613                     }
1614                     else {
1615                         selectedIndex = target.viewToModel
1616                             (new Point(visible.x + visible.width - 1,
1617                                        visible.y + visible.height - 1));
1618                     }
1619                     Document doc = target.getDocument();
1620                     if ((selectedIndex != 0) &&
1621                         (selectedIndex  > (doc.getLength()-1))) {
1622                         selectedIndex = doc.getLength()-1;
1623                     }
1624                     else if(selectedIndex  < 0) {
1625                         selectedIndex = 0;
1626                     }
1627                     if (select)
1628                         target.moveCaretPosition(selectedIndex);
1629                     else
1630                         target.setCaretPosition(selectedIndex);
1631                 }
1632             }
1633         }
1634 
1635         private boolean select;
1636         private boolean left;
1637     }
1638 
1639     @SuppressWarnings("serial") // Superclass is not serializable across versions
1640     static class DumpModelAction extends TextAction {
1641 
1642         DumpModelAction() {
1643             super("dump-model");
1644         }
1645 
1646         public void actionPerformed(ActionEvent e) {
1647             JTextComponent target = getTextComponent(e);
1648             if (target != null) {
1649                 Document d = target.getDocument();
1650                 if (d instanceof AbstractDocument) {
1651                     ((AbstractDocument) d).dump(System.err);
1652                 }
1653             }
1654         }
1655     }
1656 
1657     /*
1658      * Action to move the selection by way of the
1659      * getNextVisualPositionFrom method. Constructor indicates direction
1660      * to use.
1661      */
1662     @SuppressWarnings("serial") // Superclass is not serializable across versions
1663     static class NextVisualPositionAction extends TextAction {
1664 
1665         /**
1666          * Create this action with the appropriate identifier.
1667          * @param nm  the name of the action, Action.NAME.
1668          * @param select whether to extend the selection when
1669          *  changing the caret position.
1670          */
1671         NextVisualPositionAction(String nm, boolean select, int direction) {
1672             super(nm);
1673             this.select = select;
1674             this.direction = direction;
1675         }
1676 
1677         /** The operation to perform when this action is triggered. */
1678         @SuppressWarnings("deprecation")
1679         public void actionPerformed(ActionEvent e) {
1680             JTextComponent target = getTextComponent(e);
1681             if (target != null) {
1682                 Caret caret = target.getCaret();
1683                 DefaultCaret bidiCaret = (caret instanceof DefaultCaret) ?
1684                                               (DefaultCaret)caret : null;
1685                 int dot = caret.getDot();
1686                 Position.Bias[] bias = new Position.Bias[1];
1687                 Point magicPosition = caret.getMagicCaretPosition();
1688 
1689                 try {
1690                     if(magicPosition == null &&
1691                        (direction == SwingConstants.NORTH ||
1692                         direction == SwingConstants.SOUTH)) {
1693                         Rectangle r = (bidiCaret != null) ?
1694                                 target.getUI().modelToView(target, dot,
1695                                                       bidiCaret.getDotBias()) :
1696                                 target.modelToView(dot);
1697                         magicPosition = new Point(r.x, r.y);
1698                     }
1699 
1700                     NavigationFilter filter = target.getNavigationFilter();
1701 
1702                     if (filter != null) {
1703                         dot = filter.getNextVisualPositionFrom
1704                                      (target, dot, (bidiCaret != null) ?
1705                                       bidiCaret.getDotBias() :
1706                                       Position.Bias.Forward, direction, bias);
1707                     }
1708                     else {
1709                         dot = target.getUI().getNextVisualPositionFrom
1710                                      (target, dot, (bidiCaret != null) ?
1711                                       bidiCaret.getDotBias() :
1712                                       Position.Bias.Forward, direction, bias);
1713                     }
1714                     if(bias[0] == null) {
1715                         bias[0] = Position.Bias.Forward;
1716                     }
1717                     if(bidiCaret != null) {
1718                         if (select) {
1719                             bidiCaret.moveDot(dot, bias[0]);
1720                         } else {
1721                             bidiCaret.setDot(dot, bias[0]);
1722                         }
1723                     }
1724                     else {
1725                         if (select) {
1726                             caret.moveDot(dot);
1727                         } else {
1728                             caret.setDot(dot);
1729                         }
1730                     }
1731                     if(magicPosition != null &&
1732                        (direction == SwingConstants.NORTH ||
1733                         direction == SwingConstants.SOUTH)) {
1734                         target.getCaret().setMagicCaretPosition(magicPosition);
1735                     }
1736                 } catch (BadLocationException ex) {
1737                 }
1738             }
1739         }
1740 
1741         private boolean select;
1742         private int direction;
1743     }
1744 
1745     /*
1746      * Position the caret to the beginning of the word.
1747      * @see DefaultEditorKit#beginWordAction
1748      * @see DefaultEditorKit#selectBeginWordAction
1749      * @see DefaultEditorKit#getActions
1750      */
1751     @SuppressWarnings("serial") // Superclass is not serializable across versions
1752     static class BeginWordAction extends TextAction {
1753 
1754         /**
1755          * Create this action with the appropriate identifier.
1756          * @param nm  the name of the action, Action.NAME.
1757          * @param select whether to extend the selection when
1758          *  changing the caret position.
1759          */
1760         BeginWordAction(String nm, boolean select) {
1761             super(nm);
1762             this.select = select;
1763         }
1764 
1765         /** The operation to perform when this action is triggered. */
1766         public void actionPerformed(ActionEvent e) {
1767             JTextComponent target = getTextComponent(e);
1768             if (target != null) {
1769                 try {
1770                     int offs = target.getCaretPosition();
1771                     int begOffs = Utilities.getWordStart(target, offs);
1772                     if (select) {
1773                         target.moveCaretPosition(begOffs);
1774                     } else {
1775                         target.setCaretPosition(begOffs);
1776                     }
1777                 } catch (BadLocationException bl) {
1778                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1779                 }
1780             }
1781         }
1782 
1783         private boolean select;
1784     }
1785 
1786     /*
1787      * Position the caret to the end of the word.
1788      * @see DefaultEditorKit#endWordAction
1789      * @see DefaultEditorKit#selectEndWordAction
1790      * @see DefaultEditorKit#getActions
1791      */
1792     @SuppressWarnings("serial") // Superclass is not serializable across versions
1793     static class EndWordAction extends TextAction {
1794 
1795         /**
1796          * Create this action with the appropriate identifier.
1797          * @param nm  the name of the action, Action.NAME.
1798          * @param select whether to extend the selection when
1799          *  changing the caret position.
1800          */
1801         EndWordAction(String nm, boolean select) {
1802             super(nm);
1803             this.select = select;
1804         }
1805 
1806         /** The operation to perform when this action is triggered. */
1807         public void actionPerformed(ActionEvent e) {
1808             JTextComponent target = getTextComponent(e);
1809             if (target != null) {
1810                 try {
1811                     int offs = target.getCaretPosition();
1812                     int endOffs = Utilities.getWordEnd(target, offs);
1813                     if (select) {
1814                         target.moveCaretPosition(endOffs);
1815                     } else {
1816                         target.setCaretPosition(endOffs);
1817                     }
1818                 } catch (BadLocationException bl) {
1819                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1820                 }
1821             }
1822         }
1823 
1824         private boolean select;
1825     }
1826 
1827     /*
1828      * Position the caret to the beginning of the previous word.
1829      * @see DefaultEditorKit#previousWordAction
1830      * @see DefaultEditorKit#selectPreviousWordAction
1831      * @see DefaultEditorKit#getActions
1832      */
1833     @SuppressWarnings("serial") // Superclass is not serializable across versions
1834     static class PreviousWordAction extends TextAction {
1835 
1836         /**
1837          * Create this action with the appropriate identifier.
1838          * @param nm  the name of the action, Action.NAME.
1839          * @param select whether to extend the selection when
1840          *  changing the caret position.
1841          */
1842         PreviousWordAction(String nm, boolean select) {
1843             super(nm);
1844             this.select = select;
1845         }
1846 
1847         /** The operation to perform when this action is triggered. */
1848         public void actionPerformed(ActionEvent e) {
1849             JTextComponent target = getTextComponent(e);
1850             if (target != null) {
1851                 int offs = target.getCaretPosition();
1852                 boolean failed = false;
1853                 try {
1854                     Element curPara =
1855                             Utilities.getParagraphElement(target, offs);
1856                     offs = Utilities.getPreviousWord(target, offs);
1857                     if(offs < curPara.getStartOffset()) {
1858                         // we should first move to the end of the
1859                         // previous paragraph (bug #4278839)
1860                         offs = Utilities.getParagraphElement(target, offs).
1861                                 getEndOffset() - 1;
1862                     }
1863                 } catch (BadLocationException bl) {
1864                     if (offs != 0) {
1865                         offs = 0;
1866                     }
1867                     else {
1868                         failed = true;
1869                     }
1870                 }
1871                 if (!failed) {
1872                     if (select) {
1873                         target.moveCaretPosition(offs);
1874                     } else {
1875                         target.setCaretPosition(offs);
1876                     }
1877                 }
1878                 else {
1879                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1880                 }
1881             }
1882         }
1883 
1884         private boolean select;
1885     }
1886 
1887     /*
1888      * Position the caret to the next of the word.
1889      * @see DefaultEditorKit#nextWordAction
1890      * @see DefaultEditorKit#selectNextWordAction
1891      * @see DefaultEditorKit#getActions
1892      */
1893     @SuppressWarnings("serial") // Superclass is not serializable across versions
1894     static class NextWordAction extends TextAction {
1895 
1896         /**
1897          * Create this action with the appropriate identifier.
1898          * @param nm  the name of the action, Action.NAME.
1899          * @param select whether to extend the selection when
1900          *  changing the caret position.
1901          */
1902         NextWordAction(String nm, boolean select) {
1903             super(nm);
1904             this.select = select;
1905         }
1906 
1907         /** The operation to perform when this action is triggered. */
1908         public void actionPerformed(ActionEvent e) {
1909             JTextComponent target = getTextComponent(e);
1910             if (target != null) {
1911                 int offs = target.getCaretPosition();
1912                 boolean failed = false;
1913                 int oldOffs = offs;
1914                 Element curPara =
1915                         Utilities.getParagraphElement(target, offs);
1916                 try {
1917                     offs = Utilities.getNextWord(target, offs);
1918                     if(offs >= curPara.getEndOffset() &&
1919                             oldOffs != curPara.getEndOffset() - 1) {
1920                         // we should first move to the end of current
1921                         // paragraph (bug #4278839)
1922                         offs = curPara.getEndOffset() - 1;
1923                     }
1924                 } catch (BadLocationException bl) {
1925                     int end = target.getDocument().getLength();
1926                     if (offs != end) {
1927                         if(oldOffs != curPara.getEndOffset() - 1) {
1928                             offs = curPara.getEndOffset() - 1;
1929                         } else {
1930                         offs = end;
1931                     }
1932                     }
1933                     else {
1934                         failed = true;
1935                     }
1936                 }
1937                 if (!failed) {
1938                     if (select) {
1939                         target.moveCaretPosition(offs);
1940                     } else {
1941                         target.setCaretPosition(offs);
1942                     }
1943                 }
1944                 else {
1945                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1946                 }
1947             }
1948         }
1949 
1950         private boolean select;
1951     }
1952 
1953     /*
1954      * Position the caret to the beginning of the line.
1955      * @see DefaultEditorKit#beginLineAction
1956      * @see DefaultEditorKit#selectBeginLineAction
1957      * @see DefaultEditorKit#getActions
1958      */
1959     @SuppressWarnings("serial") // Superclass is not serializable across versions
1960     static class BeginLineAction extends TextAction {
1961 
1962         /**
1963          * Create this action with the appropriate identifier.
1964          * @param nm  the name of the action, Action.NAME.
1965          * @param select whether to extend the selection when
1966          *  changing the caret position.
1967          */
1968         BeginLineAction(String nm, boolean select) {
1969             super(nm);
1970             this.select = select;
1971         }
1972 
1973         /** The operation to perform when this action is triggered. */
1974         public void actionPerformed(ActionEvent e) {
1975             JTextComponent target = getTextComponent(e);
1976             if (target != null) {
1977                 try {
1978                     int offs = target.getCaretPosition();
1979                     int begOffs = Utilities.getRowStart(target, offs);
1980                     if (select) {
1981                         target.moveCaretPosition(begOffs);
1982                     } else {
1983                         target.setCaretPosition(begOffs);
1984                     }
1985                 } catch (BadLocationException bl) {
1986                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1987                 }
1988             }
1989         }
1990 
1991         private boolean select;
1992     }
1993 
1994     /*
1995      * Position the caret to the end of the line.
1996      * @see DefaultEditorKit#endLineAction
1997      * @see DefaultEditorKit#selectEndLineAction
1998      * @see DefaultEditorKit#getActions
1999      */
2000     @SuppressWarnings("serial") // Superclass is not serializable across versions
2001     static class EndLineAction extends TextAction {
2002 
2003         /**
2004          * Create this action with the appropriate identifier.
2005          * @param nm  the name of the action, Action.NAME.
2006          * @param select whether to extend the selection when
2007          *  changing the caret position.
2008          */
2009         EndLineAction(String nm, boolean select) {
2010             super(nm);
2011             this.select = select;
2012         }
2013 
2014         /** The operation to perform when this action is triggered. */
2015         public void actionPerformed(ActionEvent e) {
2016             JTextComponent target = getTextComponent(e);
2017             if (target != null) {
2018                 try {
2019                     int offs = target.getCaretPosition();
2020                     int endOffs = Utilities.getRowEnd(target, offs);
2021                     if (select) {
2022                         target.moveCaretPosition(endOffs);
2023                     } else {
2024                         target.setCaretPosition(endOffs);
2025                     }
2026                 } catch (BadLocationException bl) {
2027                     UIManager.getLookAndFeel().provideErrorFeedback(target);
2028                 }
2029             }
2030         }
2031 
2032         private boolean select;
2033     }
2034 
2035     /*
2036      * Position the caret to the beginning of the paragraph.
2037      * @see DefaultEditorKit#beginParagraphAction
2038      * @see DefaultEditorKit#selectBeginParagraphAction
2039      * @see DefaultEditorKit#getActions
2040      */
2041     @SuppressWarnings("serial") // Superclass is not serializable across versions
2042     static class BeginParagraphAction extends TextAction {
2043 
2044         /**
2045          * Create this action with the appropriate identifier.
2046          * @param nm  the name of the action, Action.NAME.
2047          * @param select whether to extend the selection when
2048          *  changing the caret position.
2049          */
2050         BeginParagraphAction(String nm, boolean select) {
2051             super(nm);
2052             this.select = select;
2053         }
2054 
2055         /** The operation to perform when this action is triggered. */
2056         public void actionPerformed(ActionEvent e) {
2057             JTextComponent target = getTextComponent(e);
2058             if (target != null) {
2059                 int offs = target.getCaretPosition();
2060                 Element elem = Utilities.getParagraphElement(target, offs);
2061                 offs = elem.getStartOffset();
2062                 if (select) {
2063                     target.moveCaretPosition(offs);
2064                 } else {
2065                     target.setCaretPosition(offs);
2066                 }
2067             }
2068         }
2069 
2070         private boolean select;
2071     }
2072 
2073     /*
2074      * Position the caret to the end of the paragraph.
2075      * @see DefaultEditorKit#endParagraphAction
2076      * @see DefaultEditorKit#selectEndParagraphAction
2077      * @see DefaultEditorKit#getActions
2078      */
2079     @SuppressWarnings("serial") // Superclass is not serializable across versions
2080     static class EndParagraphAction extends TextAction {
2081 
2082         /**
2083          * Create this action with the appropriate identifier.
2084          * @param nm  the name of the action, Action.NAME.
2085          * @param select whether to extend the selection when
2086          *  changing the caret position.
2087          */
2088         EndParagraphAction(String nm, boolean select) {
2089             super(nm);
2090             this.select = select;
2091         }
2092 
2093         /** The operation to perform when this action is triggered. */
2094         public void actionPerformed(ActionEvent e) {
2095             JTextComponent target = getTextComponent(e);
2096             if (target != null) {
2097                 int offs = target.getCaretPosition();
2098                 Element elem = Utilities.getParagraphElement(target, offs);
2099                 offs = Math.min(target.getDocument().getLength(),
2100                                 elem.getEndOffset());
2101                 if (select) {
2102                     target.moveCaretPosition(offs);
2103                 } else {
2104                     target.setCaretPosition(offs);
2105                 }
2106             }
2107         }
2108 
2109         private boolean select;
2110     }
2111 
2112     /*
2113      * Move the caret to the beginning of the document.
2114      * @see DefaultEditorKit#beginAction
2115      * @see DefaultEditorKit#getActions
2116      */
2117     @SuppressWarnings("serial") // Superclass is not serializable across versions
2118     static class BeginAction extends TextAction {
2119 
2120         /* Create this object with the appropriate identifier. */
2121         BeginAction(String nm, boolean select) {
2122             super(nm);
2123             this.select = select;
2124         }
2125 
2126         /** The operation to perform when this action is triggered. */
2127         public void actionPerformed(ActionEvent e) {
2128             JTextComponent target = getTextComponent(e);
2129             if (target != null) {
2130                 if (select) {
2131                     target.moveCaretPosition(0);
2132                 } else {
2133                     target.setCaretPosition(0);
2134                 }
2135             }
2136         }
2137 
2138         private boolean select;
2139     }
2140 
2141     /*
2142      * Move the caret to the end of the document.
2143      * @see DefaultEditorKit#endAction
2144      * @see DefaultEditorKit#getActions
2145      */
2146     @SuppressWarnings("serial") // Superclass is not serializable across versions
2147     static class EndAction extends TextAction {
2148 
2149         /* Create this object with the appropriate identifier. */
2150         EndAction(String nm, boolean select) {
2151             super(nm);
2152             this.select = select;
2153         }
2154 
2155         /** The operation to perform when this action is triggered. */
2156         public void actionPerformed(ActionEvent e) {
2157             JTextComponent target = getTextComponent(e);
2158             if (target != null) {
2159                 Document doc = target.getDocument();
2160                 int dot = doc.getLength();
2161                 if (select) {
2162                     target.moveCaretPosition(dot);
2163                 } else {
2164                     target.setCaretPosition(dot);
2165                 }
2166             }
2167         }
2168 
2169         private boolean select;
2170     }
2171 
2172     /*
2173      * Select the word around the caret
2174      * @see DefaultEditorKit#endAction
2175      * @see DefaultEditorKit#getActions
2176      */
2177     @SuppressWarnings("serial") // Superclass is not serializable across versions
2178     static class SelectWordAction extends TextAction {
2179 
2180         /**
2181          * Create this action with the appropriate identifier.
2182          */
2183         SelectWordAction() {
2184             super(selectWordAction);
2185             start = new BeginWordAction("pigdog", false);
2186             end = new EndWordAction("pigdog", true);
2187         }
2188 
2189         /** The operation to perform when this action is triggered. */
2190         public void actionPerformed(ActionEvent e) {
2191             start.actionPerformed(e);
2192             end.actionPerformed(e);
2193         }
2194 
2195         private Action start;
2196         private Action end;
2197     }
2198 
2199     /*
2200      * Select the line around the caret
2201      * @see DefaultEditorKit#endAction
2202      * @see DefaultEditorKit#getActions
2203      */
2204     @SuppressWarnings("serial") // Superclass is not serializable across versions
2205     static class SelectLineAction extends TextAction {
2206 
2207         /**
2208          * Create this action with the appropriate identifier.
2209          */
2210         SelectLineAction() {
2211             super(selectLineAction);
2212             start = new BeginLineAction("pigdog", false);
2213             end = new EndLineAction("pigdog", true);
2214         }
2215 
2216         /** The operation to perform when this action is triggered. */
2217         public void actionPerformed(ActionEvent e) {
2218             start.actionPerformed(e);
2219             end.actionPerformed(e);
2220         }
2221 
2222         private Action start;
2223         private Action end;
2224     }
2225 
2226     /*
2227      * Select the paragraph around the caret
2228      * @see DefaultEditorKit#endAction
2229      * @see DefaultEditorKit#getActions
2230      */
2231     @SuppressWarnings("serial") // Superclass is not serializable across versions
2232     static class SelectParagraphAction extends TextAction {
2233 
2234         /**
2235          * Create this action with the appropriate identifier.
2236          */
2237         SelectParagraphAction() {
2238             super(selectParagraphAction);
2239             start = new BeginParagraphAction("pigdog", false);
2240             end = new EndParagraphAction("pigdog", true);
2241         }
2242 
2243         /** The operation to perform when this action is triggered. */
2244         public void actionPerformed(ActionEvent e) {
2245             start.actionPerformed(e);
2246             end.actionPerformed(e);
2247         }
2248 
2249         private Action start;
2250         private Action end;
2251     }
2252 
2253     /*
2254      * Select the entire document
2255      * @see DefaultEditorKit#endAction
2256      * @see DefaultEditorKit#getActions
2257      */
2258     @SuppressWarnings("serial") // Superclass is not serializable across versions
2259     static class SelectAllAction extends TextAction {
2260 
2261         /**
2262          * Create this action with the appropriate identifier.
2263          */
2264         SelectAllAction() {
2265             super(selectAllAction);
2266         }
2267 
2268         /** The operation to perform when this action is triggered. */
2269         public void actionPerformed(ActionEvent e) {
2270             JTextComponent target = getTextComponent(e);
2271             if (target != null) {
2272                 Document doc = target.getDocument();
2273                 target.setCaretPosition(0);
2274                 target.moveCaretPosition(doc.getLength());
2275             }
2276         }
2277 
2278     }
2279 
2280     /*
2281      * Remove the selection, if any.
2282      * @see DefaultEditorKit#unselectAction
2283      * @see DefaultEditorKit#getActions
2284      */
2285     @SuppressWarnings("serial") // Superclass is not serializable across versions
2286     static class UnselectAction extends TextAction {
2287 
2288         /**
2289          * Create this action with the appropriate identifier.
2290          */
2291         UnselectAction() {
2292             super(unselectAction);
2293         }
2294 
2295         /** The operation to perform when this action is triggered. */
2296         public void actionPerformed(ActionEvent e) {
2297             JTextComponent target = getTextComponent(e);
2298             if (target != null) {
2299                 target.setCaretPosition(target.getCaretPosition());
2300             }
2301         }
2302 
2303     }
2304 
2305     /*
2306      * Toggles the ComponentOrientation of the text component.
2307      * @see DefaultEditorKit#toggleComponentOrientationAction
2308      * @see DefaultEditorKit#getActions
2309      */
2310     @SuppressWarnings("serial") // Superclass is not serializable across versions
2311     static class ToggleComponentOrientationAction extends TextAction {
2312 
2313         /**
2314          * Create this action with the appropriate identifier.
2315          */
2316         ToggleComponentOrientationAction() {
2317             super(toggleComponentOrientationAction);
2318         }
2319 
2320         /** The operation to perform when this action is triggered. */
2321         public void actionPerformed(ActionEvent e) {
2322             JTextComponent target = getTextComponent(e);
2323             if (target != null) {
2324                 ComponentOrientation last = target.getComponentOrientation();
2325                 ComponentOrientation next;
2326                 if( last == ComponentOrientation.RIGHT_TO_LEFT )
2327                     next = ComponentOrientation.LEFT_TO_RIGHT;
2328                 else
2329                     next = ComponentOrientation.RIGHT_TO_LEFT;
2330                 target.setComponentOrientation(next);
2331                 target.repaint();
2332             }
2333         }
2334     }
2335 
2336 }