19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package javax.swing.text.html;
26
27 import java.awt.*;
28 import java.awt.image.ImageObserver;
29 import java.net.*;
30 import java.util.Dictionary;
31 import javax.swing.*;
32 import javax.swing.text.*;
33 import javax.swing.event.*;
34
35 /**
36 * View of an Image, intended to support the HTML <IMG> tag.
37 * Supports scaling via the HEIGHT and WIDTH attributes of the tag.
38 * If the image is unable to be loaded any text specified via the
39 * <code>ALT</code> attribute will be rendered.
40 * <p>
41 * While this class has been part of swing for a while now, it is public
42 * as of 1.4.
43 *
44 * @author Scott Violet
45 * @see IconView
46 * @since 1.4
47 */
48 public class ImageView extends View {
49 /**
50 * If true, when some of the bits are available a repaint is done.
51 * <p>
52 * This is set to false as swing does not offer a repaint that takes a
53 * delay. If this were true, a bunch of immediate repaints would get
54 * generated that end up significantly delaying the loading of the image
55 * (or anything else going on for that matter).
56 */
57 private static boolean sIsInc = false;
58 /**
59 * Repaint delay when some of the bits are available.
126 /** Alignment along the vertical (Y) axis. */
127 private float vAlign;
128
129
130
131 /**
132 * Creates a new view that represents an IMG element.
133 *
134 * @param elem the element to create a view for
135 */
136 public ImageView(Element elem) {
137 super(elem);
138 fBounds = new Rectangle();
139 imageObserver = new ImageHandler();
140 state = RELOAD_FLAG | RELOAD_IMAGE_FLAG;
141 }
142
143 /**
144 * Returns the text to display if the image cannot be loaded. This is
145 * obtained from the Elements attribute set with the attribute name
146 * <code>HTML.Attribute.ALT</code>.
147 *
148 * @return the test to display if the image cannot be loaded.
149 */
150 public String getAltText() {
151 return (String)getElement().getAttributes().getAttribute
152 (HTML.Attribute.ALT);
153 }
154
155 /**
156 * Return a URL for the image source,
157 * or null if it could not be determined.
158 *
159 * @return the URL for the image source, or null if it could not be determined.
160 */
161 public URL getImageURL() {
162 String src = (String)getElement().getAttributes().
163 getAttribute(HTML.Attribute.SRC);
164 if (src == null) {
165 return null;
166 }
197 *
198 * @return the image to render.
199 */
200 public Image getImage() {
201 sync();
202 return image;
203 }
204
205 private Image getImage(boolean enabled) {
206 Image img = getImage();
207 if (! enabled) {
208 if (disabledImage == null) {
209 disabledImage = GrayFilter.createDisabledImage(img);
210 }
211 img = disabledImage;
212 }
213 return img;
214 }
215
216 /**
217 * Sets how the image is loaded. If <code>newValue</code> is true,
218 * the image will be loaded when first asked for, otherwise it will
219 * be loaded asynchronously. The default is to not load synchronously,
220 * that is to load the image asynchronously.
221 *
222 * @param newValue if {@code true} the image will be loaded when first asked for,
223 * otherwise it will be asynchronously.
224 */
225 public void setLoadsSynchronously(boolean newValue) {
226 synchronized(this) {
227 if (newValue) {
228 state |= SYNC_LOAD_FLAG;
229 }
230 else {
231 state = (state | SYNC_LOAD_FLAG) ^ SYNC_LOAD_FLAG;
232 }
233 }
234 }
235
236 /**
237 * Returns {@code true} if the image should be loaded when first asked for.
247 *
248 * @return the StyleSheet
249 */
250 protected StyleSheet getStyleSheet() {
251 HTMLDocument doc = (HTMLDocument) getDocument();
252 return doc.getStyleSheet();
253 }
254
255 /**
256 * Fetches the attributes to use when rendering. This is
257 * implemented to multiplex the attributes specified in the
258 * model with a StyleSheet.
259 */
260 public AttributeSet getAttributes() {
261 sync();
262 return attr;
263 }
264
265 /**
266 * For images the tooltip text comes from text specified with the
267 * <code>ALT</code> attribute. This is overriden to return
268 * <code>getAltText</code>.
269 *
270 * @see JTextComponent#getToolTipText
271 */
272 public String getToolTipText(float x, float y, Shape allocation) {
273 return getAltText();
274 }
275
276 /**
277 * Update any cached values that come from attributes.
278 */
279 protected void setPropertiesFromAttributes() {
280 StyleSheet sheet = getStyleSheet();
281 this.attr = sheet.getViewAttributes(this);
282
283 // Gutters
284 borderSize = (short)getIntAttr(HTML.Attribute.BORDER, isLink() ?
285 DEFAULT_BORDER : 0);
286
287 leftInset = rightInset = (short)(getIntAttr(HTML.Attribute.HSPACE,
288 0) + borderSize);
655
656 /**
657 * Makes sure the necessary properties and image is loaded.
658 */
659 private void sync() {
660 int s = state;
661 if ((s & RELOAD_IMAGE_FLAG) != 0) {
662 refreshImage();
663 }
664 s = state;
665 if ((s & RELOAD_FLAG) != 0) {
666 synchronized(this) {
667 state = (state | RELOAD_FLAG) ^ RELOAD_FLAG;
668 }
669 setPropertiesFromAttributes();
670 }
671 }
672
673 /**
674 * Loads the image and updates the size accordingly. This should be
675 * invoked instead of invoking <code>loadImage</code> or
676 * <code>updateImageSize</code> directly.
677 */
678 private void refreshImage() {
679 synchronized(this) {
680 // clear out width/height/realoadimage flag and set loading flag
681 state = (state | LOADING_FLAG | RELOAD_IMAGE_FLAG | WIDTH_FLAG |
682 HEIGHT_FLAG) ^ (WIDTH_FLAG | HEIGHT_FLAG |
683 RELOAD_IMAGE_FLAG);
684 image = null;
685 width = height = 0;
686 }
687
688 try {
689 // Load the image
690 loadImage();
691
692 // And update the size params
693 updateImageSize();
694 }
695 finally {
696 synchronized(this) {
697 // Clear out state in case someone threw an exception.
698 state = (state | LOADING_FLAG) ^ LOADING_FLAG;
699 }
700 }
701 }
702
703 /**
704 * Loads the image from the URL <code>getImageURL</code>. This should
705 * only be invoked from <code>refreshImage</code>.
706 */
707 private void loadImage() {
708 URL src = getImageURL();
709 Image newImage = null;
710 if (src != null) {
711 @SuppressWarnings("unchecked")
712 Dictionary<URL, Image> cache = (Dictionary)getDocument().
713 getProperty(IMAGE_CACHE_PROPERTY);
714 if (cache != null) {
715 newImage = cache.get(src);
716 }
717 else {
718 newImage = Toolkit.getDefaultToolkit().createImage(src);
719 if (newImage != null && getLoadsSynchronously()) {
720 // Force the image to be loaded by using an ImageIcon.
721 ImageIcon ii = new ImageIcon();
722 ii.setImage(newImage);
723 }
724 }
725 }
726 image = newImage;
727 }
728
729 /**
730 * Recreates and reloads the image. This should
731 * only be invoked from <code>refreshImage</code>.
732 */
733 private void updateImageSize() {
734 int newWidth = 0;
735 int newHeight = 0;
736 int newState = 0;
737 Image newImage = getImage();
738
739 if (newImage != null) {
740 Element elem = getElement();
741 AttributeSet attr = elem.getAttributes();
742
743 // Get the width/height and set the state ivar before calling
744 // anything that might cause the image to be loaded, and thus the
745 // ImageHandler to be called.
746 newWidth = getIntAttr(HTML.Attribute.WIDTH, -1);
747 if (newWidth > 0) {
748 newState |= WIDTH_FLAG;
749 }
750 newHeight = getIntAttr(HTML.Attribute.HEIGHT, -1);
751 if (newHeight > 0) {
829 }
830 }
831 }
832
833 /**
834 * Returns the view to use for alternate text. This may be null.
835 */
836 private View getAltView() {
837 View view;
838
839 synchronized(this) {
840 view = altView;
841 }
842 if (view != null && view.getParent() == null) {
843 view.setParent(getParent());
844 }
845 return view;
846 }
847
848 /**
849 * Invokes <code>preferenceChanged</code> on the event displatching
850 * thread.
851 */
852 private void safePreferenceChanged() {
853 if (SwingUtilities.isEventDispatchThread()) {
854 Document doc = getDocument();
855 if (doc instanceof AbstractDocument) {
856 ((AbstractDocument)doc).readLock();
857 }
858 preferenceChanged(null, true, true);
859 if (doc instanceof AbstractDocument) {
860 ((AbstractDocument)doc).readUnlock();
861 }
862 }
863 else {
864 SwingUtilities.invokeLater(new Runnable() {
865 public void run() {
866 safePreferenceChanged();
867 }
868 });
869 }
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package javax.swing.text.html;
26
27 import java.awt.*;
28 import java.awt.image.ImageObserver;
29 import java.net.*;
30 import java.util.Dictionary;
31 import javax.swing.*;
32 import javax.swing.text.*;
33 import javax.swing.event.*;
34
35 /**
36 * View of an Image, intended to support the HTML <IMG> tag.
37 * Supports scaling via the HEIGHT and WIDTH attributes of the tag.
38 * If the image is unable to be loaded any text specified via the
39 * {@code ALT} attribute will be rendered.
40 * <p>
41 * While this class has been part of swing for a while now, it is public
42 * as of 1.4.
43 *
44 * @author Scott Violet
45 * @see IconView
46 * @since 1.4
47 */
48 public class ImageView extends View {
49 /**
50 * If true, when some of the bits are available a repaint is done.
51 * <p>
52 * This is set to false as swing does not offer a repaint that takes a
53 * delay. If this were true, a bunch of immediate repaints would get
54 * generated that end up significantly delaying the loading of the image
55 * (or anything else going on for that matter).
56 */
57 private static boolean sIsInc = false;
58 /**
59 * Repaint delay when some of the bits are available.
126 /** Alignment along the vertical (Y) axis. */
127 private float vAlign;
128
129
130
131 /**
132 * Creates a new view that represents an IMG element.
133 *
134 * @param elem the element to create a view for
135 */
136 public ImageView(Element elem) {
137 super(elem);
138 fBounds = new Rectangle();
139 imageObserver = new ImageHandler();
140 state = RELOAD_FLAG | RELOAD_IMAGE_FLAG;
141 }
142
143 /**
144 * Returns the text to display if the image cannot be loaded. This is
145 * obtained from the Elements attribute set with the attribute name
146 * {@code HTML.Attribute.ALT}.
147 *
148 * @return the test to display if the image cannot be loaded.
149 */
150 public String getAltText() {
151 return (String)getElement().getAttributes().getAttribute
152 (HTML.Attribute.ALT);
153 }
154
155 /**
156 * Return a URL for the image source,
157 * or null if it could not be determined.
158 *
159 * @return the URL for the image source, or null if it could not be determined.
160 */
161 public URL getImageURL() {
162 String src = (String)getElement().getAttributes().
163 getAttribute(HTML.Attribute.SRC);
164 if (src == null) {
165 return null;
166 }
197 *
198 * @return the image to render.
199 */
200 public Image getImage() {
201 sync();
202 return image;
203 }
204
205 private Image getImage(boolean enabled) {
206 Image img = getImage();
207 if (! enabled) {
208 if (disabledImage == null) {
209 disabledImage = GrayFilter.createDisabledImage(img);
210 }
211 img = disabledImage;
212 }
213 return img;
214 }
215
216 /**
217 * Sets how the image is loaded. If {@code newValue} is true,
218 * the image will be loaded when first asked for, otherwise it will
219 * be loaded asynchronously. The default is to not load synchronously,
220 * that is to load the image asynchronously.
221 *
222 * @param newValue if {@code true} the image will be loaded when first asked for,
223 * otherwise it will be asynchronously.
224 */
225 public void setLoadsSynchronously(boolean newValue) {
226 synchronized(this) {
227 if (newValue) {
228 state |= SYNC_LOAD_FLAG;
229 }
230 else {
231 state = (state | SYNC_LOAD_FLAG) ^ SYNC_LOAD_FLAG;
232 }
233 }
234 }
235
236 /**
237 * Returns {@code true} if the image should be loaded when first asked for.
247 *
248 * @return the StyleSheet
249 */
250 protected StyleSheet getStyleSheet() {
251 HTMLDocument doc = (HTMLDocument) getDocument();
252 return doc.getStyleSheet();
253 }
254
255 /**
256 * Fetches the attributes to use when rendering. This is
257 * implemented to multiplex the attributes specified in the
258 * model with a StyleSheet.
259 */
260 public AttributeSet getAttributes() {
261 sync();
262 return attr;
263 }
264
265 /**
266 * For images the tooltip text comes from text specified with the
267 * {@code ALT} attribute. This is overriden to return
268 * {@code getAltText}.
269 *
270 * @see JTextComponent#getToolTipText
271 */
272 public String getToolTipText(float x, float y, Shape allocation) {
273 return getAltText();
274 }
275
276 /**
277 * Update any cached values that come from attributes.
278 */
279 protected void setPropertiesFromAttributes() {
280 StyleSheet sheet = getStyleSheet();
281 this.attr = sheet.getViewAttributes(this);
282
283 // Gutters
284 borderSize = (short)getIntAttr(HTML.Attribute.BORDER, isLink() ?
285 DEFAULT_BORDER : 0);
286
287 leftInset = rightInset = (short)(getIntAttr(HTML.Attribute.HSPACE,
288 0) + borderSize);
655
656 /**
657 * Makes sure the necessary properties and image is loaded.
658 */
659 private void sync() {
660 int s = state;
661 if ((s & RELOAD_IMAGE_FLAG) != 0) {
662 refreshImage();
663 }
664 s = state;
665 if ((s & RELOAD_FLAG) != 0) {
666 synchronized(this) {
667 state = (state | RELOAD_FLAG) ^ RELOAD_FLAG;
668 }
669 setPropertiesFromAttributes();
670 }
671 }
672
673 /**
674 * Loads the image and updates the size accordingly. This should be
675 * invoked instead of invoking {@code loadImage} or
676 * {@code updateImageSize} directly.
677 */
678 private void refreshImage() {
679 synchronized(this) {
680 // clear out width/height/realoadimage flag and set loading flag
681 state = (state | LOADING_FLAG | RELOAD_IMAGE_FLAG | WIDTH_FLAG |
682 HEIGHT_FLAG) ^ (WIDTH_FLAG | HEIGHT_FLAG |
683 RELOAD_IMAGE_FLAG);
684 image = null;
685 width = height = 0;
686 }
687
688 try {
689 // Load the image
690 loadImage();
691
692 // And update the size params
693 updateImageSize();
694 }
695 finally {
696 synchronized(this) {
697 // Clear out state in case someone threw an exception.
698 state = (state | LOADING_FLAG) ^ LOADING_FLAG;
699 }
700 }
701 }
702
703 /**
704 * Loads the image from the URL {@code getImageURL}. This should
705 * only be invoked from {@code refreshImage}.
706 */
707 private void loadImage() {
708 URL src = getImageURL();
709 Image newImage = null;
710 if (src != null) {
711 @SuppressWarnings("unchecked")
712 Dictionary<URL, Image> cache = (Dictionary)getDocument().
713 getProperty(IMAGE_CACHE_PROPERTY);
714 if (cache != null) {
715 newImage = cache.get(src);
716 }
717 else {
718 newImage = Toolkit.getDefaultToolkit().createImage(src);
719 if (newImage != null && getLoadsSynchronously()) {
720 // Force the image to be loaded by using an ImageIcon.
721 ImageIcon ii = new ImageIcon();
722 ii.setImage(newImage);
723 }
724 }
725 }
726 image = newImage;
727 }
728
729 /**
730 * Recreates and reloads the image. This should
731 * only be invoked from {@code refreshImage}.
732 */
733 private void updateImageSize() {
734 int newWidth = 0;
735 int newHeight = 0;
736 int newState = 0;
737 Image newImage = getImage();
738
739 if (newImage != null) {
740 Element elem = getElement();
741 AttributeSet attr = elem.getAttributes();
742
743 // Get the width/height and set the state ivar before calling
744 // anything that might cause the image to be loaded, and thus the
745 // ImageHandler to be called.
746 newWidth = getIntAttr(HTML.Attribute.WIDTH, -1);
747 if (newWidth > 0) {
748 newState |= WIDTH_FLAG;
749 }
750 newHeight = getIntAttr(HTML.Attribute.HEIGHT, -1);
751 if (newHeight > 0) {
829 }
830 }
831 }
832
833 /**
834 * Returns the view to use for alternate text. This may be null.
835 */
836 private View getAltView() {
837 View view;
838
839 synchronized(this) {
840 view = altView;
841 }
842 if (view != null && view.getParent() == null) {
843 view.setParent(getParent());
844 }
845 return view;
846 }
847
848 /**
849 * Invokes {@code preferenceChanged} on the event displatching
850 * thread.
851 */
852 private void safePreferenceChanged() {
853 if (SwingUtilities.isEventDispatchThread()) {
854 Document doc = getDocument();
855 if (doc instanceof AbstractDocument) {
856 ((AbstractDocument)doc).readLock();
857 }
858 preferenceChanged(null, true, true);
859 if (doc instanceof AbstractDocument) {
860 ((AbstractDocument)doc).readUnlock();
861 }
862 }
863 else {
864 SwingUtilities.invokeLater(new Runnable() {
865 public void run() {
866 safePreferenceChanged();
867 }
868 });
869 }
|