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 java.io.*;
  28 import java.awt.*;
  29 import java.awt.event.ActionEvent;
  30 import java.beans.PropertyChangeEvent;
  31 import java.beans.PropertyChangeListener;
  32 import javax.swing.event.*;
  33 import javax.swing.Action;
  34 import javax.swing.JEditorPane;
  35 import javax.swing.KeyStroke;
  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 styled text and
  43  * provides a minimal set of actions for editing styled text.
  44  *
  45  * @author  Timothy Prinzing
  46  */
  47 public class StyledEditorKit extends DefaultEditorKit {
  48 
  49     /**
  50      * Creates a new EditorKit used for styled documents.
  51      */
  52     public StyledEditorKit() {
  53         createInputAttributeUpdated();
  54         createInputAttributes();
  55     }
  56 
  57     /**
  58      * Gets the input attributes for the pane.  When
  59      * the caret moves and there is no selection, the
  60      * input attributes are automatically mutated to
  61      * reflect the character attributes of the current
  62      * caret location.  The styled editing actions
  63      * use the input attributes to carry out their
  64      * actions.
  65      *
  66      * @return the attribute set
  67      */
  68     public MutableAttributeSet getInputAttributes() {
  69         return inputAttributes;
  70     }
  71 
  72     /**
  73      * Fetches the element representing the current
  74      * run of character attributes for the caret.
  75      *
  76      * @return the element
  77      */
  78     public Element getCharacterAttributeRun() {
  79         return currentRun;
  80     }
  81 
  82     // --- EditorKit methods ---------------------------
  83 
  84     /**
  85      * Fetches the command list for the editor.  This is
  86      * the list of commands supported by the superclass
  87      * augmented by the collection of commands defined
  88      * locally for style operations.
  89      *
  90      * @return the command list
  91      */
  92     public Action[] getActions() {
  93         return TextAction.augmentList(super.getActions(), defaultActions);
  94     }
  95 
  96     /**
  97      * Creates an uninitialized text storage model
  98      * that is appropriate for this type of editor.
  99      *
 100      * @return the model
 101      */
 102     public Document createDefaultDocument() {
 103         return new DefaultStyledDocument();
 104     }
 105 
 106     /**
 107      * Called when the kit is being installed into
 108      * a JEditorPane.
 109      *
 110      * @param c the JEditorPane
 111      */
 112     public void install(JEditorPane c) {
 113         c.addCaretListener(inputAttributeUpdater);
 114         c.addPropertyChangeListener(inputAttributeUpdater);
 115         Caret caret = c.getCaret();
 116         if (caret != null) {
 117             inputAttributeUpdater.updateInputAttributes
 118                                   (caret.getDot(), caret.getMark(), c);
 119         }
 120     }
 121 
 122     /**
 123      * Called when the kit is being removed from the
 124      * JEditorPane.  This is used to unregister any
 125      * listeners that were attached.
 126      *
 127      * @param c the JEditorPane
 128      */
 129     public void deinstall(JEditorPane c) {
 130         c.removeCaretListener(inputAttributeUpdater);
 131         c.removePropertyChangeListener(inputAttributeUpdater);
 132 
 133         // remove references to current document so it can be collected.
 134         currentRun = null;
 135         currentParagraph = null;
 136     }
 137 
 138    /**
 139      * Fetches a factory that is suitable for producing
 140      * views of any models that are produced by this
 141      * kit.  This is implemented to return View implementations
 142      * for the following kinds of elements:
 143      * <ul>
 144      * <li>AbstractDocument.ContentElementName
 145      * <li>AbstractDocument.ParagraphElementName
 146      * <li>AbstractDocument.SectionElementName
 147      * <li>StyleConstants.ComponentElementName
 148      * <li>StyleConstants.IconElementName
 149      * </ul>
 150      *
 151      * @return the factory
 152      */
 153     public ViewFactory getViewFactory() {
 154         return defaultFactory;
 155     }
 156 
 157     /**
 158      * Creates a copy of the editor kit.
 159      *
 160      * @return the copy
 161      */
 162     public Object clone() {
 163         StyledEditorKit o = (StyledEditorKit)super.clone();
 164         o.currentRun = o.currentParagraph = null;
 165         o.createInputAttributeUpdated();
 166         o.createInputAttributes();
 167         return o;
 168     }
 169 
 170     /**
 171      * Creates the AttributeSet used for the selection.
 172      */
 173     private void createInputAttributes() {
 174         inputAttributes = new SimpleAttributeSet() {
 175             public AttributeSet getResolveParent() {
 176                 return (currentParagraph != null) ?
 177                            currentParagraph.getAttributes() : null;
 178             }
 179 
 180             public Object clone() {
 181                 return new SimpleAttributeSet(this);
 182             }
 183         };
 184     }
 185 
 186     /**
 187      * Creates a new <code>AttributeTracker</code>.
 188      */
 189     private void createInputAttributeUpdated() {
 190         inputAttributeUpdater = new AttributeTracker();
 191     }
 192 
 193 
 194     private static final ViewFactory defaultFactory = new StyledViewFactory();
 195 
 196     Element currentRun;
 197     Element currentParagraph;
 198 
 199     /**
 200      * This is the set of attributes used to store the
 201      * input attributes.
 202      */
 203     MutableAttributeSet inputAttributes;
 204 
 205     /**
 206      * This listener will be attached to the caret of
 207      * the text component that the EditorKit gets installed
 208      * into.  This should keep the input attributes updated
 209      * for use by the styled actions.
 210      */
 211     private AttributeTracker inputAttributeUpdater;
 212 
 213     /**
 214      * Tracks caret movement and keeps the input attributes set
 215      * to reflect the current set of attribute definitions at the
 216      * caret position.
 217      * <p>This implements PropertyChangeListener to update the
 218      * input attributes when the Document changes, as if the Document
 219      * changes the attributes will almost certainly change.
 220      */
 221     class AttributeTracker implements CaretListener, PropertyChangeListener, Serializable {
 222 
 223         /**
 224          * Updates the attributes. <code>dot</code> and <code>mark</code>
 225          * mark give the positions of the selection in <code>c</code>.
 226          */
 227         void updateInputAttributes(int dot, int mark, JTextComponent c) {
 228             // EditorKit might not have installed the StyledDocument yet.
 229             Document aDoc = c.getDocument();
 230             if (!(aDoc instanceof StyledDocument)) {
 231                 return ;
 232             }
 233             int start = Math.min(dot, mark);
 234             // record current character attributes.
 235             StyledDocument doc = (StyledDocument)aDoc;
 236             // If nothing is selected, get the attributes from the character
 237             // before the start of the selection, otherwise get the attributes
 238             // from the character element at the start of the selection.
 239             Element run;
 240             currentParagraph = doc.getParagraphElement(start);
 241             if (currentParagraph.getStartOffset() == start || dot != mark) {
 242                 // Get the attributes from the character at the selection
 243                 // if in a different paragrah!
 244                 run = doc.getCharacterElement(start);
 245             }
 246             else {
 247                 run = doc.getCharacterElement(Math.max(start-1, 0));
 248             }
 249             if (run != currentRun) {
 250                     /*
 251                      * PENDING(prinz) All attributes that represent a single
 252                      * glyph position and can't be inserted into should be
 253                      * removed from the input attributes... this requires
 254                      * mixing in an interface to indicate that condition.
 255                      * When we can add things again this logic needs to be
 256                      * improved!!
 257                      */
 258                 currentRun = run;
 259                 createInputAttributes(currentRun, getInputAttributes());
 260             }
 261         }
 262 
 263         public void propertyChange(PropertyChangeEvent evt) {
 264             Object newValue = evt.getNewValue();
 265             Object source = evt.getSource();
 266 
 267             if ((source instanceof JTextComponent) &&
 268                 (newValue instanceof Document)) {
 269                 // New document will have changed selection to 0,0.
 270                 updateInputAttributes(0, 0, (JTextComponent)source);
 271             }
 272         }
 273 
 274         public void caretUpdate(CaretEvent e) {
 275             updateInputAttributes(e.getDot(), e.getMark(),
 276                                   (JTextComponent)e.getSource());
 277         }
 278     }
 279 
 280     /**
 281      * Copies the key/values in <code>element</code>s AttributeSet into
 282      * <code>set</code>. This does not copy component, icon, or element
 283      * names attributes. Subclasses may wish to refine what is and what
 284      * isn't copied here. But be sure to first remove all the attributes that
 285      * are in <code>set</code>.<p>
 286      * This is called anytime the caret moves over a different location.
 287      *
 288      */
 289     protected void createInputAttributes(Element element,
 290                                          MutableAttributeSet set) {
 291         if (element.getAttributes().getAttributeCount() > 0
 292             || element.getEndOffset() - element.getStartOffset() > 1
 293             || element.getEndOffset() < element.getDocument().getLength()) {
 294             set.removeAttributes(set);
 295             set.addAttributes(element.getAttributes());
 296             set.removeAttribute(StyleConstants.ComponentAttribute);
 297             set.removeAttribute(StyleConstants.IconAttribute);
 298             set.removeAttribute(AbstractDocument.ElementNameAttribute);
 299             set.removeAttribute(StyleConstants.ComposedTextAttribute);
 300         }
 301     }
 302 
 303     // ---- default ViewFactory implementation ---------------------
 304 
 305     static class StyledViewFactory implements ViewFactory {
 306 
 307         public View create(Element elem) {
 308             String kind = elem.getName();
 309             if (kind != null) {
 310                 if (kind.equals(AbstractDocument.ContentElementName)) {
 311                     return new LabelView(elem);
 312                 } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
 313                     return new ParagraphView(elem);
 314                 } else if (kind.equals(AbstractDocument.SectionElementName)) {
 315                     return new BoxView(elem, View.Y_AXIS);
 316                 } else if (kind.equals(StyleConstants.ComponentElementName)) {
 317                     return new ComponentView(elem);
 318                 } else if (kind.equals(StyleConstants.IconElementName)) {
 319                     return new IconView(elem);
 320                 }
 321             }
 322 
 323             // default to text display
 324             return new LabelView(elem);
 325         }
 326 
 327     }
 328 
 329     // --- Action implementations ---------------------------------
 330 
 331     private static final Action[] defaultActions = {
 332         new FontFamilyAction("font-family-SansSerif", "SansSerif"),
 333         new FontFamilyAction("font-family-Monospaced", "Monospaced"),
 334         new FontFamilyAction("font-family-Serif", "Serif"),
 335         new FontSizeAction("font-size-8", 8),
 336         new FontSizeAction("font-size-10", 10),
 337         new FontSizeAction("font-size-12", 12),
 338         new FontSizeAction("font-size-14", 14),
 339         new FontSizeAction("font-size-16", 16),
 340         new FontSizeAction("font-size-18", 18),
 341         new FontSizeAction("font-size-24", 24),
 342         new FontSizeAction("font-size-36", 36),
 343         new FontSizeAction("font-size-48", 48),
 344         new AlignmentAction("left-justify", StyleConstants.ALIGN_LEFT),
 345         new AlignmentAction("center-justify", StyleConstants.ALIGN_CENTER),
 346         new AlignmentAction("right-justify", StyleConstants.ALIGN_RIGHT),
 347         new BoldAction(),
 348         new ItalicAction(),
 349         new StyledInsertBreakAction(),
 350         new UnderlineAction()
 351     };
 352 
 353     /**
 354      * An action that assumes it's being fired on a JEditorPane
 355      * with a StyledEditorKit (or subclass) installed.  This has
 356      * some convenience methods for causing character or paragraph
 357      * level attribute changes.  The convenience methods will
 358      * throw an IllegalArgumentException if the assumption of
 359      * a StyledDocument, a JEditorPane, or a StyledEditorKit
 360      * fail to be true.
 361      * <p>
 362      * The component that gets acted upon by the action
 363      * will be the source of the ActionEvent if the source
 364      * can be narrowed to a JEditorPane type.  If the source
 365      * can't be narrowed, the most recently focused text
 366      * component is changed.  If neither of these are the
 367      * case, the action cannot be performed.
 368      * <p>
 369      * <strong>Warning:</strong>
 370      * Serialized objects of this class will not be compatible with
 371      * future Swing releases. The current serialization support is
 372      * appropriate for short term storage or RMI between applications running
 373      * the same version of Swing.  As of 1.4, support for long term storage
 374      * of all JavaBeans&trade;
 375      * has been added to the <code>java.beans</code> package.
 376      * Please see {@link java.beans.XMLEncoder}.
 377      */
 378     @SuppressWarnings("serial") // Same-version serialization only
 379     public abstract static class StyledTextAction extends TextAction {
 380 
 381         /**
 382          * Creates a new StyledTextAction from a string action name.
 383          *
 384          * @param nm the name of the action
 385          */
 386         public StyledTextAction(String nm) {
 387             super(nm);
 388         }
 389 
 390         /**
 391          * Gets the target editor for an action.
 392          *
 393          * @param e the action event
 394          * @return the editor
 395          */
 396         protected final JEditorPane getEditor(ActionEvent e) {
 397             JTextComponent tcomp = getTextComponent(e);
 398             if (tcomp instanceof JEditorPane) {
 399                 return (JEditorPane) tcomp;
 400             }
 401             return null;
 402         }
 403 
 404         /**
 405          * Gets the document associated with an editor pane.
 406          *
 407          * @param e the editor
 408          * @return the document
 409          * @exception IllegalArgumentException for the wrong document type
 410          */
 411         protected final StyledDocument getStyledDocument(JEditorPane e) {
 412             Document d = e.getDocument();
 413             if (d instanceof StyledDocument) {
 414                 return (StyledDocument) d;
 415             }
 416             throw new IllegalArgumentException("document must be StyledDocument");
 417         }
 418 
 419         /**
 420          * Gets the editor kit associated with an editor pane.
 421          *
 422          * @param e the editor pane
 423          * @return the kit
 424          * @exception IllegalArgumentException for the wrong document type
 425          */
 426         protected final StyledEditorKit getStyledEditorKit(JEditorPane e) {
 427             EditorKit k = e.getEditorKit();
 428             if (k instanceof StyledEditorKit) {
 429                 return (StyledEditorKit) k;
 430             }
 431             throw new IllegalArgumentException("EditorKit must be StyledEditorKit");
 432         }
 433 
 434         /**
 435          * Applies the given attributes to character
 436          * content.  If there is a selection, the attributes
 437          * are applied to the selection range.  If there
 438          * is no selection, the attributes are applied to
 439          * the input attribute set which defines the attributes
 440          * for any new text that gets inserted.
 441          *
 442          * @param editor the editor
 443          * @param attr the attributes
 444          * @param replace   if true, then replace the existing attributes first
 445          */
 446         protected final void setCharacterAttributes(JEditorPane editor,
 447                                               AttributeSet attr, boolean replace) {
 448             int p0 = editor.getSelectionStart();
 449             int p1 = editor.getSelectionEnd();
 450             if (p0 != p1) {
 451                 StyledDocument doc = getStyledDocument(editor);
 452                 doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
 453             }
 454             StyledEditorKit k = getStyledEditorKit(editor);
 455             MutableAttributeSet inputAttributes = k.getInputAttributes();
 456             if (replace) {
 457                 inputAttributes.removeAttributes(inputAttributes);
 458             }
 459             inputAttributes.addAttributes(attr);
 460         }
 461 
 462         /**
 463          * Applies the given attributes to paragraphs.  If
 464          * there is a selection, the attributes are applied
 465          * to the paragraphs that intersect the selection.
 466          * if there is no selection, the attributes are applied
 467          * to the paragraph at the current caret position.
 468          *
 469          * @param editor the editor
 470          * @param attr the attributes
 471          * @param replace   if true, replace the existing attributes first
 472          */
 473         protected final void setParagraphAttributes(JEditorPane editor,
 474                                            AttributeSet attr, boolean replace) {
 475             int p0 = editor.getSelectionStart();
 476             int p1 = editor.getSelectionEnd();
 477             StyledDocument doc = getStyledDocument(editor);
 478             doc.setParagraphAttributes(p0, p1 - p0, attr, replace);
 479         }
 480 
 481     }
 482 
 483     /**
 484      * An action to set the font family in the associated
 485      * JEditorPane.  This will use the family specified as
 486      * the command string on the ActionEvent if there is one,
 487      * otherwise the family that was initialized with will be used.
 488      * <p>
 489      * <strong>Warning:</strong>
 490      * Serialized objects of this class will not be compatible with
 491      * future Swing releases. The current serialization support is
 492      * appropriate for short term storage or RMI between applications running
 493      * the same version of Swing.  As of 1.4, support for long term storage
 494      * of all JavaBeans&trade;
 495      * has been added to the <code>java.beans</code> package.
 496      * Please see {@link java.beans.XMLEncoder}.
 497      */
 498     @SuppressWarnings("serial") // Same-version serialization only
 499     public static class FontFamilyAction extends StyledTextAction {
 500 
 501         /**
 502          * Creates a new FontFamilyAction.
 503          *
 504          * @param nm the action name
 505          * @param family the font family
 506          */
 507         public FontFamilyAction(String nm, String family) {
 508             super(nm);
 509             this.family = family;
 510         }
 511 
 512         /**
 513          * Sets the font family.
 514          *
 515          * @param e the event
 516          */
 517         public void actionPerformed(ActionEvent e) {
 518             JEditorPane editor = getEditor(e);
 519             if (editor != null) {
 520                 String family = this.family;
 521                 if ((e != null) && (e.getSource() == editor)) {
 522                     String s = e.getActionCommand();
 523                     if (s != null) {
 524                         family = s;
 525                     }
 526                 }
 527                 if (family != null) {
 528                     MutableAttributeSet attr = new SimpleAttributeSet();
 529                     StyleConstants.setFontFamily(attr, family);
 530                     setCharacterAttributes(editor, attr, false);
 531                 } else {
 532                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 533                 }
 534             }
 535         }
 536 
 537         private String family;
 538     }
 539 
 540     /**
 541      * An action to set the font size in the associated
 542      * JEditorPane.  This will use the size specified as
 543      * the command string on the ActionEvent if there is one,
 544      * otherwise the size that was initialized with will be used.
 545      * <p>
 546      * <strong>Warning:</strong>
 547      * Serialized objects of this class will not be compatible with
 548      * future Swing releases. The current serialization support is
 549      * appropriate for short term storage or RMI between applications running
 550      * the same version of Swing.  As of 1.4, support for long term storage
 551      * of all JavaBeans&trade;
 552      * has been added to the <code>java.beans</code> package.
 553      * Please see {@link java.beans.XMLEncoder}.
 554      */
 555     @SuppressWarnings("serial") // Same-version serialization only
 556     public static class FontSizeAction extends StyledTextAction {
 557 
 558         /**
 559          * Creates a new FontSizeAction.
 560          *
 561          * @param nm the action name
 562          * @param size the font size
 563          */
 564         public FontSizeAction(String nm, int size) {
 565             super(nm);
 566             this.size = size;
 567         }
 568 
 569         /**
 570          * Sets the font size.
 571          *
 572          * @param e the action event
 573          */
 574         public void actionPerformed(ActionEvent e) {
 575             JEditorPane editor = getEditor(e);
 576             if (editor != null) {
 577                 int size = this.size;
 578                 if ((e != null) && (e.getSource() == editor)) {
 579                     String s = e.getActionCommand();
 580                     try {
 581                         size = Integer.parseInt(s, 10);
 582                     } catch (NumberFormatException nfe) {
 583                     }
 584                 }
 585                 if (size != 0) {
 586                     MutableAttributeSet attr = new SimpleAttributeSet();
 587                     StyleConstants.setFontSize(attr, size);
 588                     setCharacterAttributes(editor, attr, false);
 589                 } else {
 590                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 591                 }
 592             }
 593         }
 594 
 595         private int size;
 596     }
 597 
 598     /**
 599      * An action to set foreground color.  This sets the
 600      * <code>StyleConstants.Foreground</code> attribute for the
 601      * currently selected range of the target JEditorPane.
 602      * This is done by calling
 603      * <code>StyledDocument.setCharacterAttributes</code>
 604      * on the styled document associated with the target
 605      * JEditorPane.
 606      * <p>
 607      * If the target text component is specified as the
 608      * source of the ActionEvent and there is a command string,
 609      * the command string will be interpreted as the foreground
 610      * color.  It will be interpreted by called
 611      * <code>Color.decode</code>, and should therefore be
 612      * legal input for that method.
 613      * <p>
 614      * <strong>Warning:</strong>
 615      * Serialized objects of this class will not be compatible with
 616      * future Swing releases. The current serialization support is
 617      * appropriate for short term storage or RMI between applications running
 618      * the same version of Swing.  As of 1.4, support for long term storage
 619      * of all JavaBeans&trade;
 620      * has been added to the <code>java.beans</code> package.
 621      * Please see {@link java.beans.XMLEncoder}.
 622      */
 623     @SuppressWarnings("serial") // Same-version serialization only
 624     public static class ForegroundAction extends StyledTextAction {
 625 
 626         /**
 627          * Creates a new ForegroundAction.
 628          *
 629          * @param nm the action name
 630          * @param fg the foreground color
 631          */
 632         public ForegroundAction(String nm, Color fg) {
 633             super(nm);
 634             this.fg = fg;
 635         }
 636 
 637         /**
 638          * Sets the foreground color.
 639          *
 640          * @param e the action event
 641          */
 642         public void actionPerformed(ActionEvent e) {
 643             JEditorPane editor = getEditor(e);
 644             if (editor != null) {
 645                 Color fg = this.fg;
 646                 if ((e != null) && (e.getSource() == editor)) {
 647                     String s = e.getActionCommand();
 648                     try {
 649                         fg = Color.decode(s);
 650                     } catch (NumberFormatException nfe) {
 651                     }
 652                 }
 653                 if (fg != null) {
 654                     MutableAttributeSet attr = new SimpleAttributeSet();
 655                     StyleConstants.setForeground(attr, fg);
 656                     setCharacterAttributes(editor, attr, false);
 657                 } else {
 658                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 659                 }
 660             }
 661         }
 662 
 663         private Color fg;
 664     }
 665 
 666     /**
 667      * An action to set paragraph alignment.  This sets the
 668      * <code>StyleConstants.Alignment</code> attribute for the
 669      * currently selected range of the target JEditorPane.
 670      * This is done by calling
 671      * <code>StyledDocument.setParagraphAttributes</code>
 672      * on the styled document associated with the target
 673      * JEditorPane.
 674      * <p>
 675      * If the target text component is specified as the
 676      * source of the ActionEvent and there is a command string,
 677      * the command string will be interpreted as an integer
 678      * that should be one of the legal values for the
 679      * <code>StyleConstants.Alignment</code> attribute.
 680      * <p>
 681      * <strong>Warning:</strong>
 682      * Serialized objects of this class will not be compatible with
 683      * future Swing releases. The current serialization support is
 684      * appropriate for short term storage or RMI between applications running
 685      * the same version of Swing.  As of 1.4, support for long term storage
 686      * of all JavaBeans&trade;
 687      * has been added to the <code>java.beans</code> package.
 688      * Please see {@link java.beans.XMLEncoder}.
 689      */
 690     @SuppressWarnings("serial") // Same-version serialization only
 691     public static class AlignmentAction extends StyledTextAction {
 692 
 693         /**
 694          * Creates a new AlignmentAction.
 695          *
 696          * @param nm the action name
 697          * @param a the alignment &gt;= 0
 698          */
 699         public AlignmentAction(String nm, int a) {
 700             super(nm);
 701             this.a = a;
 702         }
 703 
 704         /**
 705          * Sets the alignment.
 706          *
 707          * @param e the action event
 708          */
 709         public void actionPerformed(ActionEvent e) {
 710             JEditorPane editor = getEditor(e);
 711             if (editor != null) {
 712                 int a = this.a;
 713                 if ((e != null) && (e.getSource() == editor)) {
 714                     String s = e.getActionCommand();
 715                     try {
 716                         a = Integer.parseInt(s, 10);
 717                     } catch (NumberFormatException nfe) {
 718                     }
 719                 }
 720                 MutableAttributeSet attr = new SimpleAttributeSet();
 721                 StyleConstants.setAlignment(attr, a);
 722                 setParagraphAttributes(editor, attr, false);
 723             }
 724         }
 725 
 726         private int a;
 727     }
 728 
 729     /**
 730      * An action to toggle the bold attribute.
 731      * <p>
 732      * <strong>Warning:</strong>
 733      * Serialized objects of this class will not be compatible with
 734      * future Swing releases. The current serialization support is
 735      * appropriate for short term storage or RMI between applications running
 736      * the same version of Swing.  As of 1.4, support for long term storage
 737      * of all JavaBeans&trade;
 738      * has been added to the <code>java.beans</code> package.
 739      * Please see {@link java.beans.XMLEncoder}.
 740      */
 741     @SuppressWarnings("serial") // Same-version serialization only
 742     public static class BoldAction extends StyledTextAction {
 743 
 744         /**
 745          * Constructs a new BoldAction.
 746          */
 747         public BoldAction() {
 748             super("font-bold");
 749         }
 750 
 751         /**
 752          * Toggles the bold attribute.
 753          *
 754          * @param e the action event
 755          */
 756         public void actionPerformed(ActionEvent e) {
 757             JEditorPane editor = getEditor(e);
 758             if (editor != null) {
 759                 StyledEditorKit kit = getStyledEditorKit(editor);
 760                 MutableAttributeSet attr = kit.getInputAttributes();
 761                 boolean bold = (StyleConstants.isBold(attr)) ? false : true;
 762                 SimpleAttributeSet sas = new SimpleAttributeSet();
 763                 StyleConstants.setBold(sas, bold);
 764                 setCharacterAttributes(editor, sas, false);
 765             }
 766         }
 767     }
 768 
 769     /**
 770      * An action to toggle the italic attribute.
 771      * <p>
 772      * <strong>Warning:</strong>
 773      * Serialized objects of this class will not be compatible with
 774      * future Swing releases. The current serialization support is
 775      * appropriate for short term storage or RMI between applications running
 776      * the same version of Swing.  As of 1.4, support for long term storage
 777      * of all JavaBeans&trade;
 778      * has been added to the <code>java.beans</code> package.
 779      * Please see {@link java.beans.XMLEncoder}.
 780      */
 781     @SuppressWarnings("serial") // Same-version serialization only
 782     public static class ItalicAction extends StyledTextAction {
 783 
 784         /**
 785          * Constructs a new ItalicAction.
 786          */
 787         public ItalicAction() {
 788             super("font-italic");
 789         }
 790 
 791         /**
 792          * Toggles the italic attribute.
 793          *
 794          * @param e the action event
 795          */
 796         public void actionPerformed(ActionEvent e) {
 797             JEditorPane editor = getEditor(e);
 798             if (editor != null) {
 799                 StyledEditorKit kit = getStyledEditorKit(editor);
 800                 MutableAttributeSet attr = kit.getInputAttributes();
 801                 boolean italic = (StyleConstants.isItalic(attr)) ? false : true;
 802                 SimpleAttributeSet sas = new SimpleAttributeSet();
 803                 StyleConstants.setItalic(sas, italic);
 804                 setCharacterAttributes(editor, sas, false);
 805             }
 806         }
 807     }
 808 
 809     /**
 810      * An action to toggle the underline attribute.
 811      * <p>
 812      * <strong>Warning:</strong>
 813      * Serialized objects of this class will not be compatible with
 814      * future Swing releases. The current serialization support is
 815      * appropriate for short term storage or RMI between applications running
 816      * the same version of Swing.  As of 1.4, support for long term storage
 817      * of all JavaBeans&trade;
 818      * has been added to the <code>java.beans</code> package.
 819      * Please see {@link java.beans.XMLEncoder}.
 820      */
 821     @SuppressWarnings("serial") // Same-version serialization only
 822     public static class UnderlineAction extends StyledTextAction {
 823 
 824         /**
 825          * Constructs a new UnderlineAction.
 826          */
 827         public UnderlineAction() {
 828             super("font-underline");
 829         }
 830 
 831         /**
 832          * Toggles the Underline attribute.
 833          *
 834          * @param e the action event
 835          */
 836         public void actionPerformed(ActionEvent e) {
 837             JEditorPane editor = getEditor(e);
 838             if (editor != null) {
 839                 StyledEditorKit kit = getStyledEditorKit(editor);
 840                 MutableAttributeSet attr = kit.getInputAttributes();
 841                 boolean underline = (StyleConstants.isUnderline(attr)) ? false : true;
 842                 SimpleAttributeSet sas = new SimpleAttributeSet();
 843                 StyleConstants.setUnderline(sas, underline);
 844                 setCharacterAttributes(editor, sas, false);
 845             }
 846         }
 847     }
 848 
 849 
 850     /**
 851      * StyledInsertBreakAction has similar behavior to that of
 852      * <code>DefaultEditorKit.InsertBreakAction</code>. That is when
 853      * its <code>actionPerformed</code> method is invoked, a newline
 854      * is inserted. Beyond that, this will reset the input attributes to
 855      * what they were before the newline was inserted.
 856      */
 857     static class StyledInsertBreakAction extends StyledTextAction {
 858         private SimpleAttributeSet tempSet;
 859 
 860         StyledInsertBreakAction() {
 861             super(insertBreakAction);
 862         }
 863 
 864         public void actionPerformed(ActionEvent e) {
 865             JEditorPane target = getEditor(e);
 866 
 867             if (target != null) {
 868                 if ((!target.isEditable()) || (!target.isEnabled())) {
 869                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 870                     return;
 871                 }
 872                 StyledEditorKit sek = getStyledEditorKit(target);
 873 
 874                 if (tempSet != null) {
 875                     tempSet.removeAttributes(tempSet);
 876                 }
 877                 else {
 878                     tempSet = new SimpleAttributeSet();
 879                 }
 880                 tempSet.addAttributes(sek.getInputAttributes());
 881                 target.replaceSelection("\n");
 882 
 883                 MutableAttributeSet ia = sek.getInputAttributes();
 884 
 885                 ia.removeAttributes(ia);
 886                 ia.addAttributes(tempSet);
 887                 tempSet.removeAttributes(tempSet);
 888             }
 889             else {
 890                 // See if we are in a JTextComponent.
 891                 JTextComponent text = getTextComponent(e);
 892 
 893                 if (text != null) {
 894                     if ((!text.isEditable()) || (!text.isEnabled())) {
 895                         UIManager.getLookAndFeel().provideErrorFeedback(target);
 896                         return;
 897                     }
 898                     text.replaceSelection("\n");
 899                 }
 900             }
 901         }
 902     }
 903 }