1 /*
   2  * Copyright (c) 1997, 2010, 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 java.io.*;
  28 import java.awt.*;
  29 import java.awt.event.ActionEvent;
  30 import java.text.*;
  31 import javax.swing.Action;
  32 import javax.swing.KeyStroke;
  33 import javax.swing.SwingConstants;
  34 import javax.swing.UIManager;
  35 
  36 /**
  37  * This is the set of things needed by a text component
  38  * to be a reasonably functioning editor for some <em>type</em>
  39  * of text document.  This implementation provides a default
  40  * implementation which treats text as plain text and
  41  * provides a minimal set of actions for a simple editor.
  42  * <p>
  43  * <dl>
  44  * <dt><b><font size=+1>Newlines</font></b>
  45  * <dd>
  46  * There are two properties which deal with newlines.  The
  47  * system property, <code>line.separator</code>, is defined to be
  48  * platform-dependent, either "\n", "\r", or "\r\n".  There is also
  49  * a property defined in <code>DefaultEditorKit</code>, called
  50  * <a href=#EndOfLineStringProperty><code>EndOfLineStringProperty</code></a>,
  51  * which is defined automatically when a document is loaded, to be
  52  * the first occurrence of any of the newline characters.
  53  * When a document is loaded, <code>EndOfLineStringProperty</code>
  54  * is set appropriately, and when the document is written back out, the
  55  * <code>EndOfLineStringProperty</code> is used.  But while the document
  56  * is in memory, the "\n" character is used to define a
  57  * newline, regardless of how the newline is defined when
  58  * the document is on disk.  Therefore, for searching purposes,
  59  * "\n" should always be used.  When a new document is created,
  60  * and the <code>EndOfLineStringProperty</code> has not been defined,
  61  * it will use the System property when writing out the
  62  * document.
  63  * <p>Note that <code>EndOfLineStringProperty</code> is set
  64  * on the <code>Document</code> using the <code>get/putProperty</code>
  65  * methods.  Subclasses may override this behavior.
  66  *
  67  * </dl>
  68  *
  69  * @author  Timothy Prinzing
  70  */
  71 public class DefaultEditorKit extends EditorKit {
  72 
  73     /**
  74      * default constructor for DefaultEditorKit
  75      */
  76     public DefaultEditorKit() {
  77     }
  78 
  79     /**
  80      * Gets the MIME type of the data that this
  81      * kit represents support for.  The default
  82      * is <code>text/plain</code>.
  83      *
  84      * @return the type
  85      */
  86     public String getContentType() {
  87         return "text/plain";
  88     }
  89 
  90     /**
  91      * Fetches a factory that is suitable for producing
  92      * views of any models that are produced by this
  93      * kit.  The default is to have the UI produce the
  94      * factory, so this method has no implementation.
  95      *
  96      * @return the view factory
  97      */
  98     public ViewFactory getViewFactory() {
  99         return null;
 100     }
 101 
 102     /**
 103      * Fetches the set of commands that can be used
 104      * on a text component that is using a model and
 105      * view produced by this kit.
 106      *
 107      * @return the command list
 108      */
 109     public Action[] getActions() {
 110         return defaultActions;
 111     }
 112 
 113     /**
 114      * Fetches a caret that can navigate through views
 115      * produced by the associated ViewFactory.
 116      *
 117      * @return the caret
 118      */
 119     public Caret createCaret() {
 120         return null;
 121     }
 122 
 123     /**
 124      * Creates an uninitialized text storage model (PlainDocument)
 125      * that is appropriate for this type of editor.
 126      *
 127      * @return the model
 128      */
 129     public Document createDefaultDocument() {
 130         return new PlainDocument();
 131     }
 132 
 133     /**
 134      * Inserts content from the given stream which is expected
 135      * to be in a format appropriate for this kind of content
 136      * handler.
 137      *
 138      * @param in  The stream to read from
 139      * @param doc The destination for the insertion.
 140      * @param pos The location in the document to place the
 141      *   content >= 0.
 142      * @exception IOException on any I/O error
 143      * @exception BadLocationException if pos represents an invalid
 144      *   location within the document.
 145      */
 146     public void read(InputStream in, Document doc, int pos)
 147         throws IOException, BadLocationException {
 148 
 149         read(new InputStreamReader(in), doc, pos);
 150     }
 151 
 152     /**
 153      * Writes content from a document to the given stream
 154      * in a format appropriate for this kind of content handler.
 155      *
 156      * @param out The stream to write to
 157      * @param doc The source for the write.
 158      * @param pos The location in the document to fetch the
 159      *   content >= 0.
 160      * @param len The amount to write out >= 0.
 161      * @exception IOException on any I/O error
 162      * @exception BadLocationException if pos represents an invalid
 163      *   location within the document.
 164      */
 165     public void write(OutputStream out, Document doc, int pos, int len)
 166         throws IOException, BadLocationException {
 167         OutputStreamWriter osw = new OutputStreamWriter(out);
 168 
 169         write(osw, doc, pos, len);
 170         osw.flush();
 171     }
 172 
 173     /**
 174      * Gets the input attributes for the pane. This method exists for
 175      * the benefit of StyledEditorKit so that the read method will
 176      * pick up the correct attributes to apply to inserted text.
 177      * This class's implementation simply returns null.
 178      *
 179      * @return null
 180      */
 181     MutableAttributeSet getInputAttributes() {
 182         return null;
 183     }
 184 
 185     /**
 186      * Inserts content from the given stream, which will be
 187      * treated as plain text.
 188      *
 189      * @param in  The stream to read from
 190      * @param doc The destination for the insertion.
 191      * @param pos The location in the document to place the
 192      *   content >= 0.
 193      * @exception IOException on any I/O error
 194      * @exception BadLocationException if pos represents an invalid
 195      *   location within the document.
 196      */
 197     public void read(Reader in, Document doc, int pos)
 198         throws IOException, BadLocationException {
 199 
 200         char[] buff = new char[4096];
 201         int nch;
 202         boolean lastWasCR = false;
 203         boolean isCRLF = false;
 204         boolean isCR = false;
 205         int last;
 206         boolean wasEmpty = (doc.getLength() == 0);
 207         AttributeSet attr = getInputAttributes();
 208 
 209         // Read in a block at a time, mapping \r\n to \n, as well as single
 210         // \r's to \n's. If a \r\n is encountered, \r\n will be set as the
 211         // newline string for the document, if \r is encountered it will
 212         // be set as the newline character, otherwise the newline property
 213         // for the document will be removed.
 214         while ((nch = in.read(buff, 0, buff.length)) != -1) {
 215             last = 0;
 216             for(int counter = 0; counter < nch; counter++) {
 217                 switch(buff[counter]) {
 218                 case '\r':
 219                     if (lastWasCR) {
 220                         isCR = true;
 221                         if (counter == 0) {
 222                             doc.insertString(pos, "\n", attr);
 223                             pos++;
 224                         }
 225                         else {
 226                             buff[counter - 1] = '\n';
 227                         }
 228                     }
 229                     else {
 230                         lastWasCR = true;
 231                     }
 232                     break;
 233                 case '\n':
 234                     if (lastWasCR) {
 235                         if (counter > (last + 1)) {
 236                             doc.insertString(pos, new String(buff, last,
 237                                             counter - last - 1), attr);
 238                             pos += (counter - last - 1);
 239                         }
 240                         // else nothing to do, can skip \r, next write will
 241                         // write \n
 242                         lastWasCR = false;
 243                         last = counter;
 244                         isCRLF = true;
 245                     }
 246                     break;
 247                 default:
 248                     if (lastWasCR) {
 249                         isCR = true;
 250                         if (counter == 0) {
 251                             doc.insertString(pos, "\n", attr);
 252                             pos++;
 253                         }
 254                         else {
 255                             buff[counter - 1] = '\n';
 256                         }
 257                         lastWasCR = false;
 258                     }
 259                     break;
 260                 }
 261             }
 262             if (last < nch) {
 263                 if(lastWasCR) {
 264                     if (last < (nch - 1)) {
 265                         doc.insertString(pos, new String(buff, last,
 266                                          nch - last - 1), attr);
 267                         pos += (nch - last - 1);
 268                     }
 269                 }
 270                 else {
 271                     doc.insertString(pos, new String(buff, last,
 272                                      nch - last), attr);
 273                     pos += (nch - last);
 274                 }
 275             }
 276         }
 277         if (lastWasCR) {
 278             doc.insertString(pos, "\n", attr);
 279             isCR = true;
 280         }
 281         if (wasEmpty) {
 282             if (isCRLF) {
 283                 doc.putProperty(EndOfLineStringProperty, "\r\n");
 284             }
 285             else if (isCR) {
 286                 doc.putProperty(EndOfLineStringProperty, "\r");
 287             }
 288             else {
 289                 doc.putProperty(EndOfLineStringProperty, "\n");
 290             }
 291         }
 292     }
 293 
 294     /**
 295      * Writes content from a document to the given stream
 296      * as plain text.
 297      *
 298      * @param out  The stream to write to
 299      * @param doc The source for the write.
 300      * @param pos The location in the document to fetch the
 301      *   content from >= 0.
 302      * @param len The amount to write out >= 0.
 303      * @exception IOException on any I/O error
 304      * @exception BadLocationException if pos is not within 0 and
 305      *   the length of the document.
 306      */
 307     public void write(Writer out, Document doc, int pos, int len)
 308         throws IOException, BadLocationException {
 309 
 310         if ((pos < 0) || ((pos + len) > doc.getLength())) {
 311             throw new BadLocationException("DefaultEditorKit.write", pos);
 312         }
 313         Segment data = new Segment();
 314         int nleft = len;
 315         int offs = pos;
 316         Object endOfLineProperty = doc.getProperty(EndOfLineStringProperty);
 317         if (endOfLineProperty == null) {
 318             try {
 319                 endOfLineProperty = System.getProperty("line.separator");
 320             } catch (SecurityException se) { }
 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<sup><font size="-2">TM</font></sup>
 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     public static class DefaultKeyTypedAction extends TextAction {
 851 
 852         /**
 853          * Creates this object with the appropriate identifier.
 854          */
 855         public DefaultKeyTypedAction() {
 856             super(defaultKeyTypedAction);
 857         }
 858 
 859         /**
 860          * The operation to perform when this action is triggered.
 861          *
 862          * @param e the action event
 863          */
 864         public void actionPerformed(ActionEvent e) {
 865             JTextComponent target = getTextComponent(e);
 866             if ((target != null) && (e != null)) {
 867                 if ((! target.isEditable()) || (! target.isEnabled())) {
 868                     return;
 869                 }
 870                 String content = e.getActionCommand();
 871                 int mod = e.getModifiers();
 872                 if ((content != null) && (content.length() > 0) &&
 873                     ((mod & ActionEvent.ALT_MASK) == (mod & ActionEvent.CTRL_MASK))) {
 874                     char c = content.charAt(0);
 875                     if ((c >= 0x20) && (c != 0x7F)) {
 876                         target.replaceSelection(content);
 877                     }
 878                 }
 879             }
 880         }
 881     }
 882 
 883     /**
 884      * Places content into the associated document.
 885      * If there is a selection, it is removed before
 886      * the new content is added.
 887      * <p>
 888      * <strong>Warning:</strong>
 889      * Serialized objects of this class will not be compatible with
 890      * future Swing releases. The current serialization support is
 891      * appropriate for short term storage or RMI between applications running
 892      * the same version of Swing.  As of 1.4, support for long term storage
 893      * of all JavaBeans<sup><font size="-2">TM</font></sup>
 894      * has been added to the <code>java.beans</code> package.
 895      * Please see {@link java.beans.XMLEncoder}.
 896      *
 897      * @see DefaultEditorKit#insertContentAction
 898      * @see DefaultEditorKit#getActions
 899      */
 900     public static class InsertContentAction extends TextAction {
 901 
 902         /**
 903          * Creates this object with the appropriate identifier.
 904          */
 905         public InsertContentAction() {
 906             super(insertContentAction);
 907         }
 908 
 909         /**
 910          * The operation to perform when this action is triggered.
 911          *
 912          * @param e the action event
 913          */
 914         public void actionPerformed(ActionEvent e) {
 915             JTextComponent target = getTextComponent(e);
 916             if ((target != null) && (e != null)) {
 917                 if ((! target.isEditable()) || (! target.isEnabled())) {
 918                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 919                     return;
 920                 }
 921                 String content = e.getActionCommand();
 922                 if (content != null) {
 923                     target.replaceSelection(content);
 924                 } else {
 925                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 926                 }
 927             }
 928         }
 929     }
 930 
 931     /**
 932      * Places a line/paragraph break into the document.
 933      * If there is a selection, it is removed before
 934      * the break is added.
 935      * <p>
 936      * <strong>Warning:</strong>
 937      * Serialized objects of this class will not be compatible with
 938      * future Swing releases. The current serialization support is
 939      * appropriate for short term storage or RMI between applications running
 940      * the same version of Swing.  As of 1.4, support for long term storage
 941      * of all JavaBeans<sup><font size="-2">TM</font></sup>
 942      * has been added to the <code>java.beans</code> package.
 943      * Please see {@link java.beans.XMLEncoder}.
 944      *
 945      * @see DefaultEditorKit#insertBreakAction
 946      * @see DefaultEditorKit#getActions
 947      */
 948     public static class InsertBreakAction extends TextAction {
 949 
 950         /**
 951          * Creates this object with the appropriate identifier.
 952          */
 953         public InsertBreakAction() {
 954             super(insertBreakAction);
 955         }
 956 
 957         /**
 958          * The operation to perform when this action is triggered.
 959          *
 960          * @param e the action event
 961          */
 962         public void actionPerformed(ActionEvent e) {
 963             JTextComponent target = getTextComponent(e);
 964             if (target != null) {
 965                 if ((! target.isEditable()) || (! target.isEnabled())) {
 966                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 967                     return;
 968                 }
 969                 target.replaceSelection("\n");
 970             }
 971         }
 972     }
 973 
 974     /**
 975      * Places a tab character into the document. If there
 976      * is a selection, it is removed before the tab is added.
 977      * <p>
 978      * <strong>Warning:</strong>
 979      * Serialized objects of this class will not be compatible with
 980      * future Swing releases. The current serialization support is
 981      * appropriate for short term storage or RMI between applications running
 982      * the same version of Swing.  As of 1.4, support for long term storage
 983      * of all JavaBeans<sup><font size="-2">TM</font></sup>
 984      * has been added to the <code>java.beans</code> package.
 985      * Please see {@link java.beans.XMLEncoder}.
 986      *
 987      * @see DefaultEditorKit#insertTabAction
 988      * @see DefaultEditorKit#getActions
 989      */
 990     public static class InsertTabAction extends TextAction {
 991 
 992         /**
 993          * Creates this object with the appropriate identifier.
 994          */
 995         public InsertTabAction() {
 996             super(insertTabAction);
 997         }
 998 
 999         /**
1000          * The operation to perform when this action is triggered.
1001          *
1002          * @param e the action event
1003          */
1004         public void actionPerformed(ActionEvent e) {
1005             JTextComponent target = getTextComponent(e);
1006             if (target != null) {
1007                 if ((! target.isEditable()) || (! target.isEnabled())) {
1008                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1009                     return;
1010                 }
1011                 target.replaceSelection("\t");
1012             }
1013         }
1014     }
1015 
1016     /*
1017      * Deletes the character of content that precedes the
1018      * current caret position.
1019      * @see DefaultEditorKit#deletePrevCharAction
1020      * @see DefaultEditorKit#getActions
1021      */
1022     static class DeletePrevCharAction extends TextAction {
1023 
1024         /**
1025          * Creates this object with the appropriate identifier.
1026          */
1027         DeletePrevCharAction() {
1028             super(deletePrevCharAction);
1029         }
1030 
1031         /**
1032          * The operation to perform when this action is triggered.
1033          *
1034          * @param e the action event
1035          */
1036         public void actionPerformed(ActionEvent e) {
1037             JTextComponent target = getTextComponent(e);
1038             boolean beep = true;
1039             if ((target != null) && (target.isEditable())) {
1040                 try {
1041                     Document doc = target.getDocument();
1042                     Caret caret = target.getCaret();
1043                     int dot = caret.getDot();
1044                     int mark = caret.getMark();
1045                     if (dot != mark) {
1046                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1047                         beep = false;
1048                     } else if (dot > 0) {
1049                         int delChars = 1;
1050 
1051                         if (dot > 1) {
1052                             String dotChars = doc.getText(dot - 2, 2);
1053                             char c0 = dotChars.charAt(0);
1054                             char c1 = dotChars.charAt(1);
1055 
1056                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1057                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1058                                 delChars = 2;
1059                             }
1060                         }
1061 
1062                         doc.remove(dot - delChars, delChars);
1063                         beep = false;
1064                     }
1065                 } catch (BadLocationException bl) {
1066                 }
1067             }
1068             if (beep) {
1069                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1070             }
1071         }
1072     }
1073 
1074     /*
1075      * Deletes the character of content that follows the
1076      * current caret position.
1077      * @see DefaultEditorKit#deleteNextCharAction
1078      * @see DefaultEditorKit#getActions
1079      */
1080     static class DeleteNextCharAction extends TextAction {
1081 
1082         /* Create this object with the appropriate identifier. */
1083         DeleteNextCharAction() {
1084             super(deleteNextCharAction);
1085         }
1086 
1087         /** The operation to perform when this action is triggered. */
1088         public void actionPerformed(ActionEvent e) {
1089             JTextComponent target = getTextComponent(e);
1090             boolean beep = true;
1091             if ((target != null) && (target.isEditable())) {
1092                 try {
1093                     Document doc = target.getDocument();
1094                     Caret caret = target.getCaret();
1095                     int dot = caret.getDot();
1096                     int mark = caret.getMark();
1097                     if (dot != mark) {
1098                         doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
1099                         beep = false;
1100                     } else if (dot < doc.getLength()) {
1101                         int delChars = 1;
1102 
1103                         if (dot < doc.getLength() - 1) {
1104                             String dotChars = doc.getText(dot, 2);
1105                             char c0 = dotChars.charAt(0);
1106                             char c1 = dotChars.charAt(1);
1107 
1108                             if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
1109                                 c1 >= '\uDC00' && c1 <= '\uDFFF') {
1110                                 delChars = 2;
1111                             }
1112                         }
1113 
1114                         doc.remove(dot, delChars);
1115                         beep = false;
1116                     }
1117                 } catch (BadLocationException bl) {
1118                 }
1119             }
1120             if (beep) {
1121                 UIManager.getLookAndFeel().provideErrorFeedback(target);
1122             }
1123         }
1124     }
1125 
1126 
1127     /*
1128      * Deletes the word that precedes/follows the beginning of the selection.
1129      * @see DefaultEditorKit#getActions
1130      */
1131     static class DeleteWordAction extends TextAction {
1132         DeleteWordAction(String name) {
1133             super(name);
1134             assert (name == deletePrevWordAction)
1135                 || (name == deleteNextWordAction);
1136         }
1137         /**
1138          * The operation to perform when this action is triggered.
1139          *
1140          * @param e the action event
1141          */
1142         public void actionPerformed(ActionEvent e) {
1143             final JTextComponent target = getTextComponent(e);
1144             if ((target != null) && (e != null)) {
1145                 if ((! target.isEditable()) || (! target.isEnabled())) {
1146                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1147                     return;
1148                 }
1149                 boolean beep = true;
1150                 try {
1151                     final int start = target.getSelectionStart();
1152                     final Element line =
1153                         Utilities.getParagraphElement(target, start);
1154                     int end;
1155                     if (deleteNextWordAction == getValue(Action.NAME)) {
1156                         end = Utilities.
1157                             getNextWordInParagraph(target, line, start, false);
1158                         if (end == java.text.BreakIterator.DONE) {
1159                             //last word in the paragraph
1160                             final int endOfLine = line.getEndOffset();
1161                             if (start == endOfLine - 1) {
1162                                 //for last position remove last \n
1163                                 end = endOfLine;
1164                             } else {
1165                                 //remove to the end of the paragraph
1166                                 end = endOfLine - 1;
1167                             }
1168                         }
1169                     } else {
1170                         end = Utilities.
1171                             getPrevWordInParagraph(target, line, start);
1172                         if (end == java.text.BreakIterator.DONE) {
1173                             //there is no previous word in the paragraph
1174                             final int startOfLine = line.getStartOffset();
1175                             if (start == startOfLine) {
1176                                 //for first position remove previous \n
1177                                 end = startOfLine - 1;
1178                             } else {
1179                                 //remove to the start of the paragraph
1180                                 end = startOfLine;
1181                             }
1182                         }
1183                     }
1184                     int offs = Math.min(start, end);
1185                     int len = Math.abs(end - start);
1186                     if (offs >= 0) {
1187                         target.getDocument().remove(offs, len);
1188                         beep = false;
1189                     }
1190                 } catch (BadLocationException ignore) {
1191                 }
1192                 if (beep) {
1193                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1194                 }
1195             }
1196         }
1197     }
1198 
1199 
1200     /*
1201      * Sets the editor into read-only mode.
1202      * @see DefaultEditorKit#readOnlyAction
1203      * @see DefaultEditorKit#getActions
1204      */
1205     static class ReadOnlyAction extends TextAction {
1206 
1207         /* Create this object with the appropriate identifier. */
1208         ReadOnlyAction() {
1209             super(readOnlyAction);
1210         }
1211 
1212         /**
1213          * The operation to perform when this action is triggered.
1214          *
1215          * @param e the action event
1216          */
1217         public void actionPerformed(ActionEvent e) {
1218             JTextComponent target = getTextComponent(e);
1219             if (target != null) {
1220                 target.setEditable(false);
1221             }
1222         }
1223     }
1224 
1225     /*
1226      * Sets the editor into writeable mode.
1227      * @see DefaultEditorKit#writableAction
1228      * @see DefaultEditorKit#getActions
1229      */
1230     static class WritableAction extends TextAction {
1231 
1232         /* Create this object with the appropriate identifier. */
1233         WritableAction() {
1234             super(writableAction);
1235         }
1236 
1237         /**
1238          * The operation to perform when this action is triggered.
1239          *
1240          * @param e the action event
1241          */
1242         public void actionPerformed(ActionEvent e) {
1243             JTextComponent target = getTextComponent(e);
1244             if (target != null) {
1245                 target.setEditable(true);
1246             }
1247         }
1248     }
1249 
1250     /**
1251      * Cuts the selected region and place its contents
1252      * into the system clipboard.
1253      * <p>
1254      * <strong>Warning:</strong>
1255      * Serialized objects of this class will not be compatible with
1256      * future Swing releases. The current serialization support is
1257      * appropriate for short term storage or RMI between applications running
1258      * the same version of Swing.  As of 1.4, support for long term storage
1259      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1260      * has been added to the <code>java.beans</code> package.
1261      * Please see {@link java.beans.XMLEncoder}.
1262      *
1263      * @see DefaultEditorKit#cutAction
1264      * @see DefaultEditorKit#getActions
1265      */
1266     public static class CutAction extends TextAction {
1267 
1268         /** Create this object with the appropriate identifier. */
1269         public CutAction() {
1270             super(cutAction);
1271         }
1272 
1273         /**
1274          * The operation to perform when this action is triggered.
1275          *
1276          * @param e the action event
1277          */
1278         public void actionPerformed(ActionEvent e) {
1279             JTextComponent target = getTextComponent(e);
1280             if (target != null) {
1281                 target.cut();
1282             }
1283         }
1284     }
1285 
1286     /**
1287      * Copies the selected region and place its contents
1288      * into the system clipboard.
1289      * <p>
1290      * <strong>Warning:</strong>
1291      * Serialized objects of this class will not be compatible with
1292      * future Swing releases. The current serialization support is
1293      * appropriate for short term storage or RMI between applications running
1294      * the same version of Swing.  As of 1.4, support for long term storage
1295      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1296      * has been added to the <code>java.beans</code> package.
1297      * Please see {@link java.beans.XMLEncoder}.
1298      *
1299      * @see DefaultEditorKit#copyAction
1300      * @see DefaultEditorKit#getActions
1301      */
1302     public static class CopyAction extends TextAction {
1303 
1304         /** Create this object with the appropriate identifier. */
1305         public CopyAction() {
1306             super(copyAction);
1307         }
1308 
1309         /**
1310          * The operation to perform when this action is triggered.
1311          *
1312          * @param e the action event
1313          */
1314         public void actionPerformed(ActionEvent e) {
1315             JTextComponent target = getTextComponent(e);
1316             if (target != null) {
1317                 target.copy();
1318             }
1319         }
1320     }
1321 
1322     /**
1323      * Pastes the contents of the system clipboard into the
1324      * selected region, or before the caret if nothing is
1325      * selected.
1326      * <p>
1327      * <strong>Warning:</strong>
1328      * Serialized objects of this class will not be compatible with
1329      * future Swing releases. The current serialization support is
1330      * appropriate for short term storage or RMI between applications running
1331      * the same version of Swing.  As of 1.4, support for long term storage
1332      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1333      * has been added to the <code>java.beans</code> package.
1334      * Please see {@link java.beans.XMLEncoder}.
1335      *
1336      * @see DefaultEditorKit#pasteAction
1337      * @see DefaultEditorKit#getActions
1338      */
1339     public static class PasteAction extends TextAction {
1340 
1341         /** Create this object with the appropriate identifier. */
1342         public PasteAction() {
1343             super(pasteAction);
1344         }
1345 
1346         /**
1347          * The operation to perform when this action is triggered.
1348          *
1349          * @param e the action event
1350          */
1351         public void actionPerformed(ActionEvent e) {
1352             JTextComponent target = getTextComponent(e);
1353             if (target != null) {
1354                 target.paste();
1355             }
1356         }
1357     }
1358 
1359     /**
1360      * Creates a beep.
1361      * <p>
1362      * <strong>Warning:</strong>
1363      * Serialized objects of this class will not be compatible with
1364      * future Swing releases. The current serialization support is
1365      * appropriate for short term storage or RMI between applications running
1366      * the same version of Swing.  As of 1.4, support for long term storage
1367      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1368      * has been added to the <code>java.beans</code> package.
1369      * Please see {@link java.beans.XMLEncoder}.
1370      *
1371      * @see DefaultEditorKit#beepAction
1372      * @see DefaultEditorKit#getActions
1373      */
1374     public static class BeepAction extends TextAction {
1375 
1376         /** Create this object with the appropriate identifier. */
1377         public BeepAction() {
1378             super(beepAction);
1379         }
1380 
1381         /**
1382          * The operation to perform when this action is triggered.
1383          *
1384          * @param e the action event
1385          */
1386         public void actionPerformed(ActionEvent e) {
1387             JTextComponent target = getTextComponent(e);
1388             UIManager.getLookAndFeel().provideErrorFeedback(target);
1389         }
1390     }
1391 
1392     /**
1393      * Scrolls up/down vertically.  The select version of this action extends
1394      * the selection, instead of simply moving the caret.
1395      *
1396      * @see DefaultEditorKit#pageUpAction
1397      * @see DefaultEditorKit#pageDownAction
1398      * @see DefaultEditorKit#getActions
1399      */
1400     static class VerticalPageAction extends TextAction {
1401 
1402         /** Create this object with the appropriate identifier. */
1403         public VerticalPageAction(String nm, int direction, boolean select) {
1404             super(nm);
1405             this.select = select;
1406             this.direction = direction;
1407         }
1408 
1409         /** The operation to perform when this action is triggered. */
1410         public void actionPerformed(ActionEvent e) {
1411             JTextComponent target = getTextComponent(e);
1412             if (target != null) {
1413                 Rectangle visible = target.getVisibleRect();
1414                 Rectangle newVis = new Rectangle(visible);
1415                 int selectedIndex = target.getCaretPosition();
1416                 int scrollAmount = direction *
1417                         target.getScrollableBlockIncrement(
1418                                   visible, SwingConstants.VERTICAL, direction);
1419                 int initialY = visible.y;
1420                 Caret caret = target.getCaret();
1421                 Point magicPosition = caret.getMagicCaretPosition();
1422 
1423                 if (selectedIndex != -1) {
1424                     try {
1425                         Rectangle dotBounds = target.modelToView(
1426                                                      selectedIndex);
1427                         int x = (magicPosition != null) ? magicPosition.x :
1428                                                           dotBounds.x;
1429                         int h = dotBounds.height;
1430                         if (h > 0) {
1431                             // We want to scroll by a multiple of caret height,
1432                             // rounding towards lower integer
1433                             scrollAmount = scrollAmount / h * h;
1434                         }
1435                         newVis.y = constrainY(target,
1436                                 initialY + scrollAmount, visible.height);
1437 
1438                         int newIndex;
1439 
1440                         if (visible.contains(dotBounds.x, dotBounds.y)) {
1441                             // Dot is currently visible, base the new
1442                             // location off the old, or
1443                             newIndex = target.viewToModel(
1444                                 new Point(x, constrainY(target,
1445                                           dotBounds.y + scrollAmount, 0)));
1446                         }
1447                         else {
1448                             // Dot isn't visible, choose the top or the bottom
1449                             // for the new location.
1450                             if (direction == -1) {
1451                                 newIndex = target.viewToModel(new Point(
1452                                     x, newVis.y));
1453                             }
1454                             else {
1455                                 newIndex = target.viewToModel(new Point(
1456                                     x, newVis.y + visible.height));
1457                             }
1458                         }
1459                         newIndex = constrainOffset(target, newIndex);
1460                         if (newIndex != selectedIndex) {
1461                             // Make sure the new visible location contains
1462                             // the location of dot, otherwise Caret will
1463                             // cause an additional scroll.
1464                             int newY = getAdjustedY(target, newVis, newIndex);
1465 
1466                             if (direction == -1 && newY <= initialY || direction == 1 && newY >= initialY) {
1467                                 // Change index and correct newVis.y only if won't cause scrolling upward
1468                                 newVis.y = newY;
1469 
1470                                 if (select) {
1471                                     target.moveCaretPosition(newIndex);
1472                                 } else {
1473                                     target.setCaretPosition(newIndex);
1474                                 }
1475                             }
1476                         }
1477                     } catch (BadLocationException ble) { }
1478                 } else {
1479                     newVis.y = constrainY(target,
1480                             initialY + scrollAmount, visible.height);
1481                 }
1482                 if (magicPosition != null) {
1483                     caret.setMagicCaretPosition(magicPosition);
1484                 }
1485                 target.scrollRectToVisible(newVis);
1486             }
1487         }
1488 
1489         /**
1490          * Makes sure <code>y</code> is a valid location in
1491          * <code>target</code>.
1492          */
1493         private int constrainY(JTextComponent target, int y, int vis) {
1494             if (y < 0) {
1495                 y = 0;
1496             }
1497             else if (y + vis > target.getHeight()) {
1498                 y = Math.max(0, target.getHeight() - vis);
1499             }
1500             return y;
1501         }
1502 
1503         /**
1504          * Ensures that <code>offset</code> is a valid offset into the
1505          * model for <code>text</code>.
1506          */
1507         private int constrainOffset(JTextComponent text, int offset) {
1508             Document doc = text.getDocument();
1509 
1510             if ((offset != 0) && (offset > doc.getLength())) {
1511                 offset = doc.getLength();
1512             }
1513             if (offset  < 0) {
1514                 offset = 0;
1515             }
1516             return offset;
1517         }
1518 
1519         /**
1520          * Returns adjustsed {@code y} position that indicates the location to scroll to
1521          * after selecting <code>index</code>.
1522          */
1523         private int getAdjustedY(JTextComponent text, Rectangle visible, int index) {
1524             int result = visible.y;
1525 
1526             try {
1527                 Rectangle dotBounds = text.modelToView(index);
1528 
1529                 if (dotBounds.y < visible.y) {
1530                     result = dotBounds.y;
1531                 } else {
1532                     if ((dotBounds.y > visible.y + visible.height) ||
1533                             (dotBounds.y + dotBounds.height > visible.y + visible.height)) {
1534                         result = dotBounds.y + dotBounds.height - visible.height;
1535                     }
1536                 }
1537             } catch (BadLocationException ble) {
1538             }
1539 
1540             return result;
1541         }
1542 
1543         /**
1544          * Adjusts the Rectangle to contain the bounds of the character at
1545          * <code>index</code> in response to a page up.
1546          */
1547         private boolean select;
1548 
1549         /**
1550          * Direction to scroll, 1 is down, -1 is up.
1551          */
1552         private int direction;
1553     }
1554 
1555 
1556     /**
1557      * Pages one view to the left or right.
1558      */
1559     static class PageAction extends TextAction {
1560 
1561         /** Create this object with the appropriate identifier. */
1562         public PageAction(String nm, boolean left, boolean select) {
1563             super(nm);
1564             this.select = select;
1565             this.left = left;
1566         }
1567 
1568         /** The operation to perform when this action is triggered. */
1569         public void actionPerformed(ActionEvent e) {
1570             JTextComponent target = getTextComponent(e);
1571             if (target != null) {
1572                 int selectedIndex;
1573                 Rectangle visible = new Rectangle();
1574                 target.computeVisibleRect(visible);
1575                 if (left) {
1576                     visible.x = Math.max(0, visible.x - visible.width);
1577                 }
1578                 else {
1579                     visible.x += visible.width;
1580                 }
1581 
1582                 selectedIndex = target.getCaretPosition();
1583                 if(selectedIndex != -1) {
1584                     if (left) {
1585                         selectedIndex = target.viewToModel
1586                             (new Point(visible.x, visible.y));
1587                     }
1588                     else {
1589                         selectedIndex = target.viewToModel
1590                             (new Point(visible.x + visible.width - 1,
1591                                        visible.y + visible.height - 1));
1592                     }
1593                     Document doc = target.getDocument();
1594                     if ((selectedIndex != 0) &&
1595                         (selectedIndex  > (doc.getLength()-1))) {
1596                         selectedIndex = doc.getLength()-1;
1597                     }
1598                     else if(selectedIndex  < 0) {
1599                         selectedIndex = 0;
1600                     }
1601                     if (select)
1602                         target.moveCaretPosition(selectedIndex);
1603                     else
1604                         target.setCaretPosition(selectedIndex);
1605                 }
1606             }
1607         }
1608 
1609         private boolean select;
1610         private boolean left;
1611     }
1612 
1613     static class DumpModelAction extends TextAction {
1614 
1615         DumpModelAction() {
1616             super("dump-model");
1617         }
1618 
1619         public void actionPerformed(ActionEvent e) {
1620             JTextComponent target = getTextComponent(e);
1621             if (target != null) {
1622                 Document d = target.getDocument();
1623                 if (d instanceof AbstractDocument) {
1624                     ((AbstractDocument) d).dump(System.err);
1625                 }
1626             }
1627         }
1628     }
1629 
1630     /*
1631      * Action to move the selection by way of the
1632      * getNextVisualPositionFrom method. Constructor indicates direction
1633      * to use.
1634      */
1635     static class NextVisualPositionAction extends TextAction {
1636 
1637         /**
1638          * Create this action with the appropriate identifier.
1639          * @param nm  the name of the action, Action.NAME.
1640          * @param select whether to extend the selection when
1641          *  changing the caret position.
1642          */
1643         NextVisualPositionAction(String nm, boolean select, int direction) {
1644             super(nm);
1645             this.select = select;
1646             this.direction = direction;
1647         }
1648 
1649         /** The operation to perform when this action is triggered. */
1650         public void actionPerformed(ActionEvent e) {
1651             JTextComponent target = getTextComponent(e);
1652             if (target != null) {
1653                 Caret caret = target.getCaret();
1654                 DefaultCaret bidiCaret = (caret instanceof DefaultCaret) ?
1655                                               (DefaultCaret)caret : null;
1656                 int dot = caret.getDot();
1657                 Position.Bias[] bias = new Position.Bias[1];
1658                 Point magicPosition = caret.getMagicCaretPosition();
1659 
1660                 try {
1661                     if(magicPosition == null &&
1662                        (direction == SwingConstants.NORTH ||
1663                         direction == SwingConstants.SOUTH)) {
1664                         Rectangle r = (bidiCaret != null) ?
1665                                 target.getUI().modelToView(target, dot,
1666                                                       bidiCaret.getDotBias()) :
1667                                 target.modelToView(dot);
1668                         magicPosition = new Point(r.x, r.y);
1669                     }
1670 
1671                     NavigationFilter filter = target.getNavigationFilter();
1672 
1673                     if (filter != null) {
1674                         dot = filter.getNextVisualPositionFrom
1675                                      (target, dot, (bidiCaret != null) ?
1676                                       bidiCaret.getDotBias() :
1677                                       Position.Bias.Forward, direction, bias);
1678                     }
1679                     else {
1680                         dot = target.getUI().getNextVisualPositionFrom
1681                                      (target, dot, (bidiCaret != null) ?
1682                                       bidiCaret.getDotBias() :
1683                                       Position.Bias.Forward, direction, bias);
1684                     }
1685                     if(bias[0] == null) {
1686                         bias[0] = Position.Bias.Forward;
1687                     }
1688                     if(bidiCaret != null) {
1689                         if (select) {
1690                             bidiCaret.moveDot(dot, bias[0]);
1691                         } else {
1692                             bidiCaret.setDot(dot, bias[0]);
1693                         }
1694                     }
1695                     else {
1696                         if (select) {
1697                             caret.moveDot(dot);
1698                         } else {
1699                             caret.setDot(dot);
1700                         }
1701                     }
1702                     if(magicPosition != null &&
1703                        (direction == SwingConstants.NORTH ||
1704                         direction == SwingConstants.SOUTH)) {
1705                         target.getCaret().setMagicCaretPosition(magicPosition);
1706                     }
1707                 } catch (BadLocationException ex) {
1708                 }
1709             }
1710         }
1711 
1712         private boolean select;
1713         private int direction;
1714     }
1715 
1716     /*
1717      * Position the caret to the beginning of the word.
1718      * @see DefaultEditorKit#beginWordAction
1719      * @see DefaultEditorKit#selectBeginWordAction
1720      * @see DefaultEditorKit#getActions
1721      */
1722     static class BeginWordAction extends TextAction {
1723 
1724         /**
1725          * Create this action with the appropriate identifier.
1726          * @param nm  the name of the action, Action.NAME.
1727          * @param select whether to extend the selection when
1728          *  changing the caret position.
1729          */
1730         BeginWordAction(String nm, boolean select) {
1731             super(nm);
1732             this.select = select;
1733         }
1734 
1735         /** The operation to perform when this action is triggered. */
1736         public void actionPerformed(ActionEvent e) {
1737             JTextComponent target = getTextComponent(e);
1738             if (target != null) {
1739                 try {
1740                     int offs = target.getCaretPosition();
1741                     int begOffs = Utilities.getWordStart(target, offs);
1742                     if (select) {
1743                         target.moveCaretPosition(begOffs);
1744                     } else {
1745                         target.setCaretPosition(begOffs);
1746                     }
1747                 } catch (BadLocationException bl) {
1748                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1749                 }
1750             }
1751         }
1752 
1753         private boolean select;
1754     }
1755 
1756     /*
1757      * Position the caret to the end of the word.
1758      * @see DefaultEditorKit#endWordAction
1759      * @see DefaultEditorKit#selectEndWordAction
1760      * @see DefaultEditorKit#getActions
1761      */
1762     static class EndWordAction extends TextAction {
1763 
1764         /**
1765          * Create this action with the appropriate identifier.
1766          * @param nm  the name of the action, Action.NAME.
1767          * @param select whether to extend the selection when
1768          *  changing the caret position.
1769          */
1770         EndWordAction(String nm, boolean select) {
1771             super(nm);
1772             this.select = select;
1773         }
1774 
1775         /** The operation to perform when this action is triggered. */
1776         public void actionPerformed(ActionEvent e) {
1777             JTextComponent target = getTextComponent(e);
1778             if (target != null) {
1779                 try {
1780                     int offs = target.getCaretPosition();
1781                     int endOffs = Utilities.getWordEnd(target, offs);
1782                     if (select) {
1783                         target.moveCaretPosition(endOffs);
1784                     } else {
1785                         target.setCaretPosition(endOffs);
1786                     }
1787                 } catch (BadLocationException bl) {
1788                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1789                 }
1790             }
1791         }
1792 
1793         private boolean select;
1794     }
1795 
1796     /*
1797      * Position the caret to the beginning of the previous word.
1798      * @see DefaultEditorKit#previousWordAction
1799      * @see DefaultEditorKit#selectPreviousWordAction
1800      * @see DefaultEditorKit#getActions
1801      */
1802     static class PreviousWordAction extends TextAction {
1803 
1804         /**
1805          * Create this action with the appropriate identifier.
1806          * @param nm  the name of the action, Action.NAME.
1807          * @param select whether to extend the selection when
1808          *  changing the caret position.
1809          */
1810         PreviousWordAction(String nm, boolean select) {
1811             super(nm);
1812             this.select = select;
1813         }
1814 
1815         /** The operation to perform when this action is triggered. */
1816         public void actionPerformed(ActionEvent e) {
1817             JTextComponent target = getTextComponent(e);
1818             if (target != null) {
1819                 int offs = target.getCaretPosition();
1820                 boolean failed = false;
1821                 try {
1822                     Element curPara =
1823                             Utilities.getParagraphElement(target, offs);
1824                     offs = Utilities.getPreviousWord(target, offs);
1825                     if(offs < curPara.getStartOffset()) {
1826                         // we should first move to the end of the
1827                         // previous paragraph (bug #4278839)
1828                         offs = Utilities.getParagraphElement(target, offs).
1829                                 getEndOffset() - 1;
1830                     }
1831                 } catch (BadLocationException bl) {
1832                     if (offs != 0) {
1833                         offs = 0;
1834                     }
1835                     else {
1836                         failed = true;
1837                     }
1838                 }
1839                 if (!failed) {
1840                     if (select) {
1841                         target.moveCaretPosition(offs);
1842                     } else {
1843                         target.setCaretPosition(offs);
1844                     }
1845                 }
1846                 else {
1847                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1848                 }
1849             }
1850         }
1851 
1852         private boolean select;
1853     }
1854 
1855     /*
1856      * Position the caret to the next of the word.
1857      * @see DefaultEditorKit#nextWordAction
1858      * @see DefaultEditorKit#selectNextWordAction
1859      * @see DefaultEditorKit#getActions
1860      */
1861     static class NextWordAction extends TextAction {
1862 
1863         /**
1864          * Create this action with the appropriate identifier.
1865          * @param nm  the name of the action, Action.NAME.
1866          * @param select whether to extend the selection when
1867          *  changing the caret position.
1868          */
1869         NextWordAction(String nm, boolean select) {
1870             super(nm);
1871             this.select = select;
1872         }
1873 
1874         /** The operation to perform when this action is triggered. */
1875         public void actionPerformed(ActionEvent e) {
1876             JTextComponent target = getTextComponent(e);
1877             if (target != null) {
1878                 int offs = target.getCaretPosition();
1879                 boolean failed = false;
1880                 int oldOffs = offs;
1881                 Element curPara =
1882                         Utilities.getParagraphElement(target, offs);
1883                 try {
1884                     offs = Utilities.getNextWord(target, offs);
1885                     if(offs >= curPara.getEndOffset() &&
1886                             oldOffs != curPara.getEndOffset() - 1) {
1887                         // we should first move to the end of current
1888                         // paragraph (bug #4278839)
1889                         offs = curPara.getEndOffset() - 1;
1890                     }
1891                 } catch (BadLocationException bl) {
1892                     int end = target.getDocument().getLength();
1893                     if (offs != end) {
1894                         if(oldOffs != curPara.getEndOffset() - 1) {
1895                             offs = curPara.getEndOffset() - 1;
1896                         } else {
1897                         offs = end;
1898                     }
1899                     }
1900                     else {
1901                         failed = true;
1902                     }
1903                 }
1904                 if (!failed) {
1905                     if (select) {
1906                         target.moveCaretPosition(offs);
1907                     } else {
1908                         target.setCaretPosition(offs);
1909                     }
1910                 }
1911                 else {
1912                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1913                 }
1914             }
1915         }
1916 
1917         private boolean select;
1918     }
1919 
1920     /*
1921      * Position the caret to the beginning of the line.
1922      * @see DefaultEditorKit#beginLineAction
1923      * @see DefaultEditorKit#selectBeginLineAction
1924      * @see DefaultEditorKit#getActions
1925      */
1926     static class BeginLineAction extends TextAction {
1927 
1928         /**
1929          * Create this action with the appropriate identifier.
1930          * @param nm  the name of the action, Action.NAME.
1931          * @param select whether to extend the selection when
1932          *  changing the caret position.
1933          */
1934         BeginLineAction(String nm, boolean select) {
1935             super(nm);
1936             this.select = select;
1937         }
1938 
1939         /** The operation to perform when this action is triggered. */
1940         public void actionPerformed(ActionEvent e) {
1941             JTextComponent target = getTextComponent(e);
1942             if (target != null) {
1943                 try {
1944                     int offs = target.getCaretPosition();
1945                     int begOffs = Utilities.getRowStart(target, offs);
1946                     if (select) {
1947                         target.moveCaretPosition(begOffs);
1948                     } else {
1949                         target.setCaretPosition(begOffs);
1950                     }
1951                 } catch (BadLocationException bl) {
1952                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1953                 }
1954             }
1955         }
1956 
1957         private boolean select;
1958     }
1959 
1960     /*
1961      * Position the caret to the end of the line.
1962      * @see DefaultEditorKit#endLineAction
1963      * @see DefaultEditorKit#selectEndLineAction
1964      * @see DefaultEditorKit#getActions
1965      */
1966     static class EndLineAction extends TextAction {
1967 
1968         /**
1969          * Create this action with the appropriate identifier.
1970          * @param nm  the name of the action, Action.NAME.
1971          * @param select whether to extend the selection when
1972          *  changing the caret position.
1973          */
1974         EndLineAction(String nm, boolean select) {
1975             super(nm);
1976             this.select = select;
1977         }
1978 
1979         /** The operation to perform when this action is triggered. */
1980         public void actionPerformed(ActionEvent e) {
1981             JTextComponent target = getTextComponent(e);
1982             if (target != null) {
1983                 try {
1984                     int offs = target.getCaretPosition();
1985                     int endOffs = Utilities.getRowEnd(target, offs);
1986                     if (select) {
1987                         target.moveCaretPosition(endOffs);
1988                     } else {
1989                         target.setCaretPosition(endOffs);
1990                     }
1991                 } catch (BadLocationException bl) {
1992                     UIManager.getLookAndFeel().provideErrorFeedback(target);
1993                 }
1994             }
1995         }
1996 
1997         private boolean select;
1998     }
1999 
2000     /*
2001      * Position the caret to the beginning of the paragraph.
2002      * @see DefaultEditorKit#beginParagraphAction
2003      * @see DefaultEditorKit#selectBeginParagraphAction
2004      * @see DefaultEditorKit#getActions
2005      */
2006     static class BeginParagraphAction extends TextAction {
2007 
2008         /**
2009          * Create this action with the appropriate identifier.
2010          * @param nm  the name of the action, Action.NAME.
2011          * @param select whether to extend the selection when
2012          *  changing the caret position.
2013          */
2014         BeginParagraphAction(String nm, boolean select) {
2015             super(nm);
2016             this.select = select;
2017         }
2018 
2019         /** The operation to perform when this action is triggered. */
2020         public void actionPerformed(ActionEvent e) {
2021             JTextComponent target = getTextComponent(e);
2022             if (target != null) {
2023                 int offs = target.getCaretPosition();
2024                 Element elem = Utilities.getParagraphElement(target, offs);
2025                 offs = elem.getStartOffset();
2026                 if (select) {
2027                     target.moveCaretPosition(offs);
2028                 } else {
2029                     target.setCaretPosition(offs);
2030                 }
2031             }
2032         }
2033 
2034         private boolean select;
2035     }
2036 
2037     /*
2038      * Position the caret to the end of the paragraph.
2039      * @see DefaultEditorKit#endParagraphAction
2040      * @see DefaultEditorKit#selectEndParagraphAction
2041      * @see DefaultEditorKit#getActions
2042      */
2043     static class EndParagraphAction extends TextAction {
2044 
2045         /**
2046          * Create this action with the appropriate identifier.
2047          * @param nm  the name of the action, Action.NAME.
2048          * @param select whether to extend the selection when
2049          *  changing the caret position.
2050          */
2051         EndParagraphAction(String nm, boolean select) {
2052             super(nm);
2053             this.select = select;
2054         }
2055 
2056         /** The operation to perform when this action is triggered. */
2057         public void actionPerformed(ActionEvent e) {
2058             JTextComponent target = getTextComponent(e);
2059             if (target != null) {
2060                 int offs = target.getCaretPosition();
2061                 Element elem = Utilities.getParagraphElement(target, offs);
2062                 offs = Math.min(target.getDocument().getLength(),
2063                                 elem.getEndOffset());
2064                 if (select) {
2065                     target.moveCaretPosition(offs);
2066                 } else {
2067                     target.setCaretPosition(offs);
2068                 }
2069             }
2070         }
2071 
2072         private boolean select;
2073     }
2074 
2075     /*
2076      * Move the caret to the beginning of the document.
2077      * @see DefaultEditorKit#beginAction
2078      * @see DefaultEditorKit#getActions
2079      */
2080     static class BeginAction extends TextAction {
2081 
2082         /* Create this object with the appropriate identifier. */
2083         BeginAction(String nm, boolean select) {
2084             super(nm);
2085             this.select = select;
2086         }
2087 
2088         /** The operation to perform when this action is triggered. */
2089         public void actionPerformed(ActionEvent e) {
2090             JTextComponent target = getTextComponent(e);
2091             if (target != null) {
2092                 if (select) {
2093                     target.moveCaretPosition(0);
2094                 } else {
2095                     target.setCaretPosition(0);
2096                 }
2097             }
2098         }
2099 
2100         private boolean select;
2101     }
2102 
2103     /*
2104      * Move the caret to the end of the document.
2105      * @see DefaultEditorKit#endAction
2106      * @see DefaultEditorKit#getActions
2107      */
2108     static class EndAction extends TextAction {
2109 
2110         /* Create this object with the appropriate identifier. */
2111         EndAction(String nm, boolean select) {
2112             super(nm);
2113             this.select = select;
2114         }
2115 
2116         /** The operation to perform when this action is triggered. */
2117         public void actionPerformed(ActionEvent e) {
2118             JTextComponent target = getTextComponent(e);
2119             if (target != null) {
2120                 Document doc = target.getDocument();
2121                 int dot = doc.getLength();
2122                 if (select) {
2123                     target.moveCaretPosition(dot);
2124                 } else {
2125                     target.setCaretPosition(dot);
2126                 }
2127             }
2128         }
2129 
2130         private boolean select;
2131     }
2132 
2133     /*
2134      * Select the word around the caret
2135      * @see DefaultEditorKit#endAction
2136      * @see DefaultEditorKit#getActions
2137      */
2138     static class SelectWordAction extends TextAction {
2139 
2140         /**
2141          * Create this action with the appropriate identifier.
2142          * @param nm  the name of the action, Action.NAME.
2143          * @param select whether to extend the selection when
2144          *  changing the caret position.
2145          */
2146         SelectWordAction() {
2147             super(selectWordAction);
2148             start = new BeginWordAction("pigdog", false);
2149             end = new EndWordAction("pigdog", true);
2150         }
2151 
2152         /** The operation to perform when this action is triggered. */
2153         public void actionPerformed(ActionEvent e) {
2154             start.actionPerformed(e);
2155             end.actionPerformed(e);
2156         }
2157 
2158         private Action start;
2159         private Action end;
2160     }
2161 
2162     /*
2163      * Select the line around the caret
2164      * @see DefaultEditorKit#endAction
2165      * @see DefaultEditorKit#getActions
2166      */
2167     static class SelectLineAction extends TextAction {
2168 
2169         /**
2170          * Create this action with the appropriate identifier.
2171          * @param nm  the name of the action, Action.NAME.
2172          * @param select whether to extend the selection when
2173          *  changing the caret position.
2174          */
2175         SelectLineAction() {
2176             super(selectLineAction);
2177             start = new BeginLineAction("pigdog", false);
2178             end = new EndLineAction("pigdog", true);
2179         }
2180 
2181         /** The operation to perform when this action is triggered. */
2182         public void actionPerformed(ActionEvent e) {
2183             start.actionPerformed(e);
2184             end.actionPerformed(e);
2185         }
2186 
2187         private Action start;
2188         private Action end;
2189     }
2190 
2191     /*
2192      * Select the paragraph around the caret
2193      * @see DefaultEditorKit#endAction
2194      * @see DefaultEditorKit#getActions
2195      */
2196     static class SelectParagraphAction extends TextAction {
2197 
2198         /**
2199          * Create this action with the appropriate identifier.
2200          * @param nm  the name of the action, Action.NAME.
2201          * @param select whether to extend the selection when
2202          *  changing the caret position.
2203          */
2204         SelectParagraphAction() {
2205             super(selectParagraphAction);
2206             start = new BeginParagraphAction("pigdog", false);
2207             end = new EndParagraphAction("pigdog", true);
2208         }
2209 
2210         /** The operation to perform when this action is triggered. */
2211         public void actionPerformed(ActionEvent e) {
2212             start.actionPerformed(e);
2213             end.actionPerformed(e);
2214         }
2215 
2216         private Action start;
2217         private Action end;
2218     }
2219 
2220     /*
2221      * Select the entire document
2222      * @see DefaultEditorKit#endAction
2223      * @see DefaultEditorKit#getActions
2224      */
2225     static class SelectAllAction extends TextAction {
2226 
2227         /**
2228          * Create this action with the appropriate identifier.
2229          * @param nm  the name of the action, Action.NAME.
2230          * @param select whether to extend the selection when
2231          *  changing the caret position.
2232          */
2233         SelectAllAction() {
2234             super(selectAllAction);
2235         }
2236 
2237         /** The operation to perform when this action is triggered. */
2238         public void actionPerformed(ActionEvent e) {
2239             JTextComponent target = getTextComponent(e);
2240             if (target != null) {
2241                 Document doc = target.getDocument();
2242                 target.setCaretPosition(0);
2243                 target.moveCaretPosition(doc.getLength());
2244             }
2245         }
2246 
2247     }
2248 
2249     /*
2250      * Remove the selection, if any.
2251      * @see DefaultEditorKit#unselectAction
2252      * @see DefaultEditorKit#getActions
2253      */
2254     static class UnselectAction extends TextAction {
2255 
2256         /**
2257          * Create this action with the appropriate identifier.
2258          */
2259         UnselectAction() {
2260             super(unselectAction);
2261         }
2262 
2263         /** The operation to perform when this action is triggered. */
2264         public void actionPerformed(ActionEvent e) {
2265             JTextComponent target = getTextComponent(e);
2266             if (target != null) {
2267                 target.setCaretPosition(target.getCaretPosition());
2268             }
2269         }
2270 
2271     }
2272 
2273     /*
2274      * Toggles the ComponentOrientation of the text component.
2275      * @see DefaultEditorKit#toggleComponentOrientationAction
2276      * @see DefaultEditorKit#getActions
2277      */
2278     static class ToggleComponentOrientationAction extends TextAction {
2279 
2280         /**
2281          * Create this action with the appropriate identifier.
2282          */
2283         ToggleComponentOrientationAction() {
2284             super(toggleComponentOrientationAction);
2285         }
2286 
2287         /** The operation to perform when this action is triggered. */
2288         public void actionPerformed(ActionEvent e) {
2289             JTextComponent target = getTextComponent(e);
2290             if (target != null) {
2291                 ComponentOrientation last = target.getComponentOrientation();
2292                 ComponentOrientation next;
2293                 if( last == ComponentOrientation.RIGHT_TO_LEFT )
2294                     next = ComponentOrientation.LEFT_TO_RIGHT;
2295                 else
2296                     next = ComponentOrientation.RIGHT_TO_LEFT;
2297                 target.setComponentOrientation(next);
2298                 target.repaint();
2299             }
2300         }
2301     }
2302 
2303 }