import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.text.html.*; import javax.swing.tree.*; import javax.swing.undo.*; import java.awt.*; import java.util.*; import java.net.URL; import java.net.MalformedURLException; import java.io.IOException; // The following can be commented out for version 1.3 import pre13.AsyncBoxView; /** * A wrapper around JEditorPane to browse html using * an HTMLEditorKit extension that does aysynchronous layout. *
* An example invocation would be: *
* java HtmlExample url
*
*/
public class HtmlExample {
public static void main(String[] args) {
Properties props = System.getProperties();
props.put("http.proxyHost", "webcache1.eng");
props.put("http.proxyPort", "8080");
if (args.length != 1) {
System.err.println("need URL argument");
System.exit(1);
}
try {
JEditorPane html = new JEditorPane();
JEditorPane.registerEditorKitForContentType("text/html",
"HtmlExample$AsyncHTMLEditorKit");
URL u = new URL(args[0]);
html.setEditable(false);
html.addHyperlinkListener(new Hyperactive());
html.setBackground(Color.white);
html.setEditorKitForContentType("text/html", new AsyncHTMLEditorKit());
JScrollPane scroller = new JScrollPane();
JViewport vp = scroller.getViewport();
vp.add(html);
vp.setBackingStoreEnabled(true);
JFrame f = new JFrame("testing");
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add("Center", scroller);
f.pack();
f.setSize(600, 600);
f.setVisible(true);
// Not safe to call setPage from a thread
// other than the event thread
SwingUtilities.invokeLater(new Init(html, u));
} catch (MalformedURLException e) {
System.err.println("Bad url");
System.exit(1);
} catch (IOException ioe) {
System.err.println("IOException: " + ioe.getMessage());
System.exit(1);
}
}
static class Init implements Runnable {
Init(JEditorPane pane, URL u) {
this.u = u;
this.pane = pane;
}
public void run() {
try {
pane.setPage(u);
} catch (Throwable e) {
e.printStackTrace();
System.exit(1);
}
}
URL u;
JEditorPane pane;
}
/**
* An HTMLEditorKit that does asychronous layout
* to represent the body element and table cell
* elements.
*/
public static class AsyncHTMLEditorKit extends HTMLEditorKit {
public AsyncHTMLEditorKit() {
super();
}
/**
* Fetch a factory that is suitable for producing
* views of any models that are produced by this
* kit.
*
* @return the factory
*/
public ViewFactory getViewFactory() {
return asyncFactory;
}
static ViewFactory asyncFactory = new AsyncFactory();
/**
* Factory to build views of the html elements. This
* simply extends the behavior of the default html factory
* to build a view that does asynchronous layout for the
* BODY and TD elements.
*/
public static class AsyncFactory extends HTMLFactory {
public View create(Element elem) {
Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag kind = (HTML.Tag) o;
if ((kind == HTML.Tag.BODY) || (kind == HTML.Tag.TD)) {
//System.err.println("creating BlockView for: " + elem);
return new BlockView(elem, View.Y_AXIS);
}
}
return super.create(elem);
}
}
/**
* A view implementation to display an html block
* (as an asynchronous box) with CSS specifications.
*/
public static class BlockView extends AsyncBoxView {
/**
* Creates a new view that represents an
* html box. This can be used for a number
* of elements.
*
* @param elem the element to create a view for
* @param axis either View.X_AXIS or View.Y_AXIS
*/
public BlockView(Element elem, int axis) {
super(elem, axis);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
painter = sheet.getBoxPainter(attr);
width = -1f;
height = -1f;
}
/**
* Update any cached values that come from attributes.
*/
protected void setPropertiesFromAttributes() {
attr = getStyleSheet().getViewAttributes(this);
if (attr != null) {
setTopInset(painter.getInset(TOP, this));
setLeftInset(painter.getInset(LEFT, this));
setBottomInset(painter.getInset(BOTTOM, this));
setRightInset(painter.getInset(RIGHT, this));
// determine css width... default to unspecified
width = -1;
Object widthValue = attr.getAttribute(CSS.Attribute.WIDTH);
if (widthValue != null) {
// this is wrong.... but CSS.LengthValue isn't public yet
try {
width = Float.valueOf(widthValue.toString()).floatValue();
} catch (NumberFormatException nfe) {
width = -1;
}
}
// determine css height... default to unspecified
height = -1;
Object heightValue = attr.getAttribute(CSS.Attribute.HEIGHT);
if (heightValue != null) {
// this is wrong.... but CSS.LengthValue isn't public yet
try {
width = Float.valueOf(heightValue.toString()).floatValue();
} catch (NumberFormatException nfe) {
height = -1;
}
}
}
}
/**
* Get the StyleSheet to use for this view. By default the
* associated document is assumed to be an HTMLDocument, and
* the StyleSheet implementation is fetched from it.
*/
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
// --- View methods ------------------------------------------------
/**
* Determines the minimum span for this view along an
* axis. If a css length has been specified (i.e. width
* for X_AXIS or height for Y_AXIS), that will be used,
* otherwise the superclass behavior is used.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @returns the minimum span the view can be rendered into.
* @see View#getPreferredSpan
*/
public float getMinimumSpan(int axis) {
if ((axis == X_AXIS) && (width >= 0)) {
return width;
} else if ((axis == Y_AXIS) && (height >= 0)) {
return height;
} else {
return super.getMinimumSpan(axis);
}
}
/**
* Determines the preferred span for this view along an
* axis. If a css length has been specified (i.e. width
* for X_AXIS or height for Y_AXIS), that will be used,
* otherwise the superclass behavior is used.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @returns the minimum span the view can be rendered into.
* @see View#getPreferredSpan
*/
public float getPreferredSpan(int axis) {
if ((axis == X_AXIS) && (width >= 0)) {
return width;
} else if ((axis == Y_AXIS) && (height >= 0)) {
return height;
} else {
return super.getPreferredSpan(axis);
}
}
/**
* Establishes the parent view for this view. This is
* guaranteed to be called before any other methods if the
* parent view is functioning properly.
* * This is implemented * to forward to the superclass as well as call the * setPropertiesFromAttributes * method to set the paragraph properties from the css * attributes. The call is made at this time to ensure * the ability to resolve upward through the parents * view attributes. * * @param parent the new parent, or null if the view is * being removed from a parent it was previously added * to */ public void setParent(View parent) { super.setParent(parent); setPropertiesFromAttributes(); } /** * Renders using the given rendering surface and area on that * surface. This is implemented to delegate to the css box * painter to paint the border and background prior to the * interior. * * @param g the rendering surface to use * @param allocation the allocated region to render into * @see View#paint */ public void paint(Graphics g, Shape allocation) { Rectangle a = (Rectangle) allocation; painter.paint(g, a.x, a.y, a.width, a.height, this); super.paint(g, a); } /** * Fetches the attributes to use when rendering. This is * implemented to multiplex the attributes specified in the * model with a StyleSheet. */ public AttributeSet getAttributes() { return attr; } /** * Gets the alignment. * * @param axis may be either X_AXIS or Y_AXIS * @return the alignment */ public float getAlignment(int axis) { switch (axis) { case View.X_AXIS: return 0; case View.Y_AXIS: float span = getPreferredSpan(View.Y_AXIS); View v = getView(0); float above = v.getPreferredSpan(View.Y_AXIS); float a = (((int)span) != 0) ? (above * v.getAlignment(View.Y_AXIS)) / span: 0; return a; default: throw new IllegalArgumentException("Invalid axis: " + axis); } } public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) { super.changedUpdate(changes, a, f); int pos = changes.getOffset(); if (pos <= getStartOffset() && (pos + changes.getLength()) >= getEndOffset()) { setPropertiesFromAttributes(); } } float width; float height; private AttributeSet attr; private StyleSheet.BoxPainter painter; } } static class Hyperactive implements HyperlinkListener { /** * Notification of a change relative to a * hyperlink. */ public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { JEditorPane html = (JEditorPane) e.getSource(); if (e instanceof HTMLFrameHyperlinkEvent) { HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; HTMLDocument doc = (HTMLDocument)html.getDocument(); doc.processHTMLFrameHyperlinkEvent(evt); } else { try { html.setPage(e.getURL()); } catch (Throwable t) { t.printStackTrace(); } } } } } }