/*
 * HTMLEditorPane.java  $Revision: 1.4 $  $Date: 2007/10/03 04:39:40 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Scaffold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.meldcraft.gui.swing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;

import javax.swing.JEditorPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.*;
import javax.swing.text.html.*;

import com.meldcraft.util.ColorUtil;
import com.meldcraft.util.PlatformUtil;

/**
 * JEditorPane that automatically configures itself for HTML content and
 * launches hyperlinks in an external browser.
 *
 * @author Rob Richards
 */
public class HTMLEditorPane extends JEditorPane implements HyperlinkListener
{
// Member variables
    private HTMLEditorKit   mHTMLKit;
    private Color           mBodyColor;
    private String          mBodyColorString;
    private String          mAColorString;
    private String          mAddressColorString;

// Constructors and initialization
    public HTMLEditorPane()
    {
        configureHTMLKit();

        addHyperlinkListener(this);
        setEditable(false);
    }

// Interface implementations
    // HyperlinkListener interface
    /** Launches the URL in an external browser. */
    public void hyperlinkUpdate(HyperlinkEvent e)
    {   
        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
        {   
            URL targetURL = e.getURL();
            openURL(targetURL);
        }   
    }   

// Public methods
    /**
     * Overridden to detect HTML content and configure accordingly.  Any
     * text beginning with &lt;html&gt; (case insensitive) is considered
     * to be HTML content.
     */
    public void setText(String text)
    {
        configureContentType(text);
        super.setText(text);
    }

    /** Overridden to contain DefaultCaret scrollRectToVisible calls. */
    public void scrollRectToVisible(Rectangle contentRect)
    {
    }

    /** Overridden to force text to expand width as much as possible. */
    public Dimension getMinimumSize()
    {
        return getPreferredSize();
    }

    /**
     * Override to force HTML text to color when color is different from
     * the default HTML text color.
     */
    public void setForeground(Color color)
    {
        super.setForeground(color);

        if (mHTMLKit == null)
        {
            // foreground is set during super initialization, before the
            // HTML kit is configured
            return;
        }

        if ((color == null) || !color.equals(mBodyColor))
        {
            // force the StyleSheet to use this color for all text
            StyleSheet sheet = mHTMLKit.getStyleSheet();

            String colorStr = ColorUtil.asHexValues(color);

            Style bodyStyle = sheet.getStyle(HTML.Tag.BODY.toString());
            sheet.addCSSAttribute(bodyStyle, CSS.Attribute.COLOR, colorStr);

            Style aStyle = sheet.getStyle(HTML.Tag.A.toString());
            sheet.addCSSAttribute(aStyle, CSS.Attribute.COLOR, colorStr);

            Style addressStyle = sheet.getStyle(HTML.Tag.ADDRESS.toString());
            sheet.addCSSAttribute(addressStyle, CSS.Attribute.COLOR, colorStr);
        }
        else
        {
            // use default StyleSheet colors for text
            StyleSheet sheet = mHTMLKit.getStyleSheet();

            Style bodyStyle = sheet.getStyle(HTML.Tag.BODY.toString());
            sheet.addCSSAttribute(bodyStyle, CSS.Attribute.COLOR,
                                             mBodyColorString);

            Style aStyle = sheet.getStyle(HTML.Tag.A.toString());
            sheet.addCSSAttribute(aStyle, CSS.Attribute.COLOR,
                                          mAColorString);

            Style addressStyle = sheet.getStyle(HTML.Tag.ADDRESS.toString());
            sheet.addCSSAttribute(addressStyle, CSS.Attribute.COLOR,
                                                mAddressColorString);
        }
    }

    public void configureForWidth(int width)
    {
        setSize(width, 100);

        int height = 0;
        try
        {
            Rectangle rect = modelToView(getDocument().getLength()-1);
            if (rect != null)
            {
                height = rect.y + rect.height;
            }
        }
        catch (BadLocationException e)
        {
        }

        setPreferredSize(new Dimension(width, height));
    }

// Protected methods
    protected void configureContentType(String text)
    {
        String html = "<html>";
        String contentType = "text/plain";
        if ((text != null) && (text.length() >= html.length()))
        {
            if (text.substring(0,html.length()).equalsIgnoreCase(html))
            {
                contentType = "text/html";
            }
        }

        if (!this.getContentType().equals(contentType))
        {
            this.setContentType(contentType);
        }
    }

    /** Configure this JEditorPane for HTML content. */
    protected void configureHTMLKit()
    {
        // create and configure custom editor kit -
        HTMLEditorKit kit = new CustomHTMLEditorKit();
        StyleSheet sheet = kit.getStyleSheet();

        // set the font to match the normal JEditorPane content
        Font normalFont = this.getFont();
        Style bodyStyle = sheet.getStyle(HTML.Tag.BODY.toString());
        sheet.addCSSAttribute(bodyStyle, CSS.Attribute.FONT_FAMILY,
                                         normalFont.getFamily());
        sheet.addCSSAttribute(bodyStyle, CSS.Attribute.FONT_SIZE,
                                         normalFont.getSize() + "pt");

        // register kit and configure for HTML content
        String htmlType = kit.getContentType(); // i.e. "text/html"
        this.setEditorKitForContentType(htmlType,kit);

        mHTMLKit = kit;

        // save the default text colors of the editor kit
        mBodyColorString =
            (String)bodyStyle.getAttribute(CSS.Attribute.COLOR).toString();
        mBodyColor = ColorUtil.getColor(mBodyColorString);

        Style aStyle = sheet.getStyle(HTML.Tag.A.toString());
        mAColorString =
            (String)aStyle.getAttribute(CSS.Attribute.COLOR).toString();

        Style addressStyle = sheet.getStyle(HTML.Tag.ADDRESS.toString());
        mAddressColorString =
            (String)addressStyle.getAttribute(CSS.Attribute.COLOR).toString();
    }

    /** Called when a hyperlink has been clicked. */
    protected void openURL(URL url)
    {
        try
        {
            PlatformUtil.openURL(url);
        }
        catch (java.io.IOException e)
        {
            // ignored
        }
    }

// Inner classes
    /**
     * Custom HTMLEditorKit with a private StyleSheet.  Modifications to the
     * StyleSheet returned by getStyleSheet() will not interfere with the
     * one returned by the default implementation in
     * javax.swing.text.html.HTMLEditorKit.
     */
    public class CustomHTMLEditorKit extends HTMLEditorKit
    {
        private StyleSheet mStyleSheet = null;

        /**
         * Overridden to return a private StyleSheet, as opposed to the
         * StyleSheet singleton returned by javax.swing.text.html.HTMLEditorKit.
         */
        public StyleSheet getStyleSheet()
        {
            if (mStyleSheet == null)
            {
                mStyleSheet = new StyleSheet();
                try
                {
                    // borrowed from the HTMLEditorKit source code in JDK 1.3
                    InputStream is = HTMLEditorKit.class.getResourceAsStream(
                                                    HTMLEditorKit.DEFAULT_CSS);
                    Reader r = new BufferedReader(new InputStreamReader(is));
                    mStyleSheet.loadRules(r, null);
                    r.close();
                }
                catch (Throwable e)
                {
                    // on error we simply have no styles... the html
                    // will look mighty wrong but still function.
                    //e.printStackTrace();
                }
            }
            return mStyleSheet;
        }
    }
}

