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