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