Merged gcj-eclipse branch to trunk.
From-SVN: r120621
This commit is contained in:
parent
c648dedbde
commit
97b8365caf
17478 changed files with 606493 additions and 100744 deletions
|
@ -1,18 +1,21 @@
|
|||
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.ImageIcon;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.text.AbstractDocument;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
|
@ -28,22 +31,97 @@ import javax.swing.text.html.HTML.Attribute;
|
|||
*/
|
||||
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 && spans[X_AXIS] == null)
|
||||
widthChanged = true;
|
||||
boolean heightChanged = false;
|
||||
if ((flags & ImageObserver.HEIGHT) != 0 && spans[Y_AXIS] == null)
|
||||
heightChanged = true;
|
||||
if (widthChanged || heightChanged)
|
||||
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,
|
||||
*/
|
||||
ImageIcon imageIcon;
|
||||
Image image;
|
||||
|
||||
/**
|
||||
* The image state.
|
||||
*/
|
||||
byte imageState = MediaTracker.LOADING;
|
||||
|
||||
/**
|
||||
* True when the image needs re-loading, false otherwise.
|
||||
*/
|
||||
private boolean reloadImage;
|
||||
|
||||
/**
|
||||
* True when the image properties need re-loading, false otherwise.
|
||||
*/
|
||||
private boolean reloadProperties;
|
||||
|
||||
/**
|
||||
* True when the width is set as CSS/HTML attribute.
|
||||
*/
|
||||
private boolean haveWidth;
|
||||
|
||||
/**
|
||||
* True when the height is set as CSS/HTML attribute.
|
||||
*/
|
||||
private boolean haveHeight;
|
||||
|
||||
/**
|
||||
* True when the image is currently loading.
|
||||
*/
|
||||
private boolean loading;
|
||||
|
||||
/**
|
||||
* The current width of the image.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
Length[] spans;
|
||||
|
||||
/**
|
||||
* The cached attributes.
|
||||
*/
|
||||
private AttributeSet attributes;
|
||||
|
||||
/**
|
||||
* Creates the image view that represents the given element.
|
||||
*
|
||||
|
@ -52,25 +130,36 @@ public class ImageView extends View
|
|||
public ImageView(Element element)
|
||||
{
|
||||
super(element);
|
||||
spans = new Length[2];
|
||||
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.
|
||||
*
|
||||
* @param itsTime
|
||||
* also load if the "on demand" property is set
|
||||
*/
|
||||
void reloadImage(boolean itsTime)
|
||||
private void reloadImage()
|
||||
{
|
||||
URL url = getImageURL();
|
||||
if (url == null)
|
||||
imageState = (byte) MediaTracker.ERRORED;
|
||||
else if (!(loadOnDemand && !itsTime))
|
||||
imageIcon = new ImageIcon(url);
|
||||
else
|
||||
imageState = (byte) MediaTracker.LOADING;
|
||||
loading = true;
|
||||
reloadImage = false;
|
||||
haveWidth = false;
|
||||
haveHeight = false;
|
||||
image = null;
|
||||
width = 0;
|
||||
height = 0;
|
||||
try
|
||||
{
|
||||
loadImage();
|
||||
updateSize();
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,12 +235,9 @@ public class ImageView extends View
|
|||
*/
|
||||
public AttributeSet getAttributes()
|
||||
{
|
||||
StyleSheet styles = getStyleSheet();
|
||||
if (styles == null)
|
||||
return super.getAttributes();
|
||||
else
|
||||
return CombinedAttributes.combine(super.getAttributes(),
|
||||
styles.getViewAttributes(this));
|
||||
if (attributes == null)
|
||||
attributes = getStyleSheet().getViewAttributes(this);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,10 +245,8 @@ public class ImageView extends View
|
|||
*/
|
||||
public Image getImage()
|
||||
{
|
||||
if (imageIcon == null)
|
||||
return null;
|
||||
else
|
||||
return imageIcon.getImage();
|
||||
updateState();
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,19 +259,22 @@ public class ImageView extends View
|
|||
*/
|
||||
public URL getImageURL()
|
||||
{
|
||||
Object url = getAttributes().getAttribute(Attribute.SRC);
|
||||
if (url == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
Element el = getElement();
|
||||
String src = (String) el.getAttributes().getAttribute(Attribute.SRC);
|
||||
URL url = null;
|
||||
if (src != null)
|
||||
{
|
||||
return new URL(url.toString());
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
// The URL is malformed - no image.
|
||||
return null;
|
||||
URL base = ((HTMLDocument) getDocument()).getBase();
|
||||
try
|
||||
{
|
||||
url = new URL(base, src);
|
||||
}
|
||||
catch (MalformedURLException ex)
|
||||
{
|
||||
// Return null.
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,9 +329,8 @@ public class ImageView extends View
|
|||
|
||||
if (axis == View.X_AXIS)
|
||||
{
|
||||
Object w = attrs.getAttribute(Attribute.WIDTH);
|
||||
if (w != null)
|
||||
return Integer.parseInt(w.toString());
|
||||
if (spans[axis] != null)
|
||||
return spans[axis].getValue();
|
||||
else if (image != null)
|
||||
return image.getWidth(getContainer());
|
||||
else
|
||||
|
@ -252,9 +338,8 @@ public class ImageView extends View
|
|||
}
|
||||
else if (axis == View.Y_AXIS)
|
||||
{
|
||||
Object w = attrs.getAttribute(Attribute.HEIGHT);
|
||||
if (w != null)
|
||||
return Integer.parseInt(w.toString());
|
||||
if (spans[axis] != null)
|
||||
return spans[axis].getValue();
|
||||
else if (image != null)
|
||||
return image.getHeight(getContainer());
|
||||
else
|
||||
|
@ -271,11 +356,8 @@ public class ImageView extends View
|
|||
*/
|
||||
protected StyleSheet getStyleSheet()
|
||||
{
|
||||
Document d = getElement().getDocument();
|
||||
if (d instanceof HTMLDocument)
|
||||
return ((HTMLDocument) d).getStyleSheet();
|
||||
else
|
||||
return null;
|
||||
HTMLDocument doc = (HTMLDocument) getDocument();
|
||||
return doc.getStyleSheet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,7 +370,7 @@ public class ImageView extends View
|
|||
{
|
||||
return getAltText();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paints the image or one of the two image state icons. The image is resized
|
||||
* to the shape bounds. If there is no image available, the alternative text
|
||||
|
@ -302,83 +384,22 @@ public class ImageView extends View
|
|||
*/
|
||||
public void paint(Graphics g, Shape bounds)
|
||||
{
|
||||
Rectangle r = bounds.getBounds();
|
||||
|
||||
if (imageIcon == null)
|
||||
|
||||
updateState();
|
||||
Rectangle r = bounds instanceof Rectangle ? (Rectangle) bounds
|
||||
: bounds.getBounds();
|
||||
Image image = getImage();
|
||||
if (image != null)
|
||||
{
|
||||
// Loading image on demand, rendering the loading icon so far.
|
||||
reloadImage(true);
|
||||
|
||||
// The reloadImage sets the imageIcon, unless the URL is broken
|
||||
// or malformed.
|
||||
if (imageIcon != null)
|
||||
{
|
||||
if (imageIcon.getImageLoadStatus() != MediaTracker.COMPLETE)
|
||||
{
|
||||
// Render "not ready" icon, unless the image is ready
|
||||
// immediately.
|
||||
renderIcon(g, r, getLoadingImageIcon());
|
||||
// Add the listener to repaint when the icon will be ready.
|
||||
imageIcon.setImageObserver(getContainer());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
renderIcon(g, r, getNoImageIcon());
|
||||
return;
|
||||
}
|
||||
g.drawImage(image, r.x, r.y, r.width, r.height, observer);
|
||||
}
|
||||
|
||||
imageState = (byte) imageIcon.getImageLoadStatus();
|
||||
|
||||
switch (imageState)
|
||||
else
|
||||
{
|
||||
case MediaTracker.ABORTED:
|
||||
case MediaTracker.ERRORED:
|
||||
renderIcon(g, r, getNoImageIcon());
|
||||
break;
|
||||
case MediaTracker.LOADING:
|
||||
// If the image is not loaded completely, we still render it, as the
|
||||
// partial image may be available.
|
||||
case MediaTracker.COMPLETE:
|
||||
{
|
||||
// Paint the scaled image.
|
||||
Image scaled = imageIcon.getImage().getScaledInstance(
|
||||
r.width,
|
||||
r.height,
|
||||
Image.SCALE_DEFAULT);
|
||||
ImageIcon painter = new ImageIcon(scaled);
|
||||
painter.paintIcon(getContainer(), g, r.x, r.y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render "no image" icon and the alternative "no image" text. The text is
|
||||
* rendered right from the icon and is aligned to the icon bottom.
|
||||
*/
|
||||
private void renderIcon(Graphics g, Rectangle bounds, Icon icon)
|
||||
{
|
||||
Shape current = g.getClip();
|
||||
try
|
||||
{
|
||||
g.setClip(bounds);
|
||||
Icon icon = getNoImageIcon();
|
||||
if (icon != null)
|
||||
{
|
||||
icon.paintIcon(getContainer(), g, bounds.x, bounds.y);
|
||||
g.drawString(getAltText(), bounds.x + icon.getIconWidth(),
|
||||
bounds.y + icon.getIconHeight());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
g.setClip(current);
|
||||
icon.paintIcon(getContainer(), g, r.x, r.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set if the image should be loaded only when needed (synchronuosly). By
|
||||
* default, the image loads asynchronuosly. If the image is not yet ready, the
|
||||
|
@ -395,9 +416,20 @@ public class ImageView extends View
|
|||
*/
|
||||
protected void setPropertiesFromAttributes()
|
||||
{
|
||||
// In the current implementation, nothing is cached yet, unless the image
|
||||
// itself.
|
||||
imageIcon = null;
|
||||
AttributeSet atts = getAttributes();
|
||||
StyleSheet ss = getStyleSheet();
|
||||
float emBase = ss.getEMBase(atts);
|
||||
float exBase = ss.getEXBase(atts);
|
||||
spans[X_AXIS] = (Length) atts.getAttribute(CSS.Attribute.WIDTH);
|
||||
if (spans[X_AXIS] != null)
|
||||
{
|
||||
spans[X_AXIS].setFontBases(emBase, exBase);
|
||||
}
|
||||
spans[Y_AXIS] = (Length) atts.getAttribute(CSS.Attribute.HEIGHT);
|
||||
if (spans[Y_AXIS] != null)
|
||||
{
|
||||
spans[Y_AXIS].setFontBases(emBase, exBase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -433,9 +465,130 @@ public class ImageView extends View
|
|||
*/
|
||||
public void setSize(float width, float height)
|
||||
{
|
||||
if (imageIcon == null)
|
||||
reloadImage(false);
|
||||
updateState();
|
||||
// TODO: Implement this when we have an alt view for the alt=... attribute.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This makes sure that the image and properties have been loaded.
|
||||
*/
|
||||
private void updateState()
|
||||
{
|
||||
if (reloadImage)
|
||||
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.
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
image = newImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the size parameters of the image.
|
||||
*/
|
||||
private void updateSize()
|
||||
{
|
||||
int newW = 0;
|
||||
int newH = 0;
|
||||
Image newIm = getImage();
|
||||
if (newIm != null)
|
||||
{
|
||||
AttributeSet atts = getAttributes();
|
||||
// Fetch width.
|
||||
Length l = spans[X_AXIS];
|
||||
if (l != null)
|
||||
{
|
||||
newW = (int) l.getValue();
|
||||
haveWidth = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
newW = newIm.getWidth(observer);
|
||||
}
|
||||
// Fetch height.
|
||||
l = spans[Y_AXIS];
|
||||
if (l != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue