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