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 */
25package javax.swing.text;
26
27import java.io.*;
28import java.awt.*;
29import java.awt.event.ActionEvent;
30import java.beans.PropertyChangeEvent;
31import java.beans.PropertyChangeListener;
32import javax.swing.event.*;
33import javax.swing.Action;
34import javax.swing.JEditorPane;
35import javax.swing.KeyStroke;
36import 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
48public 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&trade;
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&trade;
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&trade;
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&trade;
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&trade;
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&trade;
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&trade;
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&trade;
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}
910