Frames | No Frames |
1: /* BasicHTML.java -- Provides HTML support to ComponentUI implementations 2: Copyright (C) 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.plaf.basic; 40: 41: import java.awt.Container; 42: import java.awt.Graphics; 43: import java.awt.Rectangle; 44: import java.awt.Shape; 45: import java.io.IOException; 46: import java.io.StringReader; 47: 48: import javax.swing.JComponent; 49: import javax.swing.SwingConstants; 50: import javax.swing.event.DocumentEvent; 51: import javax.swing.text.AttributeSet; 52: import javax.swing.text.BadLocationException; 53: import javax.swing.text.Document; 54: import javax.swing.text.EditorKit; 55: import javax.swing.text.Element; 56: import javax.swing.text.Position; 57: import javax.swing.text.View; 58: import javax.swing.text.ViewFactory; 59: import javax.swing.text.html.HTMLDocument; 60: import javax.swing.text.html.HTMLEditorKit; 61: 62: /** 63: * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI} 64: * implementations. 65: * 66: * @author Roman Kennke (kennke@aicas.com) 67: */ 68: public class BasicHTML 69: { 70: 71: /** 72: * This class serves as the root view for HTML rendering components. 73: * Its purpose and implementation is similar to the BasicTextUI.RootView 74: * class, only that is implements some stuff differently due to the nature 75: * of not beeing inside a JTextComponent. 76: * 77: * @author Roman Kennke (kennke@aicas.com) 78: */ 79: private static class HTMLRootView extends View 80: { 81: /** 82: * The real root view. 83: */ 84: private View view; 85: 86: /** 87: * The component on which to render the view. 88: */ 89: private JComponent component; 90: 91: /** 92: * The EditorKit. 93: */ 94: private EditorKit editorKit; 95: 96: /** 97: * The document to use. 98: */ 99: private Document document; 100: 101: /** 102: * Creates a new RootView. 103: */ 104: public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc) 105: { 106: super(null); 107: component = c; 108: editorKit = kit; 109: document = doc; 110: setView(view); 111: setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); 112: } 113: 114: /** 115: * Returns the ViewFactory for this RootView. If the current EditorKit 116: * provides a ViewFactory, this is used. Otherwise the TextUI itself 117: * is returned as a ViewFactory. 118: * 119: * @return the ViewFactory for this RootView 120: */ 121: public ViewFactory getViewFactory() 122: { 123: return editorKit.getViewFactory(); 124: } 125: 126: /** 127: * Indicates that the preferences of one of the child view has changed. 128: * This calls revalidate on the text component. 129: * 130: * @param v the child view which's preference has changed 131: * @param width <code>true</code> if the width preference has changed 132: * @param height <code>true</code> if the height preference has changed 133: */ 134: public void preferenceChanged(View v, boolean width, boolean height) 135: { 136: component.revalidate(); 137: } 138: 139: /** 140: * Sets the real root view. 141: * 142: * @param v the root view to set 143: */ 144: public void setView(View v) 145: { 146: if (view != null) 147: view.setParent(null); 148: 149: if (v != null) 150: v.setParent(this); 151: 152: view = v; 153: } 154: 155: /** 156: * Overridden to forward to real view. 157: */ 158: public void setSize(float w, float h) 159: { 160: view.setSize(w, h); 161: } 162: 163: /** 164: * Returns the real root view, regardless of the index. 165: * 166: * @param index not used here 167: * 168: * @return the real root view, regardless of the index. 169: */ 170: public View getView(int index) 171: { 172: return view; 173: } 174: 175: /** 176: * Returns <code>1</code> since the RootView always contains one 177: * child, that is the real root of the View hierarchy. 178: * 179: * @return <code>1</code> since the RootView always contains one 180: * child, that is the real root of the View hierarchy 181: */ 182: public int getViewCount() 183: { 184: int count = 0; 185: if (view != null) 186: count = 1; 187: return count; 188: } 189: 190: /** 191: * Returns the <code>Container</code> that contains this view. This 192: * normally will be the text component that is managed by this TextUI. 193: * 194: * @return the <code>Container</code> that contains this view 195: */ 196: public Container getContainer() 197: { 198: return component; 199: } 200: 201: /** 202: * Returns the preferred span along the specified <code>axis</code>. 203: * This is delegated to the real root view. 204: * 205: * @param axis the axis for which the preferred span is queried 206: * 207: * @return the preferred span along the axis 208: */ 209: public float getPreferredSpan(int axis) 210: { 211: if (view != null) 212: return view.getPreferredSpan(axis); 213: 214: return Integer.MAX_VALUE; 215: } 216: 217: /** 218: * Paints the view. This is delegated to the real root view. 219: * 220: * @param g the <code>Graphics</code> context to paint to 221: * @param s the allocation for the View 222: */ 223: public void paint(Graphics g, Shape s) 224: { 225: if (view != null) 226: { 227: Rectangle b = s.getBounds(); 228: view.setSize(b.width, b.height); 229: view.paint(g, s); 230: } 231: } 232: 233: 234: /** 235: * Maps a position in the document into the coordinate space of the View. 236: * The output rectangle usually reflects the font height but has a width 237: * of zero. 238: * 239: * This is delegated to the real root view. 240: * 241: * @param position the position of the character in the model 242: * @param a the area that is occupied by the view 243: * @param bias either {@link Position.Bias#Forward} or 244: * {@link Position.Bias#Backward} depending on the preferred 245: * direction bias. If <code>null</code> this defaults to 246: * <code>Position.Bias.Forward</code> 247: * 248: * @return a rectangle that gives the location of the document position 249: * inside the view coordinate space 250: * 251: * @throws BadLocationException if <code>pos</code> is invalid 252: * @throws IllegalArgumentException if b is not one of the above listed 253: * valid values 254: */ 255: public Shape modelToView(int position, Shape a, Position.Bias bias) 256: throws BadLocationException 257: { 258: return view.modelToView(position, a, bias); 259: } 260: 261: /** 262: * Maps coordinates from the <code>View</code>'s space into a position 263: * in the document model. 264: * 265: * @param x the x coordinate in the view space 266: * @param y the y coordinate in the view space 267: * @param a the allocation of this <code>View</code> 268: * @param b the bias to use 269: * 270: * @return the position in the document that corresponds to the screen 271: * coordinates <code>x, y</code> 272: */ 273: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 274: { 275: return view.viewToModel(x, y, a, b); 276: } 277: 278: /** 279: * Notification about text insertions. These are forwarded to the 280: * real root view. 281: * 282: * @param ev the DocumentEvent describing the change 283: * @param shape the current allocation of the view's display 284: * @param vf the ViewFactory to use for creating new Views 285: */ 286: public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 287: { 288: view.insertUpdate(ev, shape, vf); 289: } 290: 291: /** 292: * Notification about text removals. These are forwarded to the 293: * real root view. 294: * 295: * @param ev the DocumentEvent describing the change 296: * @param shape the current allocation of the view's display 297: * @param vf the ViewFactory to use for creating new Views 298: */ 299: public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 300: { 301: view.removeUpdate(ev, shape, vf); 302: } 303: 304: /** 305: * Notification about text changes. These are forwarded to the 306: * real root view. 307: * 308: * @param ev the DocumentEvent describing the change 309: * @param shape the current allocation of the view's display 310: * @param vf the ViewFactory to use for creating new Views 311: */ 312: public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 313: { 314: view.changedUpdate(ev, shape, vf); 315: } 316: 317: /** 318: * Returns the document position that is (visually) nearest to the given 319: * document position <code>pos</code> in the given direction <code>d</code>. 320: * 321: * @param pos the document position 322: * @param b the bias for <code>pos</code> 323: * @param a the allocation for the view 324: * @param d the direction, must be either {@link SwingConstants#NORTH}, 325: * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or 326: * {@link SwingConstants#EAST} 327: * @param biasRet an array of {@link Position.Bias} that can hold at least 328: * one element, which is filled with the bias of the return position 329: * on method exit 330: * 331: * @return the document position that is (visually) nearest to the given 332: * document position <code>pos</code> in the given direction 333: * <code>d</code> 334: * 335: * @throws BadLocationException if <code>pos</code> is not a valid offset in 336: * the document model 337: */ 338: public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 339: int d, Position.Bias[] biasRet) 340: throws BadLocationException 341: { 342: return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); 343: } 344: 345: public int getStartOffset() 346: { 347: return 0; 348: } 349: 350: public int getEndOffset() 351: { 352: return getDocument().getLength(); 353: } 354: 355: public Document getDocument() 356: { 357: return document; 358: } 359: 360: /** 361: * Overridden to return null, as a RootView has no attributes on its own. 362: */ 363: public AttributeSet getAttributes() 364: { 365: return null; 366: } 367: 368: /** 369: * Overridden to provide an element for the view. 370: */ 371: public Element getElement() 372: { 373: return view.getElement(); 374: } 375: } 376: 377: /** 378: * The key that is used to store a HTML view in a JComponent's client 379: * properties. 380: */ 381: public static final String propertyKey = "html"; 382: 383: /** 384: * The key that is used to store the document base in a JComponent's client 385: * properties. The document base is used to resolve relative references 386: * in HTML. 387: */ 388: public static final String documentBaseKey = "html.base"; 389: 390: /** 391: * Creates a new instance of BasicHTML. This should not be necessary since 392: * all methods in this class are static. 393: */ 394: public BasicHTML() 395: { 396: // Nothing to do here. 397: } 398: 399: /** 400: * Creates a {@link View} instance that can be used by the component 401: * <code>c</code> to render the HTML string <code>html</code>. 402: * 403: * @param c the component that needs to render the HTML string 404: * @param html the HTML string to be rendered 405: * 406: * @return a view that can render the HTML string 407: */ 408: public static View createHTMLView(JComponent c, String html) 409: { 410: // TODO: This might be wrong. Lets see if it turns out good when 411: // the javax.swing.text.html package is in a good shape. 412: HTMLDocument doc = new HTMLDocument(); 413: HTMLEditorKit kit = new HTMLEditorKit(); 414: StringReader reader = new StringReader(html); 415: try 416: { 417: kit.read(reader, doc, 0); 418: } 419: catch (IOException ex) 420: { 421: AssertionError err = new AssertionError("unexpected IOException"); 422: err.initCause(ex); 423: throw err; 424: } 425: catch (BadLocationException ex) 426: { 427: AssertionError err = 428: new AssertionError("unexpected BadLocationException"); 429: err.initCause(ex); 430: throw err; 431: } 432: ViewFactory vf = kit.getViewFactory(); 433: Element root = doc.getDefaultRootElement(); 434: View view = vf.create(root); 435: HTMLRootView rootView = new HTMLRootView(c, view, kit, doc); 436: return rootView; 437: } 438: 439: /** 440: * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code> 441: * otherwise. 442: * 443: * @param s the string to test 444: * 445: * @return <code>true</code> if <code>s</code> is HTML, <code>false</code> 446: * otherwise 447: */ 448: public static boolean isHTMLString(String s) 449: { 450: // We consider a string to be HTML if it contains both the '<' and '>' 451: // character at least once. 452: return (s != null) && s.contains("<") && s.contains(">"); 453: } 454: 455: /** 456: * Stores a HTML renderer in <code>c</code>'s client property if 457: * <code>text</code> is HTML, otherwise it clears the corresponding client 458: * property. This is useful for {@link javax.swing.plaf.ComponentUI} 459: * implementations that are shared between it's components. 460: * 461: * @param c the component to update the renderer for 462: * @param text the string to be rendered 463: */ 464: public static void updateRenderer(JComponent c, String text) 465: { 466: if (isHTMLString(text)) 467: c.putClientProperty(propertyKey, createHTMLView(c, text)); 468: else 469: c.putClientProperty(propertyKey, null); 470: } 471: }