< prev index next >

src/java.desktop/share/classes/javax/swing/text/html/ImageView.java

Print this page




  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 &lt;IMG&gt; 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 &lt;IMG&gt; 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         }


< prev index next >