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