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);
+ }
+ });
+ }
+ }
}