ExtendedParagraphExample.java Source code

Java tutorial

Introduction

Here is the source code for ExtendedParagraphExample.java

Source

/*
Core SWING Advanced Programming 
By Kim Topley
ISBN: 0 13 083292 8       
Publisher: Prentice Hall  
*/

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.ParagraphView;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;

public class ExtendedParagraphExample {
    public static void createDocumentStyles(StyleContext sc) {
        Style defaultStyle = sc.getStyle(StyleContext.DEFAULT_STYLE);

        // Create and add the main document style
        Style mainStyle = sc.addStyle(mainStyleName, defaultStyle);
        StyleConstants.setLeftIndent(mainStyle, 16);
        StyleConstants.setRightIndent(mainStyle, 16);
        StyleConstants.setFirstLineIndent(mainStyle, 16);
        StyleConstants.setFontFamily(mainStyle, "serif");
        StyleConstants.setFontSize(mainStyle, 12);

        // Create and add the constant width style
        Style cwStyle = sc.addStyle(charStyleName, null);
        StyleConstants.setFontFamily(cwStyle, "monospaced");
        StyleConstants.setForeground(cwStyle, Color.white);

        // Create and add the heading style
        Style heading2Style = sc.addStyle(heading2StyleName, null);
        StyleConstants.setForeground(heading2Style, Color.red);
        StyleConstants.setFontSize(heading2Style, 16);
        StyleConstants.setFontFamily(heading2Style, "serif");
        StyleConstants.setBold(heading2Style, true);
        StyleConstants.setLeftIndent(heading2Style, 8);
        StyleConstants.setFirstLineIndent(heading2Style, 0);

        // Create and add the extended para styles
        Style paraStyle = sc.addStyle(paraStyleName, null);
        Color bgColor = Color.gray;
        ExtendedStyleConstants.setParagraphBackground(paraStyle, bgColor);
        ExtendedStyleConstants.setParagraphBorder(paraStyle,
                BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2),
                        BorderFactory.createCompoundBorder(
                                BorderFactory.createEtchedBorder(bgColor.brighter(), bgColor.darker()),
                                BorderFactory.createEmptyBorder(4, 4, 4, 4))));
    }

    public static void addText(JTextPane pane, StyleContext sc, Style logicalStyle, Paragraph[] content) {
        // The outer loop adds paragraphs, while the
        // inner loop adds character runs.
        int paragraphs = content.length;
        for (int i = 0; i < paragraphs; i++) {
            Run[] runs = content[i].content;
            for (int j = 0; j < runs.length; j++) {
                pane.setCharacterAttributes(
                        runs[j].styleName == null ? SimpleAttributeSet.EMPTY : sc.getStyle(runs[j].styleName),
                        true);
                pane.replaceSelection(runs[j].content);
            }

            // At the end of the paragraph, add the logical style and
            // any overriding paragraph style and then terminate the
            // paragraph with a newline.
            pane.setParagraphAttributes(SimpleAttributeSet.EMPTY, true);

            if (logicalStyle != null) {
                pane.setLogicalStyle(logicalStyle);
            }

            if (content[i].styleName != null) {
                pane.setParagraphAttributes(sc.getStyle(content[i].styleName), false);
            }

            if (i != paragraphs - 1) {
                pane.replaceSelection("\n");
            }
        }
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (Exception evt) {
        }

        JFrame f = new JFrame("Extended Paragraph Example");

        // Create the StyleContext, the document and the pane
        final StyleContext sc = new StyleContext();
        final DefaultStyledDocument doc = new DefaultStyledDocument(sc);
        final JTextPane pane = new JTextPane(doc);
        pane.setEditorKit(new ExtendedStyledEditorKit());

        try {
            // Add the text and apply the styles
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    // Build the styles
                    createDocumentStyles(sc);

                    // Add the text
                    addText(pane, sc, sc.getStyle(mainStyleName), content);
                }
            });
        } catch (Exception e) {
            System.out.println("Exception when constructing document: " + e);
            System.exit(1);
        }

        f.getContentPane().add(new JScrollPane(pane));
        f.setSize(400, 300);
        f.setVisible(true);
    }

    // Style names
    public static final String mainStyleName = "MainStyle";

    public static final String heading2StyleName = "Heading2";

    public static final String charStyleName = "ConstantWidth";

    public static final String paraStyleName = "ExtendedPara";

    // Inner classes used to define paragraph structure
    public static class Run {
        public Run(String styleName, String content) {
            this.styleName = styleName;
            this.content = content;
        }

        public String styleName;

        public String content;
    }

    public static class Paragraph {
        public Paragraph(String styleName, Run[] content) {
            this.styleName = styleName;
            this.content = content;
        }

        public String styleName;

        public Run[] content;
    }

    public static final Paragraph[] content = new Paragraph[] {
            new Paragraph(heading2StyleName, new Run[] { new Run(null, "Attributes, Styles and Style Contexts") }),
            new Paragraph(paraStyleName, new Run[] { new Run(null, "The simple "),
                    new Run(charStyleName, "PlainDocument"),
                    new Run(null,
                            " class that you saw in the previous " + "chapter is only capable of holding text. "
                                    + "The more complex text components use a more "
                                    + "sophisticated model that implements the "),
                    new Run(charStyleName, "StyledDocument"), new Run(null, " interface. "),
                    new Run(charStyleName, "StyledDocument"), new Run(null, " is a sub-interface of "),
                    new Run(charStyleName, "Document"),
                    new Run(null,
                            " that contains methods for manipulating attributes "
                                    + "that control the way in which the text in the "
                                    + "document is displayed. The Swing text package "
                                    + "contains a concrete implementation of "),
                    new Run(charStyleName, "StyledDocument"), new Run(null, " called "),
                    new Run(charStyleName, "DefaultStyledDocument"),
                    new Run(null, " that is used as the default model for "), new Run(charStyleName, "JTextPane"),
                    new Run(null, " and is also the base class from which " + "more specific models, such as the "),
                    new Run(charStyleName, "HTMLDocument"),
                    new Run(null,
                            " class that handles input in HTML format, can be "
                                    + "created. In order to make use of "),
                    new Run(charStyleName, "DefaultStyledDocument"), new Run(null, " and "),
                    new Run(charStyleName, "JTextPane"),
                    new Run(null, " you need to understand how Swing represents " + "and uses attributes.") }) };
}

class ExtendedStyleConstants {
    public ExtendedStyleConstants(String name) {
        this.name = name;
    }

    public String toString() {
        return name;
    }

    /**
     * The border to be used for a paragraph. Type is javax.swing.border.Border
     */
    public static final Object ParagraphBorder = ExtendedParagraphConstants.ParagraphBorder;

    /**
     * The background to be used for a paragraph. Type is java.awt.Color
     */
    public static final Object ParagraphBackground = ExtendedParagraphConstants.ParagraphBackground;

    /* Adds the border attribute */
    public static void setParagraphBorder(MutableAttributeSet a, Border b) {
        a.addAttribute(ParagraphBorder, b);
    }

    /* Gets the border attribute */
    public static Border getParagraphBorder(AttributeSet a) {
        return (Border) a.getAttribute(ParagraphBorder);
    }

    /* Adds the paragraph background attribute */
    public static void setParagraphBackground(MutableAttributeSet a, Color c) {
        a.addAttribute(ParagraphBackground, c);
    }

    /* Gets the paragraph background attribute */
    public static Color getParagraphBackground(AttributeSet a) {
        return (Color) a.getAttribute(ParagraphBackground);
    }

    /* A typesafe collection of extended paragraph attributes */
    public static class ExtendedParagraphConstants extends ExtendedStyleConstants
            implements AttributeSet.ParagraphAttribute {
        /**
         * The paragraph border attribute.
         */
        public static final Object ParagraphBorder = new ExtendedParagraphConstants("ParagraphBorder");

        /**
         * The paragraph background attribute.
         */
        public static final Object ParagraphBackground = new ExtendedParagraphConstants("ParagraphBackground");

        private ExtendedParagraphConstants(String name) {
            super(name);
        }
    }

    protected String name; // Name of an attribute
}

class ExtendedStyledEditorKit extends StyledEditorKit {
    public Object clone() {
        return new ExtendedStyledEditorKit();
    }

    public ViewFactory getViewFactory() {
        return defaultFactory;
    }

    /* The extended view factory */
    static class ExtendedStyledViewFactory implements ViewFactory {
        public View create(Element elem) {
            String elementName = elem.getName();
            if (elementName != null) {
                if (elementName.equals(AbstractDocument.ParagraphElementName)) {
                    return new ExtendedParagraphView(elem);
                }
            }

            // Delegate others to StyledEditorKit
            return styledEditorKitFactory.create(elem);
        }
    }

    private static final ViewFactory styledEditorKitFactory = (new StyledEditorKit()).getViewFactory();

    private static final ViewFactory defaultFactory = new ExtendedStyledViewFactory();
}

class ExtendedParagraphView extends ParagraphView {
    public ExtendedParagraphView(Element elem) {
        super(elem);
    }

    // Override ParagraphView methods
    protected void setPropertiesFromAttributes() {
        AttributeSet attr = getAttributes();
        if (attr != null) {
            super.setPropertiesFromAttributes();
            paraInsets = new Insets(getTopInset(), getLeftInset(), getBottomInset(), getRightInset());

            border = ExtendedStyleConstants.getParagraphBorder(attr);
            bgColor = ExtendedStyleConstants.getParagraphBackground(attr);
            if (bgColor != null && border == null) {
                // Provide a small margin if the background
                // is being filled and there is no border
                border = smallBorder;
            }

            if (border != null) {
                Insets borderInsets = border.getBorderInsets(getContainer());
                setInsets((short) (paraInsets.top + borderInsets.top),
                        (short) (paraInsets.left + borderInsets.left),
                        (short) (paraInsets.bottom + borderInsets.bottom),
                        (short) (paraInsets.right + borderInsets.right));
            }
        }
    }

    public void paint(Graphics g, Shape a) {
        Container comp = getContainer();
        Rectangle alloc = new Rectangle(a.getBounds());

        alloc.x += paraInsets.left;
        alloc.y += paraInsets.top;
        alloc.width -= paraInsets.left + paraInsets.right;
        alloc.height -= paraInsets.top + paraInsets.bottom;

        if (bgColor != null) {
            Color origColor = g.getColor();
            g.setColor(bgColor);
            g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height);
            g.setColor(origColor);
        }

        if (border != null) {
            // Paint the border
            border.paintBorder(comp, g, alloc.x, alloc.y, alloc.width, alloc.height);
        }
        super.paint(g, a); // Note: pass ORIGINAL allocation
    }

    // Attribute cache
    protected Color bgColor; // Background color, or null for transparent.

    protected Border border; // Border, or null for no border

    protected Insets paraInsets; // Original paragraph insets

    protected static final Border smallBorder = BorderFactory.createEmptyBorder(2, 2, 2, 2);
}