| /* BasicHTML.java -- Provides HTML support to ComponentUI implementations |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.plaf.basic; |
| |
| import java.awt.Container; |
| import java.awt.Graphics; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| import java.io.IOException; |
| import java.io.StringReader; |
| |
| import javax.swing.JComponent; |
| import javax.swing.SwingConstants; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.text.BadLocationException; |
| import javax.swing.text.Document; |
| import javax.swing.text.EditorKit; |
| import javax.swing.text.Element; |
| import javax.swing.text.Position; |
| import javax.swing.text.View; |
| import javax.swing.text.ViewFactory; |
| import javax.swing.text.html.HTMLDocument; |
| import javax.swing.text.html.HTMLEditorKit; |
| |
| /** |
| * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI} |
| * implementations. |
| * |
| * @author Roman Kennke (kennke@aicas.com) |
| */ |
| public class BasicHTML |
| { |
| |
| /** |
| * This class serves as the root view for HTML rendering components. |
| * Its purpose and implementation is similar to the BasicTextUI.RootView |
| * class, only that is implements some stuff differently due to the nature |
| * of not beeing inside a JTextComponent. |
| * |
| * @author Roman Kennke (kennke@aicas.com) |
| */ |
| private static class HTMLRootView extends View |
| { |
| /** |
| * The real root view. |
| */ |
| private View view; |
| |
| /** |
| * The component on which to render the view. |
| */ |
| private JComponent component; |
| |
| /** |
| * The EditorKit. |
| */ |
| private EditorKit editorKit; |
| |
| /** |
| * The document to use. |
| */ |
| private Document document; |
| |
| /** |
| * Creates a new RootView. |
| */ |
| public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc) |
| { |
| super(null); |
| component = c; |
| editorKit = kit; |
| document = doc; |
| setView(view); |
| } |
| |
| /** |
| * Returns the ViewFactory for this RootView. If the current EditorKit |
| * provides a ViewFactory, this is used. Otherwise the TextUI itself |
| * is returned as a ViewFactory. |
| * |
| * @return the ViewFactory for this RootView |
| */ |
| public ViewFactory getViewFactory() |
| { |
| return editorKit.getViewFactory(); |
| } |
| |
| /** |
| * Indicates that the preferences of one of the child view has changed. |
| * This calls revalidate on the text component. |
| * |
| * @param v the child view which's preference has changed |
| * @param width <code>true</code> if the width preference has changed |
| * @param height <code>true</code> if the height preference has changed |
| */ |
| public void preferenceChanged(View v, boolean width, boolean height) |
| { |
| component.revalidate(); |
| } |
| |
| /** |
| * Sets the real root view. |
| * |
| * @param v the root view to set |
| */ |
| public void setView(View v) |
| { |
| if (view != null) |
| view.setParent(null); |
| |
| if (v != null) |
| v.setParent(this); |
| |
| view = v; |
| } |
| |
| /** |
| * Returns the real root view, regardless of the index. |
| * |
| * @param index not used here |
| * |
| * @return the real root view, regardless of the index. |
| */ |
| public View getView(int index) |
| { |
| return view; |
| } |
| |
| /** |
| * Returns <code>1</code> since the RootView always contains one |
| * child, that is the real root of the View hierarchy. |
| * |
| * @return <code>1</code> since the RootView always contains one |
| * child, that is the real root of the View hierarchy |
| */ |
| public int getViewCount() |
| { |
| int count = 0; |
| if (view != null) |
| count = 1; |
| return count; |
| } |
| |
| /** |
| * Returns the <code>Container</code> that contains this view. This |
| * normally will be the text component that is managed by this TextUI. |
| * |
| * @return the <code>Container</code> that contains this view |
| */ |
| public Container getContainer() |
| { |
| return component; |
| } |
| |
| /** |
| * Returns the preferred span along the specified <code>axis</code>. |
| * This is delegated to the real root view. |
| * |
| * @param axis the axis for which the preferred span is queried |
| * |
| * @return the preferred span along the axis |
| */ |
| public float getPreferredSpan(int axis) |
| { |
| if (view != null) |
| return view.getPreferredSpan(axis); |
| |
| return Integer.MAX_VALUE; |
| } |
| |
| /** |
| * Paints the view. This is delegated to the real root view. |
| * |
| * @param g the <code>Graphics</code> context to paint to |
| * @param s the allocation for the View |
| */ |
| public void paint(Graphics g, Shape s) |
| { |
| if (view != null) |
| { |
| Rectangle b = s.getBounds(); |
| view.setSize(b.width, b.height); |
| view.paint(g, s); |
| } |
| } |
| |
| |
| /** |
| * Maps a position in the document into the coordinate space of the View. |
| * The output rectangle usually reflects the font height but has a width |
| * of zero. |
| * |
| * This is delegated to the real root view. |
| * |
| * @param position the position of the character in the model |
| * @param a the area that is occupied by the view |
| * @param bias either {@link Position.Bias#Forward} or |
| * {@link Position.Bias#Backward} depending on the preferred |
| * direction bias. If <code>null</code> this defaults to |
| * <code>Position.Bias.Forward</code> |
| * |
| * @return a rectangle that gives the location of the document position |
| * inside the view coordinate space |
| * |
| * @throws BadLocationException if <code>pos</code> is invalid |
| * @throws IllegalArgumentException if b is not one of the above listed |
| * valid values |
| */ |
| public Shape modelToView(int position, Shape a, Position.Bias bias) |
| throws BadLocationException |
| { |
| return view.modelToView(position, a, bias); |
| } |
| |
| /** |
| * Maps coordinates from the <code>View</code>'s space into a position |
| * in the document model. |
| * |
| * @param x the x coordinate in the view space |
| * @param y the y coordinate in the view space |
| * @param a the allocation of this <code>View</code> |
| * @param b the bias to use |
| * |
| * @return the position in the document that corresponds to the screen |
| * coordinates <code>x, y</code> |
| */ |
| public int viewToModel(float x, float y, Shape a, Position.Bias[] b) |
| { |
| return view.viewToModel(x, y, a, b); |
| } |
| |
| /** |
| * Notification about text insertions. These are forwarded to the |
| * real root view. |
| * |
| * @param ev the DocumentEvent describing the change |
| * @param shape the current allocation of the view's display |
| * @param vf the ViewFactory to use for creating new Views |
| */ |
| public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| view.insertUpdate(ev, shape, vf); |
| } |
| |
| /** |
| * Notification about text removals. These are forwarded to the |
| * real root view. |
| * |
| * @param ev the DocumentEvent describing the change |
| * @param shape the current allocation of the view's display |
| * @param vf the ViewFactory to use for creating new Views |
| */ |
| public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| view.removeUpdate(ev, shape, vf); |
| } |
| |
| /** |
| * Notification about text changes. These are forwarded to the |
| * real root view. |
| * |
| * @param ev the DocumentEvent describing the change |
| * @param shape the current allocation of the view's display |
| * @param vf the ViewFactory to use for creating new Views |
| */ |
| public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| view.changedUpdate(ev, shape, vf); |
| } |
| |
| /** |
| * Returns the document position that is (visually) nearest to the given |
| * document position <code>pos</code> in the given direction <code>d</code>. |
| * |
| * @param pos the document position |
| * @param b the bias for <code>pos</code> |
| * @param a the allocation for the view |
| * @param d the direction, must be either {@link SwingConstants#NORTH}, |
| * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or |
| * {@link SwingConstants#EAST} |
| * @param biasRet an array of {@link Position.Bias} that can hold at least |
| * one element, which is filled with the bias of the return position |
| * on method exit |
| * |
| * @return the document position that is (visually) nearest to the given |
| * document position <code>pos</code> in the given direction |
| * <code>d</code> |
| * |
| * @throws BadLocationException if <code>pos</code> is not a valid offset in |
| * the document model |
| */ |
| public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, |
| int d, Position.Bias[] biasRet) |
| throws BadLocationException |
| { |
| return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); |
| } |
| |
| public int getStartOffset() |
| { |
| return 0; |
| } |
| |
| public int getEndOffset() |
| { |
| return getDocument().getLength(); |
| } |
| |
| public Document getDocument() |
| { |
| return document; |
| } |
| } |
| |
| /** |
| * The key that is used to store a HTML view in a JComponent's client |
| * properties. |
| */ |
| public static final String propertyKey = "html"; |
| |
| /** |
| * The key that is used to store the document base in a JComponent's client |
| * properties. The document base is used to resolve relative references |
| * in HTML. |
| */ |
| public static final String documentBaseKey = "html.base"; |
| |
| /** |
| * Creates a new instance of BasicHTML. This should not be necessary since |
| * all methods in this class are static. |
| */ |
| public BasicHTML() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Creates a {@link View} instance that can be used by the component |
| * <code>c</code> to render the HTML string <code>html</code>. |
| * |
| * @param c the component that needs to render the HTML string |
| * @param html the HTML string to be rendered |
| * |
| * @return a view that can render the HTML string |
| */ |
| public static View createHTMLView(JComponent c, String html) |
| { |
| // TODO: This might be wrong. Lets see if it turns out good when |
| // the javax.swing.text.html package is in a good shape. |
| HTMLDocument doc = new HTMLDocument(); |
| HTMLEditorKit kit = new HTMLEditorKit(); |
| StringReader reader = new StringReader(html); |
| try |
| { |
| kit.read(reader, doc, 0); |
| } |
| catch (IOException ex) |
| { |
| AssertionError err = new AssertionError("unexpected IOException"); |
| err.initCause(ex); |
| throw err; |
| } |
| catch (BadLocationException ex) |
| { |
| AssertionError err = |
| new AssertionError("unexpected BadLocationException"); |
| err.initCause(ex); |
| throw err; |
| } |
| ViewFactory vf = kit.getViewFactory(); |
| Element root = doc.getDefaultRootElement(); |
| View view = vf.create(root); |
| HTMLRootView rootView = new HTMLRootView(c, view, kit, doc); |
| return rootView; |
| } |
| |
| /** |
| * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code> |
| * otherwise. |
| * |
| * @param s the string to test |
| * |
| * @return <code>true</code> if <code>s</code> is HTML, <code>false</code> |
| * otherwise |
| */ |
| public static boolean isHTMLString(String s) |
| { |
| // We consider a string to be HTML if it contains both the '<' and '>' |
| // character at least once. |
| return (s != null) && s.contains("<") && s.contains(">"); |
| } |
| |
| /** |
| * Stores a HTML renderer in <code>c</code>'s client property if |
| * <code>text</code> is HTML, otherwise it clears the corresponding client |
| * property. This is useful for {@link javax.swing.plaf.ComponentUI} |
| * implementations that are shared between it's components. |
| * |
| * @param c the component to update the renderer for |
| * @param text the string to be rendered |
| */ |
| public static void updateRenderer(JComponent c, String text) |
| { |
| if (isHTMLString(text)) |
| c.putClientProperty(propertyKey, createHTMLView(c, text)); |
| else |
| c.putClientProperty(propertyKey, null); |
| } |
| } |