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