|
|
|
|
@ -19,6 +19,8 @@
|
|
|
|
|
import javax.swing.text.*;
|
|
|
|
|
import javax.swing.text.html.*;
|
|
|
|
|
|
|
|
|
|
import net.java.sip.communicator.util.*;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The content of this file was based on code borrowed from Rob Kenworthy,
|
|
|
|
|
* http://www.javaworld.com/javaworld/javatips/jw-javatip109.html#resources.
|
|
|
|
|
@ -26,25 +28,34 @@
|
|
|
|
|
/**
|
|
|
|
|
* The <tt>SIPCommImageView</tt> is an <tt>ImageView</tt> which allows to
|
|
|
|
|
* specify a related path when describing an image within an html document.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @author Yana Stamcheva
|
|
|
|
|
*/
|
|
|
|
|
public class SIPCommImageView extends ImageView
|
|
|
|
|
implements ImageObserver {
|
|
|
|
|
|
|
|
|
|
public class SIPCommImageView
|
|
|
|
|
extends ImageView
|
|
|
|
|
implements ImageObserver
|
|
|
|
|
{
|
|
|
|
|
private Logger logger = Logger.getLogger(SIPCommImageView.class);
|
|
|
|
|
|
|
|
|
|
public static final String TOP = "top";
|
|
|
|
|
|
|
|
|
|
public static final String TEXTTOP = "texttop";
|
|
|
|
|
|
|
|
|
|
public static final String MIDDLE = "middle";
|
|
|
|
|
|
|
|
|
|
public static final String ABSMIDDLE = "absmiddle";
|
|
|
|
|
|
|
|
|
|
public static final String CENTER = "center";
|
|
|
|
|
|
|
|
|
|
public static final String BOTTOM = "bottom";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new view that represents an IMG element.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @param elem the element to create a view for.
|
|
|
|
|
*/
|
|
|
|
|
public SIPCommImageView(Element elem) {
|
|
|
|
|
public SIPCommImageView(Element elem)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
super(elem);
|
|
|
|
|
|
|
|
|
|
@ -57,11 +68,13 @@ public SIPCommImageView(Element elem) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initializes this view.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @param elem the element.
|
|
|
|
|
*/
|
|
|
|
|
private void initialize(Element elem) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
private void initialize(Element elem)
|
|
|
|
|
{
|
|
|
|
|
synchronized (this)
|
|
|
|
|
{
|
|
|
|
|
loading = true;
|
|
|
|
|
fWidth = fHeight = 0;
|
|
|
|
|
}
|
|
|
|
|
@ -71,47 +84,63 @@ private void initialize(Element elem) {
|
|
|
|
|
boolean customWidth = false;
|
|
|
|
|
boolean customHeight = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
fElement = elem;
|
|
|
|
|
|
|
|
|
|
// Request image from document's cache:
|
|
|
|
|
AttributeSet attr = elem.getAttributes();
|
|
|
|
|
|
|
|
|
|
if (isURL()) {
|
|
|
|
|
if (isURL())
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
URL src = getSourceURL();
|
|
|
|
|
|
|
|
|
|
if (src != null) {
|
|
|
|
|
if (src != null)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
Dictionary cache = (Dictionary) getDocument().getProperty(
|
|
|
|
|
IMAGE_CACHE_PROPERTY);
|
|
|
|
|
IMAGE_CACHE_PROPERTY);
|
|
|
|
|
|
|
|
|
|
if (cache != null)
|
|
|
|
|
fImage = (Image) cache.get(src);
|
|
|
|
|
else
|
|
|
|
|
fImage = Toolkit.getDefaultToolkit().getImage(src);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/*--Code to load from relative path--*/
|
|
|
|
|
String src = (String) fElement.getAttributes().getAttribute(
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
|
|
|
|
|
// fImage = Toolkit.getDefaultToolkit().createImage(src);
|
|
|
|
|
try {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
fImage = ImageIO.read(ImageLoader.class.getClassLoader()
|
|
|
|
|
.getResource(src));
|
|
|
|
|
.getResource(src));
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
waitForImage();
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
}
|
|
|
|
|
catch (InterruptedException e)
|
|
|
|
|
{
|
|
|
|
|
fImage = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
/*----------------------------------*/
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
{
|
|
|
|
|
fImage = null;
|
|
|
|
|
logger.error("Failed to read image.", e);
|
|
|
|
|
}
|
|
|
|
|
catch (IllegalArgumentException iae)
|
|
|
|
|
{
|
|
|
|
|
fImage = null;
|
|
|
|
|
logger.error("Failed to read image.", iae);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get height/width from params or image or defaults:
|
|
|
|
|
@ -139,51 +168,59 @@ private void initialize(Element elem) {
|
|
|
|
|
if (fImage != null)
|
|
|
|
|
if (customWidth && customHeight)
|
|
|
|
|
Toolkit.getDefaultToolkit().prepareImage(fImage, height,
|
|
|
|
|
width, this);
|
|
|
|
|
width, this);
|
|
|
|
|
else
|
|
|
|
|
Toolkit.getDefaultToolkit().prepareImage(fImage, -1, -1,
|
|
|
|
|
this);
|
|
|
|
|
} finally {
|
|
|
|
|
this);
|
|
|
|
|
} finally
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
synchronized (this)
|
|
|
|
|
{
|
|
|
|
|
loading = false;
|
|
|
|
|
|
|
|
|
|
if (customWidth || fWidth == 0) {
|
|
|
|
|
if (customWidth || fWidth == 0)
|
|
|
|
|
{
|
|
|
|
|
fWidth = width;
|
|
|
|
|
}
|
|
|
|
|
if (customHeight || fHeight == 0) {
|
|
|
|
|
if (customHeight || fHeight == 0)
|
|
|
|
|
{
|
|
|
|
|
fHeight = height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines if path is in the form of an URL.
|
|
|
|
|
*
|
|
|
|
|
* @return TRUE if the source is URL, FALSE otherwise.
|
|
|
|
|
*/
|
|
|
|
|
private boolean isURL() {
|
|
|
|
|
private boolean isURL()
|
|
|
|
|
{
|
|
|
|
|
String src = (String) fElement.getAttributes().getAttribute(
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
|
|
|
|
|
return src.toLowerCase().startsWith("file")
|
|
|
|
|
|| src.toLowerCase().startsWith("http");
|
|
|
|
|
|| src.toLowerCase().startsWith("http");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Make sure an image is loaded - ie no broken images. So far its used only
|
|
|
|
|
* for images loaded off the disk (non-URL).
|
|
|
|
|
*
|
|
|
|
|
* @throws InterruptedException
|
|
|
|
|
*/
|
|
|
|
|
private void waitForImage() throws InterruptedException {
|
|
|
|
|
private void waitForImage() throws InterruptedException
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
int w = fImage.getWidth(this);
|
|
|
|
|
int h = fImage.getHeight(this);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
int flags = Toolkit.getDefaultToolkit().checkImage(fImage, w, h,
|
|
|
|
|
this);
|
|
|
|
|
this);
|
|
|
|
|
|
|
|
|
|
if (((flags & ERROR) != 0) || ((flags & ABORT) != 0))
|
|
|
|
|
throw new InterruptedException();
|
|
|
|
|
@ -199,7 +236,8 @@ else if ((flags & (ALLBITS | FRAMEBITS)) != 0)
|
|
|
|
|
* Fetches the attributes to use when rendering. This is implemented to
|
|
|
|
|
* multiplex the attributes specified in the model with a StyleSheet.
|
|
|
|
|
*/
|
|
|
|
|
public AttributeSet getAttributes() {
|
|
|
|
|
public AttributeSet getAttributes()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return attr;
|
|
|
|
|
}
|
|
|
|
|
@ -207,7 +245,8 @@ public AttributeSet getAttributes() {
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the image is within a link.
|
|
|
|
|
*/
|
|
|
|
|
boolean isLink() {
|
|
|
|
|
boolean isLink()
|
|
|
|
|
{
|
|
|
|
|
// ! It would be nice to cache this but in an editor it can change
|
|
|
|
|
// See if I have an HREF attribute courtesy of the enclosing A tag:
|
|
|
|
|
|
|
|
|
|
@ -215,7 +254,8 @@ boolean isLink() {
|
|
|
|
|
|
|
|
|
|
fElement.getAttributes().getAttribute(HTML.Tag.A);
|
|
|
|
|
|
|
|
|
|
if (anchorAttr != null) {
|
|
|
|
|
if (anchorAttr != null)
|
|
|
|
|
{
|
|
|
|
|
return anchorAttr.isDefined(HTML.Attribute.HREF);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
@ -224,22 +264,25 @@ boolean isLink() {
|
|
|
|
|
/**
|
|
|
|
|
* Returns the size of the border to use.
|
|
|
|
|
*/
|
|
|
|
|
int getBorder() {
|
|
|
|
|
int getBorder()
|
|
|
|
|
{
|
|
|
|
|
return getIntAttr(HTML.Attribute.BORDER, isLink() ? DEFAULT_BORDER : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the amount of extra space to add along an axis.
|
|
|
|
|
*/
|
|
|
|
|
int getSpace(int axis) {
|
|
|
|
|
int getSpace(int axis)
|
|
|
|
|
{
|
|
|
|
|
return getIntAttr(axis == X_AXIS ? HTML.Attribute.HSPACE
|
|
|
|
|
: HTML.Attribute.VSPACE, 0);
|
|
|
|
|
: HTML.Attribute.VSPACE, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the border's color, or null if this is not a link.
|
|
|
|
|
*/
|
|
|
|
|
Color getBorderColor() {
|
|
|
|
|
Color getBorderColor()
|
|
|
|
|
{
|
|
|
|
|
StyledDocument doc = (StyledDocument) getDocument();
|
|
|
|
|
return doc.getForeground(getAttributes());
|
|
|
|
|
}
|
|
|
|
|
@ -247,15 +290,17 @@ Color getBorderColor() {
|
|
|
|
|
/**
|
|
|
|
|
* Returns the image's vertical alignment.
|
|
|
|
|
*/
|
|
|
|
|
float getVerticalAlignment() {
|
|
|
|
|
float getVerticalAlignment()
|
|
|
|
|
{
|
|
|
|
|
String align = (String) fElement.getAttributes().getAttribute(
|
|
|
|
|
HTML.Attribute.ALIGN);
|
|
|
|
|
if (align != null) {
|
|
|
|
|
HTML.Attribute.ALIGN);
|
|
|
|
|
if (align != null)
|
|
|
|
|
{
|
|
|
|
|
align = align.toLowerCase();
|
|
|
|
|
if (align.equals(TOP) || align.equals(TEXTTOP))
|
|
|
|
|
return 0.0f;
|
|
|
|
|
else if (align.equals(this.CENTER) || align.equals(MIDDLE)
|
|
|
|
|
|| align.equals(ABSMIDDLE))
|
|
|
|
|
|| align.equals(ABSMIDDLE))
|
|
|
|
|
return 0.5f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -264,32 +309,38 @@ else if (align.equals(this.CENTER) || align.equals(MIDDLE)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the image is at least one pixel.
|
|
|
|
|
*
|
|
|
|
|
* @param obs The <tt>ImageObserver</tt>.
|
|
|
|
|
* @return <code>true</code> if the image is not null and is at least one
|
|
|
|
|
* pixel big, <code>false</code> otherwise.
|
|
|
|
|
* pixel big, <code>false</code> otherwise.
|
|
|
|
|
*/
|
|
|
|
|
boolean hasPixels(ImageObserver obs) {
|
|
|
|
|
boolean hasPixels(ImageObserver obs)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return fImage != null && fImage.getHeight(obs) > 0
|
|
|
|
|
&& fImage.getWidth(obs) > 0;
|
|
|
|
|
&& fImage.getWidth(obs) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an URL for the image source, or null if it could not be
|
|
|
|
|
* determined.
|
|
|
|
|
*/
|
|
|
|
|
private URL getSourceURL() {
|
|
|
|
|
private URL getSourceURL()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
String src = (String) fElement.getAttributes().getAttribute(
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
HTML.Attribute.SRC);
|
|
|
|
|
if (src == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
URL reference = ((HTMLDocument) getDocument()).getBase();
|
|
|
|
|
try {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
URL u = new URL(reference, src);
|
|
|
|
|
return u;
|
|
|
|
|
} catch (MalformedURLException e) {
|
|
|
|
|
}
|
|
|
|
|
catch (MalformedURLException e)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -297,21 +348,27 @@ private URL getSourceURL() {
|
|
|
|
|
/**
|
|
|
|
|
* Look up an integer-valued attribute. <b>Not</b> recursive.
|
|
|
|
|
*/
|
|
|
|
|
private int getIntAttr(HTML.Attribute name, int deflt) {
|
|
|
|
|
private int getIntAttr(HTML.Attribute name, int deflt)
|
|
|
|
|
{
|
|
|
|
|
AttributeSet attr = fElement.getAttributes();
|
|
|
|
|
if (attr.isDefined(name)) { // does not check parents!
|
|
|
|
|
if (attr.isDefined(name))
|
|
|
|
|
{ // does not check parents!
|
|
|
|
|
int i;
|
|
|
|
|
String val = (String) attr.getAttribute(name);
|
|
|
|
|
if (val == null)
|
|
|
|
|
i = deflt;
|
|
|
|
|
else
|
|
|
|
|
try {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
i = Math.max(0, Integer.parseInt(val));
|
|
|
|
|
} catch (NumberFormatException x) {
|
|
|
|
|
}
|
|
|
|
|
catch (NumberFormatException x)
|
|
|
|
|
{
|
|
|
|
|
i = deflt;
|
|
|
|
|
}
|
|
|
|
|
return i;
|
|
|
|
|
} else
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return deflt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -319,10 +376,12 @@ private int getIntAttr(HTML.Attribute name, int deflt) {
|
|
|
|
|
* Establishes the parent view for this view. Seize this moment to cache the
|
|
|
|
|
* AWT Container I'm in.
|
|
|
|
|
*/
|
|
|
|
|
public void setParent(View parent) {
|
|
|
|
|
public void setParent(View parent)
|
|
|
|
|
{
|
|
|
|
|
super.setParent(parent);
|
|
|
|
|
fContainer = parent != null ? getContainer() : null;
|
|
|
|
|
if (parent == null && fComponent != null) {
|
|
|
|
|
if (parent == null && fComponent != null)
|
|
|
|
|
{
|
|
|
|
|
fComponent.getParent().remove(fComponent);
|
|
|
|
|
fComponent = null;
|
|
|
|
|
}
|
|
|
|
|
@ -351,12 +410,13 @@ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Paints the image.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @param g the rendering surface to use
|
|
|
|
|
* @param a the allocated region to render into
|
|
|
|
|
* @see View#paint
|
|
|
|
|
*/
|
|
|
|
|
public void paint(Graphics g, Shape a) {
|
|
|
|
|
public void paint(Graphics g, Shape a)
|
|
|
|
|
{
|
|
|
|
|
Color oldColor = g.getColor();
|
|
|
|
|
fBounds = a.getBounds();
|
|
|
|
|
int border = getBorder();
|
|
|
|
|
@ -367,7 +427,8 @@ public void paint(Graphics g, Shape a) {
|
|
|
|
|
int sel = getSelectionState();
|
|
|
|
|
|
|
|
|
|
// If no pixels yet, draw gray outline and icon:
|
|
|
|
|
if (!hasPixels(this)) {
|
|
|
|
|
if (!hasPixels(this))
|
|
|
|
|
{
|
|
|
|
|
g.setColor(Color.lightGray);
|
|
|
|
|
g.drawRect(x, y, width - 1, height - 1);
|
|
|
|
|
g.setColor(oldColor);
|
|
|
|
|
@ -378,7 +439,8 @@ public void paint(Graphics g, Shape a) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw image:
|
|
|
|
|
if (fImage != null) {
|
|
|
|
|
if (fImage != null)
|
|
|
|
|
{
|
|
|
|
|
g.drawImage(fImage, x, y, width, height, this);
|
|
|
|
|
// Use the following instead of g.drawImage when
|
|
|
|
|
// BufferedImageGraphics2D.setXORMode is fixed (4158822).
|
|
|
|
|
@ -393,10 +455,12 @@ public void paint(Graphics g, Shape a) {
|
|
|
|
|
|
|
|
|
|
// If selected exactly, we need a black border & grow-box:
|
|
|
|
|
Color bc = getBorderColor();
|
|
|
|
|
if (sel == 2) {
|
|
|
|
|
if (sel == 2)
|
|
|
|
|
{
|
|
|
|
|
// Make sure there's room for a border:
|
|
|
|
|
int delta = 2 - border;
|
|
|
|
|
if (delta > 0) {
|
|
|
|
|
if (delta > 0)
|
|
|
|
|
{
|
|
|
|
|
x += delta;
|
|
|
|
|
y += delta;
|
|
|
|
|
width -= delta << 1;
|
|
|
|
|
@ -410,7 +474,8 @@ public void paint(Graphics g, Shape a) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw border:
|
|
|
|
|
if (border > 0) {
|
|
|
|
|
if (border > 0)
|
|
|
|
|
{
|
|
|
|
|
if (bc != null)
|
|
|
|
|
g.setColor(bc);
|
|
|
|
|
// Draw a thick rectangle:
|
|
|
|
|
@ -424,28 +489,33 @@ public void paint(Graphics g, Shape a) {
|
|
|
|
|
* Request that this view be repainted. Assumes the view is still at its
|
|
|
|
|
* last-drawn location.
|
|
|
|
|
*/
|
|
|
|
|
protected void repaint(long delay) {
|
|
|
|
|
if (fContainer != null && fBounds != null) {
|
|
|
|
|
protected void repaint(long delay)
|
|
|
|
|
{
|
|
|
|
|
if (fContainer != null && fBounds != null)
|
|
|
|
|
{
|
|
|
|
|
fContainer.repaint(delay, fBounds.x, fBounds.y, fBounds.width,
|
|
|
|
|
fBounds.height);
|
|
|
|
|
fBounds.height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines whether the image is selected, and if it's the only thing
|
|
|
|
|
* selected.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @return 0 if not selected, 1 if selected, 2 if exclusively selected.
|
|
|
|
|
* "Exclusive" selection is only returned when editable.
|
|
|
|
|
*/
|
|
|
|
|
protected int getSelectionState() {
|
|
|
|
|
protected int getSelectionState()
|
|
|
|
|
{
|
|
|
|
|
int p0 = fElement.getStartOffset();
|
|
|
|
|
int p1 = fElement.getEndOffset();
|
|
|
|
|
if (fContainer instanceof JTextComponent) {
|
|
|
|
|
if (fContainer instanceof JTextComponent)
|
|
|
|
|
{
|
|
|
|
|
JTextComponent textComp = (JTextComponent) fContainer;
|
|
|
|
|
int start = textComp.getSelectionStart();
|
|
|
|
|
int end = textComp.getSelectionEnd();
|
|
|
|
|
if (start <= p0 && end >= p1) {
|
|
|
|
|
if (start <= p0 && end >= p1)
|
|
|
|
|
{
|
|
|
|
|
if (start == p0 && end == p1 && isEditable())
|
|
|
|
|
return 2;
|
|
|
|
|
else
|
|
|
|
|
@ -457,18 +527,21 @@ protected int getSelectionState() {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks whether the container is editable.
|
|
|
|
|
*
|
|
|
|
|
* @return <code>true</code> if the container is editable,
|
|
|
|
|
* <code>false</code> otherwise.
|
|
|
|
|
* <code>false</code> otherwise.
|
|
|
|
|
*/
|
|
|
|
|
protected boolean isEditable() {
|
|
|
|
|
protected boolean isEditable()
|
|
|
|
|
{
|
|
|
|
|
return fContainer instanceof JEditorPane
|
|
|
|
|
&& ((JEditorPane) fContainer).isEditable();
|
|
|
|
|
&& ((JEditorPane) fContainer).isEditable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the text editor's highlight color.
|
|
|
|
|
*/
|
|
|
|
|
protected Color getHighlightColor() {
|
|
|
|
|
protected Color getHighlightColor()
|
|
|
|
|
{
|
|
|
|
|
JTextComponent textComp = (JTextComponent) fContainer;
|
|
|
|
|
return textComp.getSelectionColor();
|
|
|
|
|
}
|
|
|
|
|
@ -481,12 +554,14 @@ protected Color getHighlightColor() {
|
|
|
|
|
// necessary and return. This is ok as we know when loading finishes
|
|
|
|
|
// it will pick up the new height/width, if necessary.
|
|
|
|
|
public boolean imageUpdate(Image img, int flags, int x, int y, int width,
|
|
|
|
|
int height) {
|
|
|
|
|
int height)
|
|
|
|
|
{
|
|
|
|
|
if (fImage == null || fImage != img)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Bail out if there was an error:
|
|
|
|
|
if ((flags & (ABORT | ERROR)) != 0) {
|
|
|
|
|
if ((flags & (ABORT | ERROR)) != 0)
|
|
|
|
|
{
|
|
|
|
|
fImage = null;
|
|
|
|
|
repaint(0);
|
|
|
|
|
return false;
|
|
|
|
|
@ -495,36 +570,47 @@ public boolean imageUpdate(Image img, int flags, int x, int y, int width,
|
|
|
|
|
// Resize image if necessary:
|
|
|
|
|
short changed = 0;
|
|
|
|
|
if ((flags & ImageObserver.HEIGHT) != 0)
|
|
|
|
|
if (!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) {
|
|
|
|
|
if (!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT))
|
|
|
|
|
{
|
|
|
|
|
changed |= 1;
|
|
|
|
|
}
|
|
|
|
|
if ((flags & ImageObserver.WIDTH) != 0)
|
|
|
|
|
if (!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) {
|
|
|
|
|
if (!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH))
|
|
|
|
|
{
|
|
|
|
|
changed |= 2;
|
|
|
|
|
}
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
if ((changed & 1) == 1) {
|
|
|
|
|
synchronized (this)
|
|
|
|
|
{
|
|
|
|
|
if ((changed & 1) == 1)
|
|
|
|
|
{
|
|
|
|
|
fWidth = width;
|
|
|
|
|
}
|
|
|
|
|
if ((changed & 2) == 2) {
|
|
|
|
|
if ((changed & 2) == 2)
|
|
|
|
|
{
|
|
|
|
|
fHeight = height;
|
|
|
|
|
}
|
|
|
|
|
if (loading) {
|
|
|
|
|
if (loading)
|
|
|
|
|
{
|
|
|
|
|
// No need to resize or repaint, still in the process of
|
|
|
|
|
// loading.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (changed != 0) {
|
|
|
|
|
if (changed != 0)
|
|
|
|
|
{
|
|
|
|
|
// May need to resize myself, asynchronously:
|
|
|
|
|
Document doc = getDocument();
|
|
|
|
|
try {
|
|
|
|
|
if (doc instanceof AbstractDocument) {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (doc instanceof AbstractDocument)
|
|
|
|
|
{
|
|
|
|
|
((AbstractDocument) doc).readLock();
|
|
|
|
|
}
|
|
|
|
|
preferenceChanged(this, true, true);
|
|
|
|
|
} finally {
|
|
|
|
|
if (doc instanceof AbstractDocument) {
|
|
|
|
|
} finally
|
|
|
|
|
{
|
|
|
|
|
if (doc instanceof AbstractDocument)
|
|
|
|
|
{
|
|
|
|
|
((AbstractDocument) doc).readUnlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -544,7 +630,7 @@ else if ((flags & SOMEBITS) != 0)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Static properties for incremental drawing. Swiped from Component.java
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* @see #imageUpdate
|
|
|
|
|
*/
|
|
|
|
|
private static boolean sIsInc = true;
|
|
|
|
|
@ -555,17 +641,18 @@ else if ((flags & SOMEBITS) != 0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines the preferred span for this view along an axis.
|
|
|
|
|
*
|
|
|
|
|
* @param axis
|
|
|
|
|
* may be either X_AXIS or Y_AXIS
|
|
|
|
|
*
|
|
|
|
|
* @param axis may be either X_AXIS or Y_AXIS
|
|
|
|
|
* @return the span the view would like to be rendered into. Typically the
|
|
|
|
|
* view is told to render into the span that is returned, although
|
|
|
|
|
* there is no guarantee. The parent may choose to resize or break
|
|
|
|
|
* the view.
|
|
|
|
|
*/
|
|
|
|
|
public float getPreferredSpan(int axis) {
|
|
|
|
|
public float getPreferredSpan(int axis)
|
|
|
|
|
{
|
|
|
|
|
int extra = 2 * (getBorder() + getSpace(axis));
|
|
|
|
|
switch (axis) {
|
|
|
|
|
switch (axis)
|
|
|
|
|
{
|
|
|
|
|
case View.X_AXIS:
|
|
|
|
|
return fWidth + extra;
|
|
|
|
|
case View.Y_AXIS:
|
|
|
|
|
@ -579,16 +666,17 @@ public float getPreferredSpan(int axis) {
|
|
|
|
|
* Determines the desired alignment for this view along an axis. This is
|
|
|
|
|
* implemented to give the alignment to the bottom of the icon along the y
|
|
|
|
|
* axis, and the default along the x axis.
|
|
|
|
|
*
|
|
|
|
|
* @param axis
|
|
|
|
|
* may be either X_AXIS or Y_AXIS
|
|
|
|
|
*
|
|
|
|
|
* @param axis may be either X_AXIS or Y_AXIS
|
|
|
|
|
* @return the desired alignment. This should be a value between 0.0 and 1.0
|
|
|
|
|
* where 0 indicates alignment at the origin and 1.0 indicates
|
|
|
|
|
* alignment to the full span away from the origin. An alignment of
|
|
|
|
|
* 0.5 would be the center of the view.
|
|
|
|
|
*/
|
|
|
|
|
public float getAlignment(int axis) {
|
|
|
|
|
switch (axis) {
|
|
|
|
|
public float getAlignment(int axis)
|
|
|
|
|
{
|
|
|
|
|
switch (axis)
|
|
|
|
|
{
|
|
|
|
|
case View.Y_AXIS:
|
|
|
|
|
return getVerticalAlignment();
|
|
|
|
|
default:
|
|
|
|
|
@ -599,24 +687,24 @@ public float getAlignment(int axis) {
|
|
|
|
|
/**
|
|
|
|
|
* Provides a mapping from the document model coordinate space to the
|
|
|
|
|
* coordinate space of the view mapped to it.
|
|
|
|
|
*
|
|
|
|
|
* @param pos
|
|
|
|
|
* the position to convert
|
|
|
|
|
* @param a
|
|
|
|
|
* the allocated region to render into
|
|
|
|
|
*
|
|
|
|
|
* @param pos the position to convert
|
|
|
|
|
* @param a the allocated region to render into
|
|
|
|
|
* @return the bounding box of the given position
|
|
|
|
|
* @exception BadLocationException
|
|
|
|
|
* if the given position does not represent a valid location
|
|
|
|
|
* in the associated document
|
|
|
|
|
* @exception BadLocationException if the given position does not represent
|
|
|
|
|
* a valid location in the associated document
|
|
|
|
|
* @see View#modelToView
|
|
|
|
|
*/
|
|
|
|
|
public Shape modelToView(int pos, Shape a, Position.Bias b)
|
|
|
|
|
throws BadLocationException {
|
|
|
|
|
throws BadLocationException
|
|
|
|
|
{
|
|
|
|
|
int p0 = getStartOffset();
|
|
|
|
|
int p1 = getEndOffset();
|
|
|
|
|
if ((pos >= p0) && (pos <= p1)) {
|
|
|
|
|
if ((pos >= p0) && (pos <= p1))
|
|
|
|
|
{
|
|
|
|
|
Rectangle r = a.getBounds();
|
|
|
|
|
if (pos == p1) {
|
|
|
|
|
if (pos == p1)
|
|
|
|
|
{
|
|
|
|
|
r.x += r.width;
|
|
|
|
|
}
|
|
|
|
|
r.width = 0;
|
|
|
|
|
@ -628,20 +716,19 @@ public Shape modelToView(int pos, Shape a, Position.Bias b)
|
|
|
|
|
/**
|
|
|
|
|
* Provides a mapping from the view coordinate space to the logical
|
|
|
|
|
* coordinate space of the model.
|
|
|
|
|
*
|
|
|
|
|
* @param x
|
|
|
|
|
* the X coordinate
|
|
|
|
|
* @param y
|
|
|
|
|
* the Y coordinate
|
|
|
|
|
* @param a
|
|
|
|
|
* the allocated region to render into
|
|
|
|
|
*
|
|
|
|
|
* @param x the X coordinate
|
|
|
|
|
* @param y the Y coordinate
|
|
|
|
|
* @param a the allocated region to render into
|
|
|
|
|
* @return the location within the model that best represents the given
|
|
|
|
|
* point of view
|
|
|
|
|
* @see View#viewToModel
|
|
|
|
|
*/
|
|
|
|
|
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
|
|
|
|
|
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
|
|
|
|
|
{
|
|
|
|
|
Rectangle alloc = (Rectangle) a;
|
|
|
|
|
if (x < alloc.x + alloc.width) {
|
|
|
|
|
if (x < alloc.x + alloc.width)
|
|
|
|
|
{
|
|
|
|
|
bias[0] = Position.Bias.Forward;
|
|
|
|
|
return getStartOffset();
|
|
|
|
|
}
|
|
|
|
|
@ -651,20 +738,20 @@ public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the size of the view. (Ignored.)
|
|
|
|
|
*
|
|
|
|
|
* @param width
|
|
|
|
|
* the width
|
|
|
|
|
* @param height
|
|
|
|
|
* the height
|
|
|
|
|
*
|
|
|
|
|
* @param width the width
|
|
|
|
|
* @param height the height
|
|
|
|
|
*/
|
|
|
|
|
public void setSize(float width, float height) {
|
|
|
|
|
public void setSize(float width, float height)
|
|
|
|
|
{
|
|
|
|
|
// Ignore this -- image size is determined by the tag attrs and
|
|
|
|
|
// the image itself, not the surrounding layout!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Static icon accessors -------------------------------------------
|
|
|
|
|
|
|
|
|
|
private Icon makeIcon(final String gifFile) throws IOException {
|
|
|
|
|
private Icon makeIcon(final String gifFile) throws IOException
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Copy resource into a byte array. This is necessary because several
|
|
|
|
|
* browsers consider Class.getResource a security risk because it can be
|
|
|
|
|
@ -672,43 +759,51 @@ private Icon makeIcon(final String gifFile) throws IOException {
|
|
|
|
|
* returns raw bytes, which we can convert to an image.
|
|
|
|
|
*/
|
|
|
|
|
InputStream resource = SIPCommImageView.class
|
|
|
|
|
.getResourceAsStream(gifFile);
|
|
|
|
|
.getResourceAsStream(gifFile);
|
|
|
|
|
|
|
|
|
|
if (resource == null) {
|
|
|
|
|
if (resource == null)
|
|
|
|
|
{
|
|
|
|
|
System.err.println(SIPCommImageView.class.getName() + "/" + gifFile
|
|
|
|
|
+ " not found.");
|
|
|
|
|
+ " not found.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
BufferedInputStream in = new BufferedInputStream(resource);
|
|
|
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
|
int n;
|
|
|
|
|
while ((n = in.read(buffer)) > 0) {
|
|
|
|
|
while ((n = in.read(buffer)) > 0)
|
|
|
|
|
{
|
|
|
|
|
out.write(buffer, 0, n);
|
|
|
|
|
}
|
|
|
|
|
in.close();
|
|
|
|
|
out.flush();
|
|
|
|
|
|
|
|
|
|
buffer = out.toByteArray();
|
|
|
|
|
if (buffer.length == 0) {
|
|
|
|
|
if (buffer.length == 0)
|
|
|
|
|
{
|
|
|
|
|
System.err.println("warning: " + gifFile + " is zero-length");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return new ImageIcon(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadIcons() {
|
|
|
|
|
try {
|
|
|
|
|
private void loadIcons()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (sPendingImageIcon == null)
|
|
|
|
|
sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC);
|
|
|
|
|
if (sMissingImageIcon == null)
|
|
|
|
|
sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC);
|
|
|
|
|
} catch (Exception x) {
|
|
|
|
|
}
|
|
|
|
|
catch (Exception x)
|
|
|
|
|
{
|
|
|
|
|
System.err.println("ImageView: Couldn't load image icons");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected StyleSheet getStyleSheet() {
|
|
|
|
|
protected StyleSheet getStyleSheet()
|
|
|
|
|
{
|
|
|
|
|
HTMLDocument doc = (HTMLDocument) getDocument();
|
|
|
|
|
return doc.getStyleSheet();
|
|
|
|
|
}
|
|
|
|
|
@ -733,24 +828,28 @@ protected StyleSheet getStyleSheet() {
|
|
|
|
|
|
|
|
|
|
private boolean fGrowProportionally; // should grow be proportional?
|
|
|
|
|
|
|
|
|
|
/** Set to true, while the receiver is locked, to indicate the reciever
|
|
|
|
|
* is loading the image. This is used in imageUpdate. */
|
|
|
|
|
/**
|
|
|
|
|
* Set to true, while the receiver is locked, to indicate the reciever is
|
|
|
|
|
* loading the image. This is used in imageUpdate.
|
|
|
|
|
*/
|
|
|
|
|
private boolean loading;
|
|
|
|
|
|
|
|
|
|
// --- constants and static stuff --------------------------------
|
|
|
|
|
|
|
|
|
|
private static Icon sPendingImageIcon, sMissingImageIcon;
|
|
|
|
|
|
|
|
|
|
private static final String PENDING_IMAGE_SRC
|
|
|
|
|
= "icons/image-delayed.gif", // both stolen from HotJava
|
|
|
|
|
MISSING_IMAGE_SRC = "icons/image-failed.gif";
|
|
|
|
|
private static final String PENDING_IMAGE_SRC = "icons/image-delayed.gif", // both
|
|
|
|
|
// stolen
|
|
|
|
|
// from
|
|
|
|
|
// HotJava
|
|
|
|
|
MISSING_IMAGE_SRC = "icons/image-failed.gif";
|
|
|
|
|
|
|
|
|
|
//$ move this someplace public
|
|
|
|
|
// $ move this someplace public
|
|
|
|
|
static final String IMAGE_CACHE_PROPERTY = "imageCache";
|
|
|
|
|
|
|
|
|
|
// Height/width to use before we know the real size:
|
|
|
|
|
private static final int DEFAULT_WIDTH = 32, DEFAULT_HEIGHT = 32,
|
|
|
|
|
// Default value of BORDER param: //? possibly move into stylesheet?
|
|
|
|
|
DEFAULT_BORDER = 2;
|
|
|
|
|
// Default value of BORDER param: //? possibly move into stylesheet?
|
|
|
|
|
DEFAULT_BORDER = 2;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|