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