1 /*
2 * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package javax.swing;
26
27 import java.awt;
28 import java.awt.image;
29 import java.beans.ConstructorProperties;
30 import java.beans.Transient;
31 import java.net.URL;
32
33 import java.io.Serializable;
34 import java.io.ObjectOutputStream;
35 import java.io.ObjectInputStream;
36 import java.io.IOException;
37
38 import java.util.Locale;
39 import javax.accessibility;
40
41 import sun.awt.AppContext;
42 import java.lang.reflect.Field;
43 import java.security.PrivilegedAction;
44 import java.security.AccessController;
45
46 /**
47 * An implementation of the Icon interface that paints Icons
48 * from Images. Images that are created from a URL, filename or byte array
49 * are preloaded using MediaTracker to monitor the loaded state
50 * of the image.
51 *
52 * <p>
53 * For further information and examples of using image icons, see
54 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/icon.html">How to Use Icons</a>
55 * in <em>The Java Tutorial.</em>
56 *
57 * <p>
58 * <strong>Warning:</strong>
59 * Serialized objects of this class will not be compatible with
60 * future Swing releases. The current serialization support is
61 * appropriate for short term storage or RMI between applications running
62 * the same version of Swing. As of 1.4, support for long term storage
63 * of all JavaBeans<sup><font size="-2">TM</font></sup>
64 * has been added to the <code>java.beans</code> package.
65 * Please see {@link java.beans.XMLEncoder}.
66 *
67 * @author Jeff Dinkins
68 * @author Lynn Monsanto
69 */
70 public class ImageIcon implements Icon, Serializable, Accessible {
71 /* Keep references to the filename and location so that
72 * alternate persistence schemes have the option to archive
73 * images symbolically rather than including the image data
74 * in the archive.
75 */
76 transient private String filename;
77 transient private URL location;
78
79 transient Image image;
80 transient int loadStatus = 0;
81 ImageObserver imageObserver;
82 String description = null;
83
84 protected final static Component component;
85 protected final static MediaTracker tracker;
86
87 static {
88 component = new Component() {};
89 AccessController.doPrivileged(new PrivilegedAction() {
90 public Object run() {
91 try {
92 // 6482575 - clear the appContext field so as not to leak it
93 Field appContextField =
94 Component.class.getDeclaredField("appContext");
95 appContextField.setAccessible(true);
96 appContextField.set(component, null);
97 }
98 catch (NoSuchFieldException e) {
99 e.printStackTrace();
100 }
101 catch (IllegalAccessException e) {
102 e.printStackTrace();
103 }
104 return null;
105 }
106 });
107 tracker = new MediaTracker(component);
108 }
109
110 /**
111 * Id used in loading images from MediaTracker.
112 */
113 private static int mediaTrackerID;
114
115 private final static Object TRACKER_KEY = new StringBuilder("TRACKER_KEY");
116
117 int width = -1;
118 int height = -1;
119
120 /**
121 * Creates an ImageIcon from the specified file. The image will
122 * be preloaded by using MediaTracker to monitor the loading state
123 * of the image.
124 * @param filename the name of the file containing the image
125 * @param description a brief textual description of the image
126 * @see #ImageIcon(String)
127 */
128 public ImageIcon(String filename, String description) {
129 image = Toolkit.getDefaultToolkit().getImage(filename);
130 if (image == null) {
131 return;
132 }
133 this.filename = filename;
134 this.description = description;
135 loadImage(image);
136 }
137
138 /**
139 * Creates an ImageIcon from the specified file. The image will
140 * be preloaded by using MediaTracker to monitor the loading state
141 * of the image. The specified String can be a file name or a
142 * file path. When specifying a path, use the Internet-standard
143 * forward-slash ("/") as a separator.
144 * (The string is converted to an URL, so the forward-slash works
145 * on all systems.)
146 * For example, specify:
147 * <pre>
148 * new ImageIcon("images/myImage.gif") </pre>
149 * The description is initialized to the <code>filename</code> string.
150 *
151 * @param filename a String specifying a filename or path
152 * @see #getDescription
153 */
154 @ConstructorProperties({"description"})
155 public ImageIcon (String filename) {
156 this(filename, filename);
157 }
158
159 /**
160 * Creates an ImageIcon from the specified URL. The image will
161 * be preloaded by using MediaTracker to monitor the loaded state
162 * of the image.
163 * @param location the URL for the image
164 * @param description a brief textual description of the image
165 * @see #ImageIcon(String)
166 */
167 public ImageIcon(URL location, String description) {
168 image = Toolkit.getDefaultToolkit().getImage(location);
169 if (image == null) {
170 return;
171 }
172 this.location = location;
173 this.description = description;
174 loadImage(image);
175 }
176
177 /**
178 * Creates an ImageIcon from the specified URL. The image will
179 * be preloaded by using MediaTracker to monitor the loaded state
180 * of the image.
181 * The icon's description is initialized to be
182 * a string representation of the URL.
183 * @param location the URL for the image
184 * @see #getDescription
185 */
186 public ImageIcon (URL location) {
187 this(location, location.toExternalForm());
188 }
189
190 /**
191 * Creates an ImageIcon from the image.
192 * @param image the image
193 * @param description a brief textual description of the image
194 */
195 public ImageIcon(Image image, String description) {
196 this(image);
197 this.description = description;
198 }
199
200 /**
201 * Creates an ImageIcon from an image object.
202 * If the image has a "comment" property that is a string,
203 * then the string is used as the description of this icon.
204 * @param image the image
205 * @see #getDescription
206 * @see java.awt.Image#getProperty
207 */
208 public ImageIcon (Image image) {
209 this.image = image;
210 Object o = image.getProperty("comment", imageObserver);
211 if (o instanceof String) {
212 description = (String) o;
213 }
214 loadImage(image);
215 }
216
217 /**
218 * Creates an ImageIcon from an array of bytes which were
219 * read from an image file containing a supported image format,
220 * such as GIF, JPEG, or (as of 1.3) PNG.
221 * Normally this array is created
222 * by reading an image using Class.getResourceAsStream(), but
223 * the byte array may also be statically stored in a class.
224 *
225 * @param imageData an array of pixels in an image format supported
226 * by the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG
227 * @param description a brief textual description of the image
228 * @see java.awt.Toolkit#createImage
229 */
230 public ImageIcon (byte[] imageData, String description) {
231 this.image = Toolkit.getDefaultToolkit().createImage(imageData);
232 if (image == null) {
233 return;
234 }
235 this.description = description;
236 loadImage(image);
237 }
238
239 /**
240 * Creates an ImageIcon from an array of bytes which were
241 * read from an image file containing a supported image format,
242 * such as GIF, JPEG, or (as of 1.3) PNG.
243 * Normally this array is created
244 * by reading an image using Class.getResourceAsStream(), but
245 * the byte array may also be statically stored in a class.
246 * If the resulting image has a "comment" property that is a string,
247 * then the string is used as the description of this icon.
248 *
249 * @param imageData an array of pixels in an image format supported by
250 * the AWT Toolkit, such as GIF, JPEG, or (as of 1.3) PNG
251 * @see java.awt.Toolkit#createImage
252 * @see #getDescription
253 * @see java.awt.Image#getProperty
254 */
255 public ImageIcon (byte[] imageData) {
256 this.image = Toolkit.getDefaultToolkit().createImage(imageData);
257 if (image == null) {
258 return;
259 }
260 Object o = image.getProperty("comment", imageObserver);
261 if (o instanceof String) {
262 description = (String) o;
263 }
264 loadImage(image);
265 }
266
267 /**
268 * Creates an uninitialized image icon.
269 */
270 public ImageIcon() {
271 }
272
273 /**
274 * Loads the image, returning only when the image is loaded.
275 * @param image the image
276 */
277 protected void loadImage(Image image) {
278 MediaTracker mTracker = getTracker();
279 synchronized(mTracker) {
280 int id = getNextID();
281
282 mTracker.addImage(image, id);
283 try {
284 mTracker.waitForID(id, 0);
285 } catch (InterruptedException e) {
286 System.out.println("INTERRUPTED while loading Image");
287 }
288 loadStatus = mTracker.statusID(id, false);
289 mTracker.removeImage(image, id);
290
291 width = image.getWidth(imageObserver);
292 height = image.getHeight(imageObserver);
293 }
294 }
295
296 /**
297 * Returns an ID to use with the MediaTracker in loading an image.
298 */
299 private int getNextID() {
300 synchronized(getTracker()) {
301 return ++mediaTrackerID;
302 }
303 }
304
305 /**
306 * Returns the MediaTracker for the current AppContext, creating a new
307 * MediaTracker if necessary.
308 */
309 private MediaTracker getTracker() {
310 Object trackerObj;
311 AppContext ac = AppContext.getAppContext();
312 // Opt: Only synchronize if trackerObj comes back null?
313 // If null, synchronize, re-check for null, and put new tracker
314 synchronized(ac) {
315 trackerObj = ac.get(TRACKER_KEY);
316 if (trackerObj == null) {
317 Component comp = new Component() {};
318 trackerObj = new MediaTracker(comp);
319 ac.put(TRACKER_KEY, trackerObj);
320 }
321 }
322 return (MediaTracker) trackerObj;
323 }
324
325 /**
326 * Returns the status of the image loading operation.
327 * @return the loading status as defined by java.awt.MediaTracker
328 * @see java.awt.MediaTracker#ABORTED
329 * @see java.awt.MediaTracker#ERRORED
330 * @see java.awt.MediaTracker#COMPLETE
331 */
332 public int getImageLoadStatus() {
333 return loadStatus;
334 }
335
336 /**
337 * Returns this icon's <code>Image</code>.
338 * @return the <code>Image</code> object for this <code>ImageIcon</code>
339 */
340 @Transient
341 public Image getImage() {
342 return image;
343 }
344
345 /**
346 * Sets the image displayed by this icon.
347 * @param image the image
348 */
349 public void setImage(Image image) {
350 this.image = image;
351 loadImage(image);
352 }
353
354 /**
355 * Gets the description of the image. This is meant to be a brief
356 * textual description of the object. For example, it might be
357 * presented to a blind user to give an indication of the purpose
358 * of the image.
359 * The description may be null.
360 *
361 * @return a brief textual description of the image
362 */
363 public String getDescription() {
364 return description;
365 }
366
367 /**
368 * Sets the description of the image. This is meant to be a brief
369 * textual description of the object. For example, it might be
370 * presented to a blind user to give an indication of the purpose
371 * of the image.
372 * @param description a brief textual description of the image
373 */
374 public void setDescription(String description) {
375 this.description = description;
376 }
377
378 /**
379 * Paints the icon.
380 * The top-left corner of the icon is drawn at
381 * the point (<code>x</code>, <code>y</code>)
382 * in the coordinate space of the graphics context <code>g</code>.
383 * If this icon has no image observer,
384 * this method uses the <code>c</code> component
385 * as the observer.
386 *
387 * @param c the component to be used as the observer
388 * if this icon has no image observer
389 * @param g the graphics context
390 * @param x the X coordinate of the icon's top-left corner
391 * @param y the Y coordinate of the icon's top-left corner
392 */
393 public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
394 if(imageObserver == null) {
395 g.drawImage(image, x, y, c);
396 } else {
397 g.drawImage(image, x, y, imageObserver);
398 }
399 }
400
401 /**
402 * Gets the width of the icon.
403 *
404 * @return the width in pixels of this icon
405 */
406 public int getIconWidth() {
407 return width;
408 }
409
410 /**
411 * Gets the height of the icon.
412 *
413 * @return the height in pixels of this icon
414 */
415 public int getIconHeight() {
416 return height;
417 }
418
419 /**
420 * Sets the image observer for the image. Set this
421 * property if the ImageIcon contains an animated GIF, so
422 * the observer is notified to update its display.
423 * For example:
424 * <pre>
425 * icon = new ImageIcon(...)
426 * button.setIcon(icon);
427 * icon.setImageObserver(button);
428 * </pre>
429 *
430 * @param observer the image observer
431 */
432 public void setImageObserver(ImageObserver observer) {
433 imageObserver = observer;
434 }
435
436 /**
437 * Returns the image observer for the image.
438 *
439 * @return the image observer, which may be null
440 */
441 @Transient
442 public ImageObserver getImageObserver() {
443 return imageObserver;
444 }
445
446 /**
447 * Returns a string representation of this image.
448 *
449 * @return a string representing this image
450 */
451 public String toString() {
452 if (description != null) {
453 return description;
454 }
455 return super.toString();
456 }
457
458 private void readObject(ObjectInputStream s)
459 throws ClassNotFoundException, IOException
460 {
461 s.defaultReadObject();
462
463 int w = s.readInt();
464 int h = s.readInt();
465 int[] pixels = (int[])(s.readObject());
466
467 if (pixels != null) {
468 Toolkit tk = Toolkit.getDefaultToolkit();
469 ColorModel cm = ColorModel.getRGBdefault();
470 image = tk.createImage(new MemoryImageSource(w, h, cm, pixels, 0, w));
471 loadImage(image);
472 }
473 }
474
475
476 private void writeObject(ObjectOutputStream s)
477 throws IOException
478 {
479 s.defaultWriteObject();
480
481 int w = getIconWidth();
482 int h = getIconHeight();
483 int[] pixels = image != null? new int[w * h] : null;
484
485 if (image != null) {
486 try {
487 PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
488 pg.grabPixels();
489 if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
490 throw new IOException("failed to load image contents");
491 }
492 }
493 catch (InterruptedException e) {
494 throw new IOException("image load interrupted");
495 }
496 }
497
498 s.writeInt(w);
499 s.writeInt(h);
500 s.writeObject(pixels);
501 }
502
503 /**
504 * --- Accessibility Support ---
505 */
506
507 private AccessibleImageIcon accessibleContext = null;
508
509 /**
510 * Gets the AccessibleContext associated with this ImageIcon.
511 * For image icons, the AccessibleContext takes the form of an
512 * AccessibleImageIcon.
513 * A new AccessibleImageIcon instance is created if necessary.
514 *
515 * @return an AccessibleImageIcon that serves as the
516 * AccessibleContext of this ImageIcon
517 * @beaninfo
518 * expert: true
519 * description: The AccessibleContext associated with this ImageIcon.
520 * @since 1.3
521 */
522 public AccessibleContext getAccessibleContext() {
523 if (accessibleContext == null) {
524 accessibleContext = new AccessibleImageIcon();
525 }
526 return accessibleContext;
527 }
528
529 /**
530 * This class implements accessibility support for the
531 * <code>ImageIcon</code> class. It provides an implementation of the
532 * Java Accessibility API appropriate to image icon user-interface
533 * elements.
534 * <p>
535 * <strong>Warning:</strong>
536 * Serialized objects of this class will not be compatible with
537 * future Swing releases. The current serialization support is
538 * appropriate for short term storage or RMI between applications running
539 * the same version of Swing. As of 1.4, support for long term storage
540 * of all JavaBeans<sup><font size="-2">TM</font></sup>
541 * has been added to the <code>java.beans</code> package.
542 * Please see {@link java.beans.XMLEncoder}.
543 * @since 1.3
544 */
545 protected class AccessibleImageIcon extends AccessibleContext
546 implements AccessibleIcon, Serializable {
547
548 /*
549 * AccessibleContest implementation -----------------
550 */
551
552 /**
553 * Gets the role of this object.
554 *
555 * @return an instance of AccessibleRole describing the role of the
556 * object
557 * @see AccessibleRole
558 */
559 public AccessibleRole getAccessibleRole() {
560 return AccessibleRole.ICON;
561 }
562
563 /**
564 * Gets the state of this object.
565 *
566 * @return an instance of AccessibleStateSet containing the current
567 * state set of the object
568 * @see AccessibleState
569 */
570 public AccessibleStateSet getAccessibleStateSet() {
571 return null;
572 }
573
574 /**
575 * Gets the Accessible parent of this object. If the parent of this
576 * object implements Accessible, this method should simply return
577 * getParent().
578 *
579 * @return the Accessible parent of this object -- can be null if this
580 * object does not have an Accessible parent
581 */
582 public Accessible getAccessibleParent() {
583 return null;
584 }
585
586 /**
587 * Gets the index of this object in its accessible parent.
588 *
589 * @return the index of this object in its parent; -1 if this
590 * object does not have an accessible parent.
591 * @see #getAccessibleParent
592 */
593 public int getAccessibleIndexInParent() {
594 return -1;
595 }
596
597 /**
598 * Returns the number of accessible children in the object. If all
599 * of the children of this object implement Accessible, than this
600 * method should return the number of children of this object.
601 *
602 * @return the number of accessible children in the object.
603 */
604 public int getAccessibleChildrenCount() {
605 return 0;
606 }
607
608 /**
609 * Returns the nth Accessible child of the object.
610 *
611 * @param i zero-based index of child
612 * @return the nth Accessible child of the object
613 */
614 public Accessible getAccessibleChild(int i) {
615 return null;
616 }
617
618 /**
619 * Returns the locale of this object.
620 *
621 * @return the locale of this object
622 */
623 public Locale getLocale() throws IllegalComponentStateException {
624 return null;
625 }
626
627 /*
628 * AccessibleIcon implementation -----------------
629 */
630
631 /**
632 * Gets the description of the icon. This is meant to be a brief
633 * textual description of the object. For example, it might be
634 * presented to a blind user to give an indication of the purpose
635 * of the icon.
636 *
637 * @return the description of the icon
638 */
639 public String getAccessibleIconDescription() {
640 return ImageIcon.this.getDescription();
641 }
642
643 /**
644 * Sets the description of the icon. This is meant to be a brief
645 * textual description of the object. For example, it might be
646 * presented to a blind user to give an indication of the purpose
647 * of the icon.
648 *
649 * @param description the description of the icon
650 */
651 public void setAccessibleIconDescription(String description) {
652 ImageIcon.this.setDescription(description);
653 }
654
655 /**
656 * Gets the height of the icon.
657 *
658 * @return the height of the icon
659 */
660 public int getAccessibleIconHeight() {
661 return ImageIcon.this.height;
662 }
663
664 /**
665 * Gets the width of the icon.
666 *
667 * @return the width of the icon
668 */
669 public int getAccessibleIconWidth() {
670 return ImageIcon.this.width;
671 }
672
673 private void readObject(ObjectInputStream s)
674 throws ClassNotFoundException, IOException
675 {
676 s.defaultReadObject();
677 }
678
679 private void writeObject(ObjectOutputStream s)
680 throws IOException
681 {
682 s.defaultWriteObject();
683 }
684 } // AccessibleImageIcon
685 }