Source for javax.swing.text.html.ImageView

   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: }