| /* MediaTracker.java -- Class used for keeping track of images | |
| Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt; | |
| import java.awt.image.ImageObserver; | |
| import java.util.ArrayList; | |
| /** | |
| * This class is used for keeping track of the status of various media | |
| * objects. | |
| * | |
| * Media objects are tracked by assigning them an ID. It is possible | |
| * to assign the same ID to mutliple objects, effectivly grouping them | |
| * together. In this case the status flags ({@link #statusID}) and error flag | |
| * (@link #isErrorID} and {@link #getErrorsID}) are ORed together. This | |
| * means that you cannot say exactly which media object has which status, | |
| * at most you can say that there <em>are</em> certain media objects with | |
| * some certain status. | |
| * | |
| * At the moment only images are supported by this class. | |
| * | |
| * @author Aaron M. Renn (arenn@urbanophile.com) | |
| * @author Bryce McKinlay | |
| */ | |
| public class MediaTracker implements java.io.Serializable | |
| { | |
| /** Indicates that the media is still loading. */ | |
| public static final int LOADING = 1 << 0; | |
| /** Indicates that the loading operation has been aborted. */ | |
| public static final int ABORTED = 1 << 1; | |
| /** Indicates that an error has occured during loading of the media. */ | |
| public static final int ERRORED = 1 << 2; | |
| /** Indicates that the media has been successfully and completely loaded. */ | |
| public static final int COMPLETE = 1 << 3; | |
| /** The component on which the media is eventually been drawn. */ | |
| Component target; | |
| /** The head of the linked list of tracked media objects. */ | |
| MediaEntry head; | |
| /** Our serialVersionUID for serialization. */ | |
| static final long serialVersionUID = -483174189758638095L; | |
| /** | |
| * This represents a media object that is tracked by a MediaTracker. | |
| * It also implements a simple linked list. | |
| */ | |
| // FIXME: The serialized form documentation says MediaEntry is a | |
| // serializable field, but the serialized form of MediaEntry itself | |
| // doesn't appear to be documented. | |
| class MediaEntry implements ImageObserver | |
| { | |
| /** The ID of the media object. */ | |
| int id; | |
| /** The media object. (only images are supported ATM). */ | |
| Image image; | |
| /** The link to the next entry in the list. */ | |
| MediaEntry next; | |
| /** The tracking status. */ | |
| int status; | |
| /** The width of the image. */ | |
| int width; | |
| /** The height of the image. */ | |
| int height; | |
| /** | |
| * Receives notification from an {@link java.awt.image.ImageProducer} | |
| * that more data of the image is available. | |
| * | |
| * @param img the image that is updated | |
| * @param flags flags from the ImageProducer that indicate the status | |
| * of the loading process | |
| * @param x the X coordinate of the upper left corner of the image | |
| * @param y the Y coordinate of the upper left corner of the image | |
| * @param width the width of the image | |
| * @param height the height of the image | |
| * | |
| * @return <code>true</code> if more data is needed, <code>false</code> | |
| * otherwise | |
| * | |
| * @see java.awt.image.ImageObserver | |
| */ | |
| public boolean imageUpdate(Image img, int flags, int x, int y, | |
| int width, int height) | |
| { | |
| if ((flags & ABORT) != 0) | |
| status = ABORTED; | |
| else if ((flags & ERROR) != 0) | |
| status = ERRORED; | |
| else if ((flags & ALLBITS) != 0) | |
| status = COMPLETE; | |
| else | |
| status = 0; | |
| synchronized (MediaTracker.this) | |
| { | |
| MediaTracker.this.notifyAll(); | |
| } | |
| // If status is not COMPLETE then we need more updates. | |
| return ((status & (COMPLETE | ERRORED | ABORTED)) == 0); | |
| } | |
| } | |
| /** | |
| * Constructs a new MediaTracker for the component <code>c</code>. The | |
| * component should be the component that uses the media (i.e. draws it). | |
| * | |
| * @param c the Component that wants to use the media | |
| */ | |
| public MediaTracker(Component c) | |
| { | |
| target = c; | |
| } | |
| /** | |
| * Adds an image to the tracker with the specified <code>ID</code>. | |
| * | |
| * @param image the image to be added | |
| * @param id the ID of the tracker list to which the image is added | |
| */ | |
| public void addImage(Image image, int id) | |
| { | |
| MediaEntry e = new MediaEntry(); | |
| e.id = id; | |
| e.image = image; | |
| synchronized(this) | |
| { | |
| e.next = head; | |
| head = e; | |
| } | |
| } | |
| /** | |
| * Adds an image to the tracker with the specified <code>ID</code>. | |
| * The image is expected to be rendered with the specified width and | |
| * height. | |
| * | |
| * @param image the image to be added | |
| * @param id the ID of the tracker list to which the image is added | |
| * @param width the width of the image | |
| * @param height the height of the image | |
| */ | |
| public void addImage(Image image, int id, int width, int height) | |
| { | |
| MediaEntry e = new MediaEntry(); | |
| e.id = id; | |
| e.image = image; | |
| e.width = width; | |
| e.height = height; | |
| synchronized(this) | |
| { | |
| e.next = head; | |
| head = e; | |
| } | |
| } | |
| /** | |
| * Checks if all media objects have finished loading, i.e. are | |
| * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. | |
| * | |
| * If the media objects are not already loading, a call to this | |
| * method does <em>not</em> start loading. This is equivalent to | |
| * a call to <code>checkAll(false)</code>. | |
| * | |
| * @return if all media objects have finished loading either by beeing | |
| * complete, have been aborted or errored. | |
| */ | |
| public boolean checkAll() | |
| { | |
| return checkAll(false); | |
| } | |
| /** | |
| * Checks if all media objects have finished loading, i.e. are | |
| * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. | |
| * | |
| * If the media objects are not already loading, and <code>load</code> | |
| * is <code>true</code> then a call to this | |
| * method starts loading the media objects. | |
| * | |
| * @param load if <code>true</code> this method starts loading objects | |
| * that are not already loading | |
| * | |
| * @return if all media objects have finished loading either by beeing | |
| * complete, have been aborted or errored. | |
| */ | |
| public boolean checkAll(boolean load) | |
| { | |
| MediaEntry e = head; | |
| boolean result = true; | |
| while (e != null) | |
| { | |
| if ((e.status & (COMPLETE | ERRORED | ABORTED)) == 0) | |
| { | |
| if (load && ((e.status & LOADING) == 0)) | |
| { | |
| if (target.prepareImage(e.image, e)) | |
| e.status = COMPLETE; | |
| else | |
| { | |
| e.status = LOADING; | |
| int flags = target.checkImage(e.image, e); | |
| if ((flags & ImageObserver.ABORT) != 0) | |
| e.status = ABORTED; | |
| else if ((flags & ImageObserver.ERROR) != 0) | |
| e.status = ERRORED; | |
| else if ((flags & ImageObserver.ALLBITS) != 0) | |
| e.status = COMPLETE; | |
| } | |
| boolean complete = (e.status | |
| & (COMPLETE | ABORTED | ERRORED)) != 0; | |
| if (!complete) | |
| result = false; | |
| } | |
| else | |
| result = false; | |
| } | |
| e = e.next; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Checks if any of the registered media objects has encountered an error | |
| * during loading. | |
| * | |
| * @return <code>true</code> if at least one media object has encountered | |
| * an error during loading, <code>false</code> otherwise | |
| * | |
| */ | |
| public boolean isErrorAny() | |
| { | |
| MediaEntry e = head; | |
| while (e != null) | |
| { | |
| if ((e.status & ERRORED) != 0) | |
| return true; | |
| e = e.next; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns all media objects that have encountered errors during loading. | |
| * | |
| * @return an array of all media objects that have encountered errors | |
| * or <code>null</code> if there were no errors at all | |
| */ | |
| public Object[] getErrorsAny() | |
| { | |
| MediaEntry e = head; | |
| ArrayList result = null; | |
| while (e != null) | |
| { | |
| if ((e.status & ERRORED) != 0) | |
| { | |
| if (result == null) | |
| result = new ArrayList(); | |
| result.add(e.image); | |
| } | |
| e = e.next; | |
| } | |
| if (result == null) | |
| return null; | |
| else | |
| return result.toArray(); | |
| } | |
| /** | |
| * Waits for all media objects to finish loading, either by completing | |
| * successfully or by aborting or encountering an error. | |
| * | |
| * @throws InterruptedException if another thread interrupted the | |
| * current thread while waiting | |
| */ | |
| public void waitForAll() throws InterruptedException | |
| { | |
| synchronized (this) | |
| { | |
| while (checkAll(true) == false) | |
| wait(); | |
| } | |
| } | |
| /** | |
| * Waits for all media objects to finish loading, either by completing | |
| * successfully or by aborting or encountering an error. | |
| * | |
| * This method waits at most <code>ms</code> milliseconds. If the | |
| * media objects have not completed loading within this timeframe, this | |
| * method returns <code>false</code>, otherwise <code>true</code>. | |
| * | |
| * @param ms timeframe in milliseconds to wait for the media objects to | |
| * finish | |
| * | |
| * @return <code>true</code> if all media objects have successfully loaded | |
| * within the timeframe, <code>false</code> otherwise | |
| * | |
| * @throws InterruptedException if another thread interrupted the | |
| * current thread while waiting | |
| */ | |
| public boolean waitForAll(long ms) throws InterruptedException | |
| { | |
| long start = System.currentTimeMillis(); | |
| boolean result = checkAll(true); | |
| synchronized (this) | |
| { | |
| while (result == false) | |
| { | |
| wait(ms); | |
| result = checkAll(true); | |
| if ((System.currentTimeMillis() - start) > ms) | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * Returns the status flags of all registered media objects ORed together. | |
| * If <code>load</code> is <code>true</code> then media objects that | |
| * are not already loading will be started to load. | |
| * | |
| * @param load if set to <code>true</code> then media objects that are | |
| * not already loading are started | |
| * | |
| * @return the status flags of all tracked media objects ORed together | |
| */ | |
| public int statusAll(boolean load) | |
| { | |
| int result = 0; | |
| MediaEntry e = head; | |
| while (e != null) | |
| { | |
| if (load && e.status == 0) | |
| { | |
| if (target.prepareImage(e.image, e)) | |
| e.status = COMPLETE; | |
| else | |
| { | |
| e.status = LOADING; | |
| int flags = target.checkImage(e.image, e); | |
| if ((flags & ImageObserver.ABORT) != 0) | |
| e.status = ABORTED; | |
| else if ((flags & ImageObserver.ERROR) != 0) | |
| e.status = ERRORED; | |
| else if ((flags & ImageObserver.ALLBITS) != 0) | |
| e.status = COMPLETE; | |
| } | |
| } | |
| result |= e.status; | |
| e = e.next; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Checks if the media objects with <code>ID</code> have completed loading. | |
| * | |
| * @param id the ID of the media objects to check | |
| * | |
| * @return <code>true</code> if all media objects with <code>ID</code> | |
| * have successfully finished | |
| */ | |
| public boolean checkID(int id) | |
| { | |
| return checkID(id, false); | |
| } | |
| /** | |
| * Checks if the media objects with <code>ID</code> have completed loading. | |
| * If <code>load</code> is <code>true</code> then media objects that | |
| * are not already loading will be started to load. | |
| * | |
| * @param id the ID of the media objects to check | |
| * @param load if set to <code>true</code> then media objects that are | |
| * not already loading are started | |
| * | |
| * @return <code>true</code> if all media objects with <code>ID</code> | |
| * have successfully finished | |
| */ | |
| public boolean checkID(int id, boolean load) | |
| { | |
| MediaEntry e = head; | |
| boolean result = true; | |
| while (e != null) | |
| { | |
| if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0)) | |
| { | |
| if (load && ((e.status & LOADING) == 0)) | |
| { | |
| e.status = LOADING; | |
| if (target.prepareImage(e.image, e)) | |
| e.status = COMPLETE; | |
| else | |
| { | |
| int flags = target.checkImage(e.image, e); | |
| if ((flags & ImageObserver.ABORT) != 0) | |
| e.status = ABORTED; | |
| else if ((flags & ImageObserver.ERROR) != 0) | |
| e.status = ERRORED; | |
| else if ((flags & ImageObserver.ALLBITS) != 0) | |
| e.status = COMPLETE; | |
| } | |
| boolean complete = (e.status | |
| & (COMPLETE | ABORTED | ERRORED)) != 0; | |
| if (!complete) | |
| result = false; | |
| } | |
| else | |
| result = false; | |
| } | |
| e = e.next; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Returns <code>true</code> if any of the media objects with <code>ID</code> | |
| * have encountered errors during loading, false otherwise. | |
| * | |
| * @param id the ID of the media objects to check | |
| * | |
| * @return <code>true</code> if any of the media objects with <code>ID</code> | |
| * have encountered errors during loading, false otherwise | |
| */ | |
| public boolean isErrorID(int id) | |
| { | |
| MediaEntry e = head; | |
| while (e != null) | |
| { | |
| if (e.id == id && ((e.status & ERRORED) != 0)) | |
| return true; | |
| e = e.next; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns all media objects with the specified ID that have encountered | |
| * an error. | |
| * | |
| * @param id the ID of the media objects to check | |
| * | |
| * @return an array of all media objects with the specified ID that | |
| * have encountered an error | |
| */ | |
| public Object[] getErrorsID(int id) | |
| { | |
| MediaEntry e = head; | |
| ArrayList result = null; | |
| while (e != null) | |
| { | |
| if (e.id == id && ((e.status & ERRORED) != 0)) | |
| { | |
| if (result == null) | |
| result = new ArrayList(); | |
| result.add(e.image); | |
| } | |
| e = e.next; | |
| } | |
| if (result == null) | |
| return null; | |
| else | |
| return result.toArray(); | |
| } | |
| /** | |
| * Waits for all media objects with the specified ID to finish loading, | |
| * either by completing successfully or by aborting or encountering an error. | |
| * | |
| * @param id the ID of the media objects to wait for | |
| * | |
| * @throws InterruptedException if another thread interrupted the | |
| * current thread while waiting | |
| */ | |
| public void waitForID(int id) throws InterruptedException | |
| { | |
| MediaEntry e = head; | |
| synchronized (this) | |
| { | |
| while (checkID (id, true) == false) | |
| wait(); | |
| } | |
| } | |
| /** | |
| * Waits for all media objects with the specified ID to finish loading, | |
| * either by completing successfully or by aborting or encountering an error. | |
| * | |
| * This method waits at most <code>ms</code> milliseconds. If the | |
| * media objects have not completed loading within this timeframe, this | |
| * method returns <code>false</code>, otherwise <code>true</code>. | |
| * | |
| * @param id the ID of the media objects to wait for | |
| * @param ms timeframe in milliseconds to wait for the media objects to | |
| * finish | |
| * | |
| * @return <code>true</code> if all media objects have successfully loaded | |
| * within the timeframe, <code>false</code> otherwise | |
| * | |
| * @throws InterruptedException if another thread interrupted the | |
| * current thread while waiting | |
| */ | |
| public boolean waitForID(int id, long ms) throws InterruptedException | |
| { | |
| MediaEntry e = head; | |
| long start = System.currentTimeMillis(); | |
| boolean result = checkID(id, true); | |
| synchronized (this) | |
| { | |
| while (result == false) | |
| { | |
| wait(ms); | |
| result = checkID(id, true); | |
| if ((System.currentTimeMillis() - start) > ms) | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * Returns the status flags of the media objects with the specified ID | |
| * ORed together. | |
| * | |
| * If <code>load</code> is <code>true</code> then media objects that | |
| * are not already loading will be started to load. | |
| * | |
| * @param load if set to <code>true</code> then media objects that are | |
| * not already loading are started | |
| * | |
| * @return the status flags of all tracked media objects ORed together | |
| */ | |
| public int statusID(int id, boolean load) | |
| { | |
| int result = 0; | |
| MediaEntry e = head; | |
| while (e != null) | |
| { | |
| if (e.id == id) | |
| { | |
| if (load && e.status == 0) | |
| { | |
| if (target.prepareImage(e.image, e)) | |
| e.status = COMPLETE; | |
| else | |
| { | |
| e.status = LOADING; | |
| int flags = target.checkImage(e.image, e); | |
| if ((flags & ImageObserver.ABORT) != 0) | |
| e.status = ABORTED; | |
| else if ((flags & ImageObserver.ERROR) != 0) | |
| e.status = ERRORED; | |
| else if ((flags & ImageObserver.ALLBITS) != 0) | |
| e.status = COMPLETE; | |
| } | |
| } | |
| result |= e.status; | |
| } | |
| e = e.next; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Removes an image from this MediaTracker. | |
| * | |
| * @param image the image to be removed | |
| */ | |
| public void removeImage(Image image) | |
| { | |
| synchronized (this) | |
| { | |
| MediaEntry e = head; | |
| MediaEntry prev = null; | |
| while (e != null) | |
| { | |
| if (e.image == image) | |
| { | |
| if (prev == null) | |
| head = e.next; | |
| else | |
| prev.next = e.next; | |
| } | |
| else | |
| prev = e; | |
| e = e.next; | |
| } | |
| } | |
| } | |
| /** | |
| * Removes an image with the specified ID from this MediaTracker. | |
| * | |
| * @param image the image to be removed | |
| */ | |
| public void removeImage(Image image, int id) | |
| { | |
| synchronized (this) | |
| { | |
| MediaEntry e = head; | |
| MediaEntry prev = null; | |
| while (e != null) | |
| { | |
| if (e.id == id && e.image == image) | |
| { | |
| if (prev == null) | |
| head = e.next; | |
| else | |
| prev.next = e.next; | |
| } | |
| else | |
| prev = e; | |
| e = e.next; | |
| } | |
| } | |
| } | |
| /** | |
| * Removes an image with the specified ID and scale from this MediaTracker. | |
| * | |
| * @param image the image to be removed | |
| */ | |
| public void removeImage(Image image, int id, int width, int height) | |
| { | |
| synchronized (this) | |
| { | |
| MediaEntry e = head; | |
| MediaEntry prev = null; | |
| while (e != null) | |
| { | |
| if (e.id == id && e.image == image | |
| && e.width == width && e.height == height) | |
| { | |
| if (prev == null) | |
| head = e.next; | |
| else | |
| prev.next = e.next; | |
| } | |
| else | |
| prev = e; | |
| e = e.next; | |
| } | |
| } | |
| } | |
| } |