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.BadLocationException; 52: import javax.swing.text.Document; 53: import javax.swing.text.EditorKit; 54: import javax.swing.text.Element; 55: import javax.swing.text.Position; 56: import javax.swing.text.View; 57: import javax.swing.text.ViewFactory; 58: import javax.swing.text.html.HTMLDocument; 59: import javax.swing.text.html.HTMLEditorKit; 60: 61: /** 62: * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI} 63: * implementations. 64: * 65: * @author Roman Kennke (kennke@aicas.com) 66: */ 67: public class BasicHTML 68: { 69: 70: /** 71: * This class serves as the root view for HTML rendering components. 72: * Its purpose and implementation is similar to the BasicTextUI.RootView 73: * class, only that is implements some stuff differently due to the nature 74: * of not beeing inside a JTextComponent. 75: * 76: * @author Roman Kennke (kennke@aicas.com) 77: */ 78: private static class HTMLRootView extends View 79: { 80: /** 81: * The real root view. 82: */ 83: private View view; 84: 85: /** 86: * The component on which to render the view. 87: */ 88: private JComponent component; 89: 90: /** 91: * The EditorKit. 92: */ 93: private EditorKit editorKit; 94: 95: /** 96: * The document to use. 97: */ 98: private Document document; 99: 100: /** 101: * Creates a new RootView. 102: */ 103: public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc) 104: { 105: super(null); 106: component = c; 107: editorKit = kit; 108: document = doc; 109: setView(view); 110: } 111: 112: /** 113: * Returns the ViewFactory for this RootView. If the current EditorKit 114: * provides a ViewFactory, this is used. Otherwise the TextUI itself 115: * is returned as a ViewFactory. 116: * 117: * @return the ViewFactory for this RootView 118: */ 119: public ViewFactory getViewFactory() 120: { 121: return editorKit.getViewFactory(); 122: } 123: 124: /** 125: * Indicates that the preferences of one of the child view has changed. 126: * This calls revalidate on the text component. 127: * 128: * @param v the child view which's preference has changed 129: * @param width <code>true</code> if the width preference has changed 130: * @param height <code>true</code> if the height preference has changed 131: */ 132: public void preferenceChanged(View v, boolean width, boolean height) 133: { 134: component.revalidate(); 135: } 136: 137: /** 138: * Sets the real root view. 139: * 140: * @param v the root view to set 141: */ 142: public void setView(View v) 143: { 144: if (view != null) 145: view.setParent(null); 146: 147: if (v != null) 148: v.setParent(this); 149: 150: view = v; 151: } 152: 153: /** 154: * Returns the real root view, regardless of the index. 155: * 156: * @param index not used here 157: * 158: * @return the real root view, regardless of the index. 159: */ 160: public View getView(int index) 161: { 162: return view; 163: } 164: 165: /** 166: * Returns <code>1</code> since the RootView always contains one 167: * child, that is the real root of the View hierarchy. 168: * 169: * @return <code>1</code> since the RootView always contains one 170: * child, that is the real root of the View hierarchy 171: */ 172: public int getViewCount() 173: { 174: int count = 0; 175: if (view != null) 176: count = 1; 177: return count; 178: } 179: 180: /** 181: * Returns the <code>Container</code> that contains this view. This 182: * normally will be the text component that is managed by this TextUI. 183: * 184: * @return the <code>Container</code> that contains this view 185: */ 186: public Container getContainer() 187: { 188: return component; 189: } 190: 191: /** 192: * Returns the preferred span along the specified <code>axis</code>. 193: * This is delegated to the real root view. 194: * 195: * @param axis the axis for which the preferred span is queried 196: * 197: * @return the preferred span along the axis 198: */ 199: public float getPreferredSpan(int axis) 200: { 201: if (view != null) 202: return view.getPreferredSpan(axis); 203: 204: return Integer.MAX_VALUE; 205: } 206: 207: /** 208: * Paints the view. This is delegated to the real root view. 209: * 210: * @param g the <code>Graphics</code> context to paint to 211: * @param s the allocation for the View 212: */ 213: public void paint(Graphics g, Shape s) 214: { 215: if (view != null) 216: { 217: Rectangle b = s.getBounds(); 218: view.setSize(b.width, b.height); 219: view.paint(g, s); 220: } 221: } 222: 223: 224: /** 225: * Maps a position in the document into the coordinate space of the View. 226: * The output rectangle usually reflects the font height but has a width 227: * of zero. 228: * 229: * This is delegated to the real root view. 230: * 231: * @param position the position of the character in the model 232: * @param a the area that is occupied by the view 233: * @param bias either {@link Position.Bias#Forward} or 234: * {@link Position.Bias#Backward} depending on the preferred 235: * direction bias. If <code>null</code> this defaults to 236: * <code>Position.Bias.Forward</code> 237: * 238: * @return a rectangle that gives the location of the document position 239: * inside the view coordinate space 240: * 241: * @throws BadLocationException if <code>pos</code> is invalid 242: * @throws IllegalArgumentException if b is not one of the above listed 243: * valid values 244: */ 245: public Shape modelToView(int position, Shape a, Position.Bias bias) 246: throws BadLocationException 247: { 248: return view.modelToView(position, a, bias); 249: } 250: 251: /** 252: * Maps coordinates from the <code>View</code>'s space into a position 253: * in the document model. 254: * 255: * @param x the x coordinate in the view space 256: * @param y the y coordinate in the view space 257: * @param a the allocation of this <code>View</code> 258: * @param b the bias to use 259: * 260: * @return the position in the document that corresponds to the screen 261: * coordinates <code>x, y</code> 262: */ 263: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 264: { 265: return view.viewToModel(x, y, a, b); 266: } 267: 268: /** 269: * Notification about text insertions. These are forwarded to the 270: * real root view. 271: * 272: * @param ev the DocumentEvent describing the change 273: * @param shape the current allocation of the view's display 274: * @param vf the ViewFactory to use for creating new Views 275: */ 276: public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 277: { 278: view.insertUpdate(ev, shape, vf); 279: } 280: 281: /** 282: * Notification about text removals. These are forwarded to the 283: * real root view. 284: * 285: * @param ev the DocumentEvent describing the change 286: * @param shape the current allocation of the view's display 287: * @param vf the ViewFactory to use for creating new Views 288: */ 289: public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 290: { 291: view.removeUpdate(ev, shape, vf); 292: } 293: 294: /** 295: * Notification about text changes. These are forwarded to the 296: * real root view. 297: * 298: * @param ev the DocumentEvent describing the change 299: * @param shape the current allocation of the view's display 300: * @param vf the ViewFactory to use for creating new Views 301: */ 302: public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 303: { 304: view.changedUpdate(ev, shape, vf); 305: } 306: 307: /** 308: * Returns the document position that is (visually) nearest to the given 309: * document position <code>pos</code> in the given direction <code>d</code>. 310: * 311: * @param pos the document position 312: * @param b the bias for <code>pos</code> 313: * @param a the allocation for the view 314: * @param d the direction, must be either {@link SwingConstants#NORTH}, 315: * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or 316: * {@link SwingConstants#EAST} 317: * @param biasRet an array of {@link Position.Bias} that can hold at least 318: * one element, which is filled with the bias of the return position 319: * on method exit 320: * 321: * @return the document position that is (visually) nearest to the given 322: * document position <code>pos</code> in the given direction 323: * <code>d</code> 324: * 325: * @throws BadLocationException if <code>pos</code> is not a valid offset in 326: * the document model 327: */ 328: public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 329: int d, Position.Bias[] biasRet) 330: throws BadLocationException 331: { 332: return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); 333: } 334: 335: public int getStartOffset() 336: { 337: return 0; 338: } 339: 340: public int getEndOffset() 341: { 342: return getDocument().getLength(); 343: } 344: 345: public Document getDocument() 346: { 347: return document; 348: } 349: } 350: 351: /** 352: * The key that is used to store a HTML view in a JComponent's client 353: * properties. 354: */ 355: public static final String propertyKey = "html"; 356: 357: /** 358: * The key that is used to store the document base in a JComponent's client 359: * properties. The document base is used to resolve relative references 360: * in HTML. 361: */ 362: public static final String documentBaseKey = "html.base"; 363: 364: /** 365: * Creates a new instance of BasicHTML. This should not be necessary since 366: * all methods in this class are static. 367: */ 368: public BasicHTML() 369: { 370: // Nothing to do here. 371: } 372: 373: /** 374: * Creates a {@link View} instance that can be used by the component 375: * <code>c</code> to render the HTML string <code>html</code>. 376: * 377: * @param c the component that needs to render the HTML string 378: * @param html the HTML string to be rendered 379: * 380: * @return a view that can render the HTML string 381: */ 382: public static View createHTMLView(JComponent c, String html) 383: { 384: // TODO: This might be wrong. Lets see if it turns out good when 385: // the javax.swing.text.html package is in a good shape. 386: HTMLDocument doc = new HTMLDocument(); 387: HTMLEditorKit kit = new HTMLEditorKit(); 388: StringReader reader = new StringReader(html); 389: try 390: { 391: kit.read(reader, doc, 0); 392: } 393: catch (IOException ex) 394: { 395: AssertionError err = new AssertionError("unexpected IOException"); 396: err.initCause(ex); 397: throw err; 398: } 399: catch (BadLocationException ex) 400: { 401: AssertionError err = 402: new AssertionError("unexpected BadLocationException"); 403: err.initCause(ex); 404: throw err; 405: } 406: ViewFactory vf = kit.getViewFactory(); 407: Element root = doc.getDefaultRootElement(); 408: View view = vf.create(root); 409: HTMLRootView rootView = new HTMLRootView(c, view, kit, doc); 410: return rootView; 411: } 412: 413: /** 414: * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code> 415: * otherwise. 416: * 417: * @param s the string to test 418: * 419: * @return <code>true</code> if <code>s</code> is HTML, <code>false</code> 420: * otherwise 421: */ 422: public static boolean isHTMLString(String s) 423: { 424: // We consider a string to be HTML if it contains both the '<' and '>' 425: // character at least once. 426: return (s != null) && s.contains("<") && s.contains(">"); 427: } 428: 429: /** 430: * Stores a HTML renderer in <code>c</code>'s client property if 431: * <code>text</code> is HTML, otherwise it clears the corresponding client 432: * property. This is useful for {@link javax.swing.plaf.ComponentUI} 433: * implementations that are shared between it's components. 434: * 435: * @param c the component to update the renderer for 436: * @param text the string to be rendered 437: */ 438: public static void updateRenderer(JComponent c, String text) 439: { 440: if (isHTMLString(text)) 441: c.putClientProperty(propertyKey, createHTMLView(c, text)); 442: else 443: c.putClientProperty(propertyKey, null); 444: } 445: }