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 @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      */
 292     protected void createInputAttributes(Element element,
 293                                          MutableAttributeSet set) {
 294         if (element.getAttributes().getAttributeCount() > 0
 295             || element.getEndOffset() - element.getStartOffset() > 1
 296             || element.getEndOffset() < element.getDocument().getLength()) {
 297             set.removeAttributes(set);
 298             set.addAttributes(element.getAttributes());
 299             set.removeAttribute(StyleConstants.ComponentAttribute);
 300             set.removeAttribute(StyleConstants.IconAttribute);
 301             set.removeAttribute(AbstractDocument.ElementNameAttribute);
 302             set.removeAttribute(StyleConstants.ComposedTextAttribute);
 303         }
 304     }
 305 
 306     // ---- default ViewFactory implementation ---------------------
 307 
 308     static class StyledViewFactory implements ViewFactory {
 309 
 310         public View create(Element elem) {
 311             String kind = elem.getName();
 312             if (kind != null) {
 313                 if (kind.equals(AbstractDocument.ContentElementName)) {
 314                     return new LabelView(elem);
 315                 } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
 316                     return new ParagraphView(elem);
 317                 } else if (kind.equals(AbstractDocument.SectionElementName)) {
 318                     return new BoxView(elem, View.Y_AXIS);
 319                 } else if (kind.equals(StyleConstants.ComponentElementName)) {
 320                     return new ComponentView(elem);
 321                 } else if (kind.equals(StyleConstants.IconElementName)) {
 322                     return new IconView(elem);
 323                 }
 324             }
 325 
 326             // default to text display
 327             return new LabelView(elem);
 328         }
 329 
 330     }
 331 
 332     // --- Action implementations ---------------------------------
 333 
 334     private static final Action[] defaultActions = {
 335         new FontFamilyAction("font-family-SansSerif", "SansSerif"),
 336         new FontFamilyAction("font-family-Monospaced", "Monospaced"),
 337         new FontFamilyAction("font-family-Serif", "Serif"),
 338         new FontSizeAction("font-size-8", 8),
 339         new FontSizeAction("font-size-10", 10),
 340         new FontSizeAction("font-size-12", 12),
 341         new FontSizeAction("font-size-14", 14),
 342         new FontSizeAction("font-size-16", 16),
 343         new FontSizeAction("font-size-18", 18),
 344         new FontSizeAction("font-size-24", 24),
 345         new FontSizeAction("font-size-36", 36),
 346         new FontSizeAction("font-size-48", 48),
 347         new AlignmentAction("left-justify", StyleConstants.ALIGN_LEFT),
 348         new AlignmentAction("center-justify", StyleConstants.ALIGN_CENTER),
 349         new AlignmentAction("right-justify", StyleConstants.ALIGN_RIGHT),
 350         new BoldAction(),
 351         new ItalicAction(),
 352         new StyledInsertBreakAction(),
 353         new UnderlineAction()
 354     };
 355 
 356     /**
 357      * An action that assumes it's being fired on a JEditorPane
 358      * with a StyledEditorKit (or subclass) installed.  This has
 359      * some convenience methods for causing character or paragraph
 360      * level attribute changes.  The convenience methods will
 361      * throw an IllegalArgumentException if the assumption of
 362      * a StyledDocument, a JEditorPane, or a StyledEditorKit
 363      * fail to be true.
 364      * <p>
 365      * The component that gets acted upon by the action
 366      * will be the source of the ActionEvent if the source
 367      * can be narrowed to a JEditorPane type.  If the source
 368      * can't be narrowed, the most recently focused text
 369      * component is changed.  If neither of these are the
 370      * case, the action cannot be performed.
 371      * <p>
 372      * <strong>Warning:</strong>
 373      * Serialized objects of this class will not be compatible with
 374      * future Swing releases. The current serialization support is
 375      * appropriate for short term storage or RMI between applications running
 376      * the same version of Swing.  As of 1.4, support for long term storage
 377      * of all JavaBeans&trade;
 378      * has been added to the <code>java.beans</code> package.
 379      * Please see {@link java.beans.XMLEncoder}.
 380      */
 381     @SuppressWarnings("serial") // Same-version serialization only
 382     public abstract static class StyledTextAction extends TextAction {
 383 
 384         /**
 385          * Creates a new StyledTextAction from a string action name.
 386          *
 387          * @param nm the name of the action
 388          */
 389         public StyledTextAction(String nm) {
 390             super(nm);
 391         }
 392 
 393         /**
 394          * Gets the target editor for an action.
 395          *
 396          * @param e the action event
 397          * @return the editor
 398          */
 399         protected final JEditorPane getEditor(ActionEvent e) {
 400             JTextComponent tcomp = getTextComponent(e);
 401             if (tcomp instanceof JEditorPane) {
 402                 return (JEditorPane) tcomp;
 403             }
 404             return null;
 405         }
 406 
 407         /**
 408          * Gets the document associated with an editor pane.
 409          *
 410          * @param e the editor
 411          * @return the document
 412          * @exception IllegalArgumentException for the wrong document type
 413          */
 414         protected final StyledDocument getStyledDocument(JEditorPane e) {
 415             Document d = e.getDocument();
 416             if (d instanceof StyledDocument) {
 417                 return (StyledDocument) d;
 418             }
 419             throw new IllegalArgumentException("document must be StyledDocument");
 420         }
 421 
 422         /**
 423          * Gets the editor kit associated with an editor pane.
 424          *
 425          * @param e the editor pane
 426          * @return the kit
 427          * @exception IllegalArgumentException for the wrong document type
 428          */
 429         protected final StyledEditorKit getStyledEditorKit(JEditorPane e) {
 430             EditorKit k = e.getEditorKit();
 431             if (k instanceof StyledEditorKit) {
 432                 return (StyledEditorKit) k;
 433             }
 434             throw new IllegalArgumentException("EditorKit must be StyledEditorKit");
 435         }
 436 
 437         /**
 438          * Applies the given attributes to character
 439          * content.  If there is a selection, the attributes
 440          * are applied to the selection range.  If there
 441          * is no selection, the attributes are applied to
 442          * the input attribute set which defines the attributes
 443          * for any new text that gets inserted.
 444          *
 445          * @param editor the editor
 446          * @param attr the attributes
 447          * @param replace   if true, then replace the existing attributes first
 448          */
 449         protected final void setCharacterAttributes(JEditorPane editor,
 450                                               AttributeSet attr, boolean replace) {
 451             int p0 = editor.getSelectionStart();
 452             int p1 = editor.getSelectionEnd();
 453             if (p0 != p1) {
 454                 StyledDocument doc = getStyledDocument(editor);
 455                 doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
 456             }
 457             StyledEditorKit k = getStyledEditorKit(editor);
 458             MutableAttributeSet inputAttributes = k.getInputAttributes();
 459             if (replace) {
 460                 inputAttributes.removeAttributes(inputAttributes);
 461             }
 462             inputAttributes.addAttributes(attr);
 463         }
 464 
 465         /**
 466          * Applies the given attributes to paragraphs.  If
 467          * there is a selection, the attributes are applied
 468          * to the paragraphs that intersect the selection.
 469          * if there is no selection, the attributes are applied
 470          * to the paragraph at the current caret position.
 471          *
 472          * @param editor the editor
 473          * @param attr the attributes
 474          * @param replace   if true, replace the existing attributes first
 475          */
 476         protected final void setParagraphAttributes(JEditorPane editor,
 477                                            AttributeSet attr, boolean replace) {
 478             int p0 = editor.getSelectionStart();
 479             int p1 = editor.getSelectionEnd();
 480             StyledDocument doc = getStyledDocument(editor);
 481             doc.setParagraphAttributes(p0, p1 - p0, attr, replace);
 482         }
 483 
 484     }
 485 
 486     /**
 487      * An action to set the font family in the associated
 488      * JEditorPane.  This will use the family specified as
 489      * the command string on the ActionEvent if there is one,
 490      * otherwise the family that was initialized with will be used.
 491      * <p>
 492      * <strong>Warning:</strong>
 493      * Serialized objects of this class will not be compatible with
 494      * future Swing releases. The current serialization support is
 495      * appropriate for short term storage or RMI between applications running
 496      * the same version of Swing.  As of 1.4, support for long term storage
 497      * of all JavaBeans&trade;
 498      * has been added to the <code>java.beans</code> package.
 499      * Please see {@link java.beans.XMLEncoder}.
 500      */
 501     @SuppressWarnings("serial") // Same-version serialization only
 502     public static class FontFamilyAction extends StyledTextAction {
 503 
 504         /**
 505          * Creates a new FontFamilyAction.
 506          *
 507          * @param nm the action name
 508          * @param family the font family
 509          */
 510         public FontFamilyAction(String nm, String family) {
 511             super(nm);
 512             this.family = family;
 513         }
 514 
 515         /**
 516          * Sets the font family.
 517          *
 518          * @param e the event
 519          */
 520         public void actionPerformed(ActionEvent e) {
 521             JEditorPane editor = getEditor(e);
 522             if (editor != null) {
 523                 String family = this.family;
 524                 if ((e != null) && (e.getSource() == editor)) {
 525                     String s = e.getActionCommand();
 526                     if (s != null) {
 527                         family = s;
 528                     }
 529                 }
 530                 if (family != null) {
 531                     MutableAttributeSet attr = new SimpleAttributeSet();
 532                     StyleConstants.setFontFamily(attr, family);
 533                     setCharacterAttributes(editor, attr, false);
 534                 } else {
 535                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 536                 }
 537             }
 538         }
 539 
 540         private String family;
 541     }
 542 
 543     /**
 544      * An action to set the font size in the associated
 545      * JEditorPane.  This will use the size specified as
 546      * the command string on the ActionEvent if there is one,
 547      * otherwise the size that was initialized with will be used.
 548      * <p>
 549      * <strong>Warning:</strong>
 550      * Serialized objects of this class will not be compatible with
 551      * future Swing releases. The current serialization support is
 552      * appropriate for short term storage or RMI between applications running
 553      * the same version of Swing.  As of 1.4, support for long term storage
 554      * of all JavaBeans&trade;
 555      * has been added to the <code>java.beans</code> package.
 556      * Please see {@link java.beans.XMLEncoder}.
 557      */
 558     @SuppressWarnings("serial") // Same-version serialization only
 559     public static class FontSizeAction extends StyledTextAction {
 560 
 561         /**
 562          * Creates a new FontSizeAction.
 563          *
 564          * @param nm the action name
 565          * @param size the font size
 566          */
 567         public FontSizeAction(String nm, int size) {
 568             super(nm);
 569             this.size = size;
 570         }
 571 
 572         /**
 573          * Sets the font size.
 574          *
 575          * @param e the action event
 576          */
 577         public void actionPerformed(ActionEvent e) {
 578             JEditorPane editor = getEditor(e);
 579             if (editor != null) {
 580                 int size = this.size;
 581                 if ((e != null) && (e.getSource() == editor)) {
 582                     String s = e.getActionCommand();
 583                     try {
 584                         size = Integer.parseInt(s, 10);
 585                     } catch (NumberFormatException nfe) {
 586                     }
 587                 }
 588                 if (size != 0) {
 589                     MutableAttributeSet attr = new SimpleAttributeSet();
 590                     StyleConstants.setFontSize(attr, size);
 591                     setCharacterAttributes(editor, attr, false);
 592                 } else {
 593                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 594                 }
 595             }
 596         }
 597 
 598         private int size;
 599     }
 600 
 601     /**
 602      * An action to set foreground color.  This sets the
 603      * <code>StyleConstants.Foreground</code> attribute for the
 604      * currently selected range of the target JEditorPane.
 605      * This is done by calling
 606      * <code>StyledDocument.setCharacterAttributes</code>
 607      * on the styled document associated with the target
 608      * JEditorPane.
 609      * <p>
 610      * If the target text component is specified as the
 611      * source of the ActionEvent and there is a command string,
 612      * the command string will be interpreted as the foreground
 613      * color.  It will be interpreted by called
 614      * <code>Color.decode</code>, and should therefore be
 615      * legal input for that method.
 616      * <p>
 617      * <strong>Warning:</strong>
 618      * Serialized objects of this class will not be compatible with
 619      * future Swing releases. The current serialization support is
 620      * appropriate for short term storage or RMI between applications running
 621      * the same version of Swing.  As of 1.4, support for long term storage
 622      * of all JavaBeans&trade;
 623      * has been added to the <code>java.beans</code> package.
 624      * Please see {@link java.beans.XMLEncoder}.
 625      */
 626     @SuppressWarnings("serial") // Same-version serialization only
 627     public static class ForegroundAction extends StyledTextAction {
 628 
 629         /**
 630          * Creates a new ForegroundAction.
 631          *
 632          * @param nm the action name
 633          * @param fg the foreground color
 634          */
 635         public ForegroundAction(String nm, Color fg) {
 636             super(nm);
 637             this.fg = fg;
 638         }
 639 
 640         /**
 641          * Sets the foreground color.
 642          *
 643          * @param e the action event
 644          */
 645         public void actionPerformed(ActionEvent e) {
 646             JEditorPane editor = getEditor(e);
 647             if (editor != null) {
 648                 Color fg = this.fg;
 649                 if ((e != null) && (e.getSource() == editor)) {
 650                     String s = e.getActionCommand();
 651                     try {
 652                         fg = Color.decode(s);
 653                     } catch (NumberFormatException nfe) {
 654                     }
 655                 }
 656                 if (fg != null) {
 657                     MutableAttributeSet attr = new SimpleAttributeSet();
 658                     StyleConstants.setForeground(attr, fg);
 659                     setCharacterAttributes(editor, attr, false);
 660                 } else {
 661                     UIManager.getLookAndFeel().provideErrorFeedback(editor);
 662                 }
 663             }
 664         }
 665 
 666         private Color fg;
 667     }
 668 
 669     /**
 670      * An action to set paragraph alignment.  This sets the
 671      * <code>StyleConstants.Alignment</code> attribute for the
 672      * currently selected range of the target JEditorPane.
 673      * This is done by calling
 674      * <code>StyledDocument.setParagraphAttributes</code>
 675      * on the styled document associated with the target
 676      * JEditorPane.
 677      * <p>
 678      * If the target text component is specified as the
 679      * source of the ActionEvent and there is a command string,
 680      * the command string will be interpreted as an integer
 681      * that should be one of the legal values for the
 682      * <code>StyleConstants.Alignment</code> attribute.
 683      * <p>
 684      * <strong>Warning:</strong>
 685      * Serialized objects of this class will not be compatible with
 686      * future Swing releases. The current serialization support is
 687      * appropriate for short term storage or RMI between applications running
 688      * the same version of Swing.  As of 1.4, support for long term storage
 689      * of all JavaBeans&trade;
 690      * has been added to the <code>java.beans</code> package.
 691      * Please see {@link java.beans.XMLEncoder}.
 692      */
 693     @SuppressWarnings("serial") // Same-version serialization only
 694     public static class AlignmentAction extends StyledTextAction {
 695 
 696         /**
 697          * Creates a new AlignmentAction.
 698          *
 699          * @param nm the action name
 700          * @param a the alignment &gt;= 0
 701          */
 702         public AlignmentAction(String nm, int a) {
 703             super(nm);
 704             this.a = a;
 705         }
 706 
 707         /**
 708          * Sets the alignment.
 709          *
 710          * @param e the action event
 711          */
 712         public void actionPerformed(ActionEvent e) {
 713             JEditorPane editor = getEditor(e);
 714             if (editor != null) {
 715                 int a = this.a;
 716                 if ((e != null) && (e.getSource() == editor)) {
 717                     String s = e.getActionCommand();
 718                     try {
 719                         a = Integer.parseInt(s, 10);
 720                     } catch (NumberFormatException nfe) {
 721                     }
 722                 }
 723                 MutableAttributeSet attr = new SimpleAttributeSet();
 724                 StyleConstants.setAlignment(attr, a);
 725                 setParagraphAttributes(editor, attr, false);
 726             }
 727         }
 728 
 729         private int a;
 730     }
 731 
 732     /**
 733      * An action to toggle the bold attribute.
 734      * <p>
 735      * <strong>Warning:</strong>
 736      * Serialized objects of this class will not be compatible with
 737      * future Swing releases. The current serialization support is
 738      * appropriate for short term storage or RMI between applications running
 739      * the same version of Swing.  As of 1.4, support for long term storage
 740      * of all JavaBeans&trade;
 741      * has been added to the <code>java.beans</code> package.
 742      * Please see {@link java.beans.XMLEncoder}.
 743      */
 744     @SuppressWarnings("serial") // Same-version serialization only
 745     public static class BoldAction extends StyledTextAction {
 746 
 747         /**
 748          * Constructs a new BoldAction.
 749          */
 750         public BoldAction() {
 751             super("font-bold");
 752         }
 753 
 754         /**
 755          * Toggles the bold attribute.
 756          *
 757          * @param e the action event
 758          */
 759         public void actionPerformed(ActionEvent e) {
 760             JEditorPane editor = getEditor(e);
 761             if (editor != null) {
 762                 StyledEditorKit kit = getStyledEditorKit(editor);
 763                 MutableAttributeSet attr = kit.getInputAttributes();
 764                 boolean bold = (StyleConstants.isBold(attr)) ? false : true;
 765                 SimpleAttributeSet sas = new SimpleAttributeSet();
 766                 StyleConstants.setBold(sas, bold);
 767                 setCharacterAttributes(editor, sas, false);
 768             }
 769         }
 770     }
 771 
 772     /**
 773      * An action to toggle the italic attribute.
 774      * <p>
 775      * <strong>Warning:</strong>
 776      * Serialized objects of this class will not be compatible with
 777      * future Swing releases. The current serialization support is
 778      * appropriate for short term storage or RMI between applications running
 779      * the same version of Swing.  As of 1.4, support for long term storage
 780      * of all JavaBeans&trade;
 781      * has been added to the <code>java.beans</code> package.
 782      * Please see {@link java.beans.XMLEncoder}.
 783      */
 784     @SuppressWarnings("serial") // Same-version serialization only
 785     public static class ItalicAction extends StyledTextAction {
 786 
 787         /**
 788          * Constructs a new ItalicAction.
 789          */
 790         public ItalicAction() {
 791             super("font-italic");
 792         }
 793 
 794         /**
 795          * Toggles the italic attribute.
 796          *
 797          * @param e the action event
 798          */
 799         public void actionPerformed(ActionEvent e) {
 800             JEditorPane editor = getEditor(e);
 801             if (editor != null) {
 802                 StyledEditorKit kit = getStyledEditorKit(editor);
 803                 MutableAttributeSet attr = kit.getInputAttributes();
 804                 boolean italic = (StyleConstants.isItalic(attr)) ? false : true;
 805                 SimpleAttributeSet sas = new SimpleAttributeSet();
 806                 StyleConstants.setItalic(sas, italic);
 807                 setCharacterAttributes(editor, sas, false);
 808             }
 809         }
 810     }
 811 
 812     /**
 813      * An action to toggle the underline attribute.
 814      * <p>
 815      * <strong>Warning:</strong>
 816      * Serialized objects of this class will not be compatible with
 817      * future Swing releases. The current serialization support is
 818      * appropriate for short term storage or RMI between applications running
 819      * the same version of Swing.  As of 1.4, support for long term storage
 820      * of all JavaBeans&trade;
 821      * has been added to the <code>java.beans</code> package.
 822      * Please see {@link java.beans.XMLEncoder}.
 823      */
 824     @SuppressWarnings("serial") // Same-version serialization only
 825     public static class UnderlineAction extends StyledTextAction {
 826 
 827         /**
 828          * Constructs a new UnderlineAction.
 829          */
 830         public UnderlineAction() {
 831             super("font-underline");
 832         }
 833 
 834         /**
 835          * Toggles the Underline attribute.
 836          *
 837          * @param e the action event
 838          */
 839         public void actionPerformed(ActionEvent e) {
 840             JEditorPane editor = getEditor(e);
 841             if (editor != null) {
 842                 StyledEditorKit kit = getStyledEditorKit(editor);
 843                 MutableAttributeSet attr = kit.getInputAttributes();
 844                 boolean underline = (StyleConstants.isUnderline(attr)) ? false : true;
 845                 SimpleAttributeSet sas = new SimpleAttributeSet();
 846                 StyleConstants.setUnderline(sas, underline);
 847                 setCharacterAttributes(editor, sas, false);
 848             }
 849         }
 850     }
 851 
 852 
 853     /**
 854      * StyledInsertBreakAction has similar behavior to that of
 855      * <code>DefaultEditorKit.InsertBreakAction</code>. That is when
 856      * its <code>actionPerformed</code> method is invoked, a newline
 857      * is inserted. Beyond that, this will reset the input attributes to
 858      * what they were before the newline was inserted.
 859      */
 860     @SuppressWarnings("serial") // Superclass is not serializable across versions
 861     static class StyledInsertBreakAction extends StyledTextAction {
 862         private SimpleAttributeSet tempSet;
 863 
 864         StyledInsertBreakAction() {
 865             super(insertBreakAction);
 866         }
 867 
 868         public void actionPerformed(ActionEvent e) {
 869             JEditorPane target = getEditor(e);
 870 
 871             if (target != null) {
 872                 if ((!target.isEditable()) || (!target.isEnabled())) {
 873                     UIManager.getLookAndFeel().provideErrorFeedback(target);
 874                     return;
 875                 }
 876                 StyledEditorKit sek = getStyledEditorKit(target);
 877 
 878                 if (tempSet != null) {
 879                     tempSet.removeAttributes(tempSet);
 880                 }
 881                 else {
 882                     tempSet = new SimpleAttributeSet();
 883                 }
 884                 tempSet.addAttributes(sek.getInputAttributes());
 885                 target.replaceSelection("\n");
 886 
 887                 MutableAttributeSet ia = sek.getInputAttributes();
 888 
 889                 ia.removeAttributes(ia);
 890                 ia.addAttributes(tempSet);
 891                 tempSet.removeAttributes(tempSet);
 892             }
 893             else {
 894                 // See if we are in a JTextComponent.
 895                 JTextComponent text = getTextComponent(e);
 896 
 897                 if (text != null) {
 898                     if ((!text.isEditable()) || (!text.isEnabled())) {
 899                         UIManager.getLookAndFeel().provideErrorFeedback(target);
 900                         return;
 901                     }
 902                     text.replaceSelection("\n");
 903                 }
 904             }
 905         }
 906     }
 907 }