Frames | No Frames |
1: package javax.swing.text.html; 2: 3: import gnu.javax.swing.text.html.CombinedAttributes; 4: import gnu.javax.swing.text.html.ImageViewIconFactory; 5: 6: import java.awt.Graphics; 7: import java.awt.Image; 8: import java.awt.MediaTracker; 9: import java.awt.Rectangle; 10: import java.awt.Shape; 11: import java.net.MalformedURLException; 12: import java.net.URL; 13: 14: import javax.swing.Icon; 15: import javax.swing.ImageIcon; 16: import javax.swing.text.AttributeSet; 17: import javax.swing.text.BadLocationException; 18: import javax.swing.text.Document; 19: import javax.swing.text.Element; 20: import javax.swing.text.View; 21: import javax.swing.text.Position.Bias; 22: import javax.swing.text.html.HTML.Attribute; 23: 24: /** 25: * A view, representing a single image, represented by the HTML IMG tag. 26: * 27: * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 28: */ 29: public class ImageView extends View 30: { 31: /** 32: * True if the image loads synchronuosly (on demand). By default, the image 33: * loads asynchronuosly. 34: */ 35: boolean loadOnDemand; 36: 37: /** 38: * The image icon, wrapping the image, 39: */ 40: ImageIcon imageIcon; 41: 42: /** 43: * The image state. 44: */ 45: byte imageState = MediaTracker.LOADING; 46: 47: /** 48: * Creates the image view that represents the given element. 49: * 50: * @param element the element, represented by this image view. 51: */ 52: public ImageView(Element element) 53: { 54: super(element); 55: } 56: 57: /** 58: * Load or reload the image. This method initiates the image reloading. After 59: * the image is ready, the repaint event will be scheduled. The current image, 60: * if it already exists, will be discarded. 61: * 62: * @param itsTime 63: * also load if the "on demand" property is set 64: */ 65: void reloadImage(boolean itsTime) 66: { 67: URL url = getImageURL(); 68: if (url == null) 69: imageState = (byte) MediaTracker.ERRORED; 70: else if (!(loadOnDemand && !itsTime)) 71: imageIcon = new ImageIcon(url); 72: else 73: imageState = (byte) MediaTracker.LOADING; 74: } 75: 76: /** 77: * Get the image alignment. This method works handling standart alignment 78: * attributes in the HTML IMG tag (align = top bottom middle left right). 79: * Depending from the parameter, either horizontal or vertical alingment 80: * information is returned. 81: * 82: * @param axis - 83: * either X_AXIS or Y_AXIS 84: */ 85: public float getAlignment(int axis) 86: { 87: AttributeSet attrs = getAttributes(); 88: Object al = attrs.getAttribute(Attribute.ALIGN); 89: 90: // Default is top left aligned. 91: if (al == null) 92: return 0.0f; 93: 94: String align = al.toString(); 95: 96: if (axis == View.X_AXIS) 97: { 98: if (align.equals("middle")) 99: return 0.5f; 100: else if (align.equals("left")) 101: return 0.0f; 102: else if (align.equals("right")) 103: return 1.0f; 104: else 105: return 0.0f; 106: } 107: else if (axis == View.Y_AXIS) 108: { 109: if (align.equals("middle")) 110: return 0.5f; 111: else if (align.equals("top")) 112: return 0.0f; 113: else if (align.equals("bottom")) 114: return 1.0f; 115: else 116: return 0.0f; 117: } 118: else 119: throw new IllegalArgumentException("axis " + axis); 120: } 121: 122: /** 123: * Get the text that should be shown as the image replacement and also as the 124: * image tool tip text. The method returns the value of the attribute, having 125: * the name {@link Attribute#ALT}. If there is no such attribute, the image 126: * name from the url is returned. If the URL is not available, the empty 127: * string is returned. 128: */ 129: public String getAltText() 130: { 131: Object rt = getAttributes().getAttribute(Attribute.ALT); 132: if (rt != null) 133: return rt.toString(); 134: else 135: { 136: URL u = getImageURL(); 137: if (u == null) 138: return ""; 139: else 140: return u.getFile(); 141: } 142: } 143: 144: /** 145: * Returns the combination of the document and the style sheet attributes. 146: */ 147: public AttributeSet getAttributes() 148: { 149: StyleSheet styles = getStyleSheet(); 150: if (styles == null) 151: return super.getAttributes(); 152: else 153: return CombinedAttributes.combine(super.getAttributes(), 154: styles.getViewAttributes(this)); 155: } 156: 157: /** 158: * Get the image to render. May return null if the image is not yet loaded. 159: */ 160: public Image getImage() 161: { 162: if (imageIcon == null) 163: return null; 164: else 165: return imageIcon.getImage(); 166: } 167: 168: /** 169: * Get the URL location of the image to render. If this method returns null, 170: * the "no image" icon is rendered instead. By defaul, url must be present as 171: * the "src" property of the IMG tag. If it is missing, null is returned and 172: * the "no image" icon is rendered. 173: * 174: * @return the URL location of the image to render. 175: */ 176: public URL getImageURL() 177: { 178: Object url = getAttributes().getAttribute(Attribute.SRC); 179: if (url == null) 180: return null; 181: 182: try 183: { 184: return new URL(url.toString()); 185: } 186: catch (MalformedURLException e) 187: { 188: // The URL is malformed - no image. 189: return null; 190: } 191: } 192: 193: /** 194: * Get the icon that should be displayed while the image is loading and hence 195: * not yet available. 196: * 197: * @return an icon, showing a non broken sheet of paper with image. 198: */ 199: public Icon getLoadingImageIcon() 200: { 201: return ImageViewIconFactory.getLoadingImageIcon(); 202: } 203: 204: /** 205: * Get the image loading strategy. 206: * 207: * @return false (default) if the image is loaded when the view is 208: * constructed, true if the image is only loaded on demand when 209: * rendering. 210: */ 211: public boolean getLoadsSynchronously() 212: { 213: return loadOnDemand; 214: } 215: 216: /** 217: * Get the icon that should be displayed when the image is not available. 218: * 219: * @return an icon, showing a broken sheet of paper with image. 220: */ 221: public Icon getNoImageIcon() 222: { 223: return ImageViewIconFactory.getNoImageIcon(); 224: } 225: 226: /** 227: * Get the preferred span of the image along the axis. The image size is first 228: * requested to the attributes {@link Attribute#WIDTH} and 229: * {@link Attribute#HEIGHT}. If they are missing, and the image is already 230: * loaded, the image size is returned. If there are no attributes, and the 231: * image is not loaded, zero is returned. 232: * 233: * @param axis - 234: * either X_AXIS or Y_AXIS 235: * @return either width of height of the image, depending on the axis. 236: */ 237: public float getPreferredSpan(int axis) 238: { 239: AttributeSet attrs = getAttributes(); 240: 241: Image image = getImage(); 242: 243: if (axis == View.X_AXIS) 244: { 245: Object w = attrs.getAttribute(Attribute.WIDTH); 246: if (w != null) 247: return Integer.parseInt(w.toString()); 248: else if (image != null) 249: return image.getWidth(getContainer()); 250: else 251: return getNoImageIcon().getIconWidth(); 252: } 253: else if (axis == View.Y_AXIS) 254: { 255: Object w = attrs.getAttribute(Attribute.HEIGHT); 256: if (w != null) 257: return Integer.parseInt(w.toString()); 258: else if (image != null) 259: return image.getHeight(getContainer()); 260: else 261: return getNoImageIcon().getIconHeight(); 262: } 263: else 264: throw new IllegalArgumentException("axis " + axis); 265: } 266: 267: /** 268: * Get the associated style sheet from the document. 269: * 270: * @return the associated style sheet. 271: */ 272: protected StyleSheet getStyleSheet() 273: { 274: Document d = getElement().getDocument(); 275: if (d instanceof HTMLDocument) 276: return ((HTMLDocument) d).getStyleSheet(); 277: else 278: return null; 279: } 280: 281: /** 282: * Get the tool tip text. This is overridden to return the value of the 283: * {@link #getAltText()}. The parameters are ignored. 284: * 285: * @return that is returned by getAltText(). 286: */ 287: public String getToolTipText(float x, float y, Shape shape) 288: { 289: return getAltText(); 290: } 291: 292: /** 293: * Paints the image or one of the two image state icons. The image is resized 294: * to the shape bounds. If there is no image available, the alternative text 295: * is displayed besides the image state icon. 296: * 297: * @param g 298: * the Graphics, used for painting. 299: * @param bounds 300: * the bounds of the region where the image or replacing icon must be 301: * painted. 302: */ 303: public void paint(Graphics g, Shape bounds) 304: { 305: Rectangle r = bounds.getBounds(); 306: 307: if (imageIcon == null) 308: 309: { 310: // Loading image on demand, rendering the loading icon so far. 311: reloadImage(true); 312: 313: // The reloadImage sets the imageIcon, unless the URL is broken 314: // or malformed. 315: if (imageIcon != null) 316: { 317: if (imageIcon.getImageLoadStatus() != MediaTracker.COMPLETE) 318: { 319: // Render "not ready" icon, unless the image is ready 320: // immediately. 321: renderIcon(g, r, getLoadingImageIcon()); 322: // Add the listener to repaint when the icon will be ready. 323: imageIcon.setImageObserver(getContainer()); 324: return; 325: } 326: } 327: else 328: { 329: renderIcon(g, r, getNoImageIcon()); 330: return; 331: } 332: } 333: 334: imageState = (byte) imageIcon.getImageLoadStatus(); 335: 336: switch (imageState) 337: { 338: case MediaTracker.ABORTED: 339: case MediaTracker.ERRORED: 340: renderIcon(g, r, getNoImageIcon()); 341: break; 342: case MediaTracker.LOADING: 343: // If the image is not loaded completely, we still render it, as the 344: // partial image may be available. 345: case MediaTracker.COMPLETE: 346: { 347: // Paint the scaled image. 348: Image scaled = imageIcon.getImage().getScaledInstance( 349: r.width, 350: r.height, 351: Image.SCALE_DEFAULT); 352: ImageIcon painter = new ImageIcon(scaled); 353: painter.paintIcon(getContainer(), g, r.x, r.y); 354: } 355: break; 356: } 357: } 358: 359: /** 360: * Render "no image" icon and the alternative "no image" text. The text is 361: * rendered right from the icon and is aligned to the icon bottom. 362: */ 363: private void renderIcon(Graphics g, Rectangle bounds, Icon icon) 364: { 365: Shape current = g.getClip(); 366: try 367: { 368: g.setClip(bounds); 369: if (icon != null) 370: { 371: icon.paintIcon(getContainer(), g, bounds.x, bounds.y); 372: g.drawString(getAltText(), bounds.x + icon.getIconWidth(), 373: bounds.y + icon.getIconHeight()); 374: } 375: } 376: finally 377: { 378: g.setClip(current); 379: } 380: } 381: 382: /** 383: * Set if the image should be loaded only when needed (synchronuosly). By 384: * default, the image loads asynchronuosly. If the image is not yet ready, the 385: * icon, returned by the {@link #getLoadingImageIcon()}, is displayed. 386: */ 387: public void setLoadsSynchronously(boolean load_on_demand) 388: { 389: loadOnDemand = load_on_demand; 390: } 391: 392: /** 393: * Update all cached properties from the attribute set, returned by the 394: * {@link #getAttributes}. 395: */ 396: protected void setPropertiesFromAttributes() 397: { 398: // In the current implementation, nothing is cached yet, unless the image 399: // itself. 400: imageIcon = null; 401: } 402: 403: /** 404: * Maps the picture co-ordinates into the image position in the model. As the 405: * image is not divideable, this is currently implemented always to return the 406: * start offset. 407: */ 408: public int viewToModel(float x, float y, Shape shape, Bias[] bias) 409: { 410: return getStartOffset(); 411: } 412: 413: /** 414: * This is currently implemented always to return the area of the image view, 415: * as the image is not divideable by character positions. 416: * 417: * @param pos character position 418: * @param area of the image view 419: * @param bias bias 420: * 421: * @return the shape, where the given character position should be mapped. 422: */ 423: public Shape modelToView(int pos, Shape area, Bias bias) 424: throws BadLocationException 425: { 426: return area; 427: } 428: 429: /** 430: * Starts loading the image asynchronuosly. If the image must be loaded 431: * synchronuosly instead, the {@link #setLoadsSynchronously} must be 432: * called before calling this method. The passed parameters are not used. 433: */ 434: public void setSize(float width, float height) 435: { 436: if (imageIcon == null) 437: reloadImage(false); 438: } 439: 440: 441: }