This brings the ImageView together with the new async loading by making it more thread-safe.
2006-12-05 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/text/html/ImageView.java (imageUpdate): Use spans field to determine if the CSS width/height are set. Call safePreferenceChanged to protect view structure from threading issues. (spans): Made package private. (ImageView): Initialize loadOnDemand with false. (loadImage): Call Toolkit.prepareImage() to make sure we have our Observer registered. (safePreferenceChanged): New helper method. Calls preferenceChanged in a thread safe environment. /Roman
Index: javax/swing/text/html/ImageView.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/ImageView.java,v retrieving revision 1.5 diff -u -1 -5 -r1.5 ImageView.java --- javax/swing/text/html/ImageView.java 1 Dec 2006 14:43:43 -0000 1.5 +++ javax/swing/text/html/ImageView.java 5 Dec 2006 11:19:44 -0000 @@ -1,67 +1,67 @@ package javax.swing.text.html; -import gnu.javax.swing.text.html.CombinedAttributes; import gnu.javax.swing.text.html.ImageViewIconFactory; import gnu.javax.swing.text.html.css.Length; import java.awt.Graphics; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.image.ImageObserver; import java.net.MalformedURLException; import java.net.URL; import javax.swing.Icon; +import javax.swing.SwingUtilities; +import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.View; import javax.swing.text.Position.Bias; import javax.swing.text.html.HTML.Attribute; /** * A view, representing a single image, represented by the HTML IMG tag. * * @author Audrius Meskauskas ([EMAIL PROTECTED]) */ public class ImageView extends View { /** * Tracks image loading state and performs the necessary layout updates. */ class Observer implements ImageObserver { public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height) { boolean widthChanged = false; - if ((flags & ImageObserver.WIDTH) != 0 - && ! getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) + if ((flags & ImageObserver.WIDTH) != 0 && spans[X_AXIS] == null) widthChanged = true; boolean heightChanged = false; - if ((flags & ImageObserver.HEIGHT) != 0 - && ! getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) - widthChanged = true; + if ((flags & ImageObserver.HEIGHT) != 0 && spans[Y_AXIS] == null) + heightChanged = true; if (widthChanged || heightChanged) - preferenceChanged(ImageView.this, widthChanged, heightChanged); - return (flags & ALLBITS) != 0; + safePreferenceChanged(ImageView.this, widthChanged, heightChanged); + boolean ret = (flags & ALLBITS) != 0; + return ret; } } /** * True if the image loads synchronuosly (on demand). By default, the image * loads asynchronuosly. */ boolean loadOnDemand; /** * The image icon, wrapping the image, */ Image image; @@ -100,49 +100,52 @@ */ private int width; /** * The current height of the image. */ private int height; /** * Our ImageObserver for tracking the loading state. */ private ImageObserver observer; /** * The CSS width and height. + * + * Package private to avoid synthetic accessor methods. */ - private Length[] spans; + Length[] spans; /** * The cached attributes. */ private AttributeSet attributes; /** * Creates the image view that represents the given element. * * @param element the element, represented by this image view. */ public ImageView(Element element) { super(element); observer = new Observer(); reloadProperties = true; reloadImage = true; + loadOnDemand = false; } /** * Load or reload the image. This method initiates the image reloading. After * the image is ready, the repaint event will be scheduled. The current image, * if it already exists, will be discarded. */ private void reloadImage() { loading = true; reloadImage = false; haveWidth = false; haveHeight = false; image = null; width = 0; @@ -475,31 +478,33 @@ reloadImage(); if (reloadProperties) setPropertiesFromAttributes(); } /** * Actually loads the image. */ private void loadImage() { URL src = getImageURL(); Image newImage = null; if (src != null) { // Call getImage(URL) to allow the toolkit caching of that image URL. - newImage = Toolkit.getDefaultToolkit().getImage(src); + Toolkit tk = Toolkit.getDefaultToolkit(); + newImage = tk.getImage(src); + tk.prepareImage(newImage, -1, -1, observer); if (newImage != null && getLoadsSynchronously()) { // Load image synchronously. MediaTracker tracker = new MediaTracker(getContainer()); tracker.addImage(newImage, 0); try { tracker.waitForID(0); } catch (InterruptedException ex) { Thread.interrupted(); } } @@ -536,16 +541,54 @@ newH = (int) l.getValue(); haveHeight = true; } else { newW = newIm.getWidth(observer); } // Go and trigger loading. Toolkit tk = Toolkit.getDefaultToolkit(); if (haveWidth || haveHeight) tk.prepareImage(newIm, width, height, observer); else tk.prepareImage(newIm, -1, -1, observer); } } + + /** + * Calls preferenceChanged from the event dispatch thread and within + * a read lock to protect us from threading issues. + * + * @param v the view + * @param width true when the width changed + * @param height true when the height changed + */ + void safePreferenceChanged(final View v, final boolean width, + final boolean height) + { + if (SwingUtilities.isEventDispatchThread()) + { + Document doc = getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try + { + preferenceChanged(v, width, height); + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + } + else + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + safePreferenceChanged(v, width, height); + } + }); + } + } }