diff options
Diffstat (limited to 'libjava/classpath/javax')
459 files changed, 49128 insertions, 13806 deletions
diff --git a/libjava/classpath/javax/imageio/IIOException.java b/libjava/classpath/javax/imageio/IIOException.java index 8f8dd03441c..b281db1b050 100644 --- a/libjava/classpath/javax/imageio/IIOException.java +++ b/libjava/classpath/javax/imageio/IIOException.java @@ -39,8 +39,9 @@ package javax.imageio; import java.io.IOException; - /** + * A runtime exception to indicate image reading and writing failures. + * * @author Michael Koch (konqueror@gmx.de) */ public class IIOException extends IOException diff --git a/libjava/classpath/javax/imageio/IIOImage.java b/libjava/classpath/javax/imageio/IIOImage.java index 651c9baaa21..0d987476239 100644 --- a/libjava/classpath/javax/imageio/IIOImage.java +++ b/libjava/classpath/javax/imageio/IIOImage.java @@ -45,13 +45,53 @@ import java.util.List; import javax.imageio.metadata.IIOMetadata; +/** + * IIOImage is a container class for components of an image file that + * stores image data, image metadata and thumbnails. + * + * The image data can be either a RenderedImage or a Raster but not + * both. Image readers that produce IIOImages will always produce + * BufferedImages from the RenderedImage field. Image writers that + * accept IIOImages will always accept RenderedImages and may + * optionally accept Rasters. + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + */ public class IIOImage { + /** + * Image data as a RenderedImage. null if this IIOImage uses the + * Raster representation. + */ protected RenderedImage image; + + /** + * Image metadata. + */ protected IIOMetadata metadata; + + /** + * Image data as a Raster. null if this IIOImage uses the + * RenderedImage representation. + */ protected Raster raster; + + /** + * A list of BufferedImage thumbnails of this image. + */ + // for 1.5 these lists are List<? extends BufferedImage> protected List thumbnails; - + + /** + * Construct an IIOImage containing raster image data, thumbnails + * and metadata. + * + * @param raster image data + * @param thumbnails a list of BufferedImage thumbnails or null + * @param metadata image metadata or null + * + * @exception IllegalArgumentException if raster is null + */ public IIOImage (Raster raster, List thumbnails, IIOMetadata metadata) { if (raster == null) @@ -62,6 +102,16 @@ public class IIOImage this.metadata = metadata; } + /** + * Construct an IIOImage containing rendered image data, thumbnails + * and metadata. + * + * @param image rendered image data + * @param thumbnails a list of BufferedImage thumbnails or null + * @param metadata image metadata or null + * + * @exception IllegalArgumentException if image is null + */ public IIOImage (RenderedImage image, List thumbnails, IIOMetadata metadata) { if (image == null) @@ -72,46 +122,111 @@ public class IIOImage this.metadata = metadata; } + /** + * Retrieve the image metadata or null if there is no metadata + * associated with this IIOImage. + * + * @return image metadata or null + */ public IIOMetadata getMetadata() { return metadata; } + /** + * Retrieve the number of thumbnails in this IIOImage. + * + * @return the number of thumbnails + */ public int getNumThumbnails() { - return thumbnails.size(); + return thumbnails == null ? 0 : thumbnails.size(); } + /** + * Retrieve the raster image data stored in this IIOImage or null if + * this image stores data using the RenderedImage representation. + * + * @return the raster image data or null + */ public Raster getRaster() { return raster; } + /** + * Retrieve the rendered image data stored in this IIOImage or null + * if this image stores data using the Raster representation. + * + * @return the rendered image data or null + */ public RenderedImage getRenderedImage() { return image; } + /** + * Retrieve the thumbnail stored at the specified index in the + * thumbnails list. + * + * @param index the index of the thumbnail to retrieve + * + * @return the buffered image thumbnail + * + * @exception IndexOutOfBoundsException if index is out-of-bounds + * @exception ClassCastException if the object returned from the + * thumbnails list is not a BufferedImage + */ public BufferedImage getThumbnail (int index) { + // This throws a ClassCastException if the returned object is not + // a BufferedImage or an IndexOutOfBoundsException if index is + // out-of-bounds. return (BufferedImage) thumbnails.get (index); } + /** + * Retrieve the list of thumbnails or null if there are no + * thumbnails associated with this IIOImage. The returned reference + * can be used to update the thumbnails list. + * + * @return a list of thumbnails or null + */ public List getThumbnails() { return thumbnails; } + /** + * Check whether this IIOImage stores its image data as a Raster or + * as a RenderedImage. + * + * @return true if this IIOImage uses the Raster representation, + * false if it uses the RenderedImage representation. + */ public boolean hasRaster() { return raster != null; } + /** + * Set this IIOImage's metadata. + * + * @param metadata the image metadata + */ public void setMetadata (IIOMetadata metadata) { this.metadata = metadata; } + /** + * Set the raster data for this image. This disposes of any + * existing rendered image data stored in this IIOImage. + * + * @param raster the image raster data + * + * @exception IllegalArgumentException if raster is null + */ public void setRaster (Raster raster) { if (raster == null) @@ -121,6 +236,14 @@ public class IIOImage this.raster = raster; } + /** + * Set the rendered image data for this image. This disposes of any + * existing raster data stored in this IIOImage. + * + * @param image the rendered image data + * + * @exception IllegalArgumentException if image is null + */ public void setRenderedImage (RenderedImage image) { if (image == null) @@ -130,9 +253,15 @@ public class IIOImage this.raster = null; } + /** + * Set the list of thumbnails for this IIOImage to a new list of + * BufferedImages or to null. Any existing thumbnails list is + * disposed. + * + * @param thumbnails a new list of thumbnails or null + */ public void setThumbnails (List thumbnails) { this.thumbnails = thumbnails; } - -} // class IIOParam +} diff --git a/libjava/classpath/javax/imageio/IIOParam.java b/libjava/classpath/javax/imageio/IIOParam.java index 01f6166059d..f6460b4e7ed 100644 --- a/libjava/classpath/javax/imageio/IIOParam.java +++ b/libjava/classpath/javax/imageio/IIOParam.java @@ -42,97 +42,317 @@ import java.awt.Point; import java.awt.Rectangle; /** + * An IIOParam stores parameters used when encoding or decoding image + * streams. ImageReadParam and ImageWriteParam extend this abstract + * base class. + * + * IIOParams allow control over how source pixels converted into + * destination pixels. This conversion can take place between a + * stream and in-memory image data, when an image reader is doing the + * conversion, or a writer can be doing the conversion from an + * in-memory source to a stream destination. + * + * An image reader can be restricted to only read from a given region; + * likewise a writer can be restricted to only write output to a given + * region. + * + * For image readers and writers, IIOParam supports image pixelation + * -- where the input image is approximated by the output image using + * larger-sized pixel blocks. For example: FIXME + * + * IIOParams can control how pixels are combined into larger blocks + * using sub-sampling matrices. For example: FIXME + * + * They can also control which source bands are read and written; this + * example reads the RGBA (red, green, blue, transparency) data from a + * PNG image and outputs just the red and transparency bands: FIXME + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) * @author Michael Koch (konqueror@gmx.de) */ public abstract class IIOParam { - protected IIOParamController controller; - protected IIOParamController defaultController; + /** + * The controller called by this IIOParam to retrieve parameters. + */ + protected IIOParamController controller = null; + + /** + * The default controller called by this IIOParam to retrieve + * parameters. + */ + protected IIOParamController defaultController = null; + + /** + * The offset in the destination where the upper-left + * decoded/encoded pixel should be located. + */ protected Point destinationOffset = new Point(0, 0); - protected ImageTypeSpecifier destinationType; - protected int[] sourceBands; - protected Rectangle sourceRegion; - protected int sourceXSubsampling; - protected int sourceYSubsampling; - protected int subsamplingXOffset; - protected int subsamplingYOffset; /** - * Initializes an <code>IIOParam</code> object. + * Sets the output colour type when writing or the destination image + * type when reading. + */ + protected ImageTypeSpecifier destinationType = null; + + /** + * An array indicating which source bands will be used or null. + */ + protected int[] sourceBands = null; + + /** + * The source pixel region or null. + */ + protected Rectangle sourceRegion = null; + + /** + * Sample every sourceXSubsampling'th pixel in the source image when + * pixelating the destination image in the horizontal direction. + */ + protected int sourceXSubsampling = 1; + + /** + * Sample every sourceYSubsampling'th pixel in the source image when + * pixelating the destination image in the vertical direction. + */ + protected int sourceYSubsampling = 1; + + /** + * Start sampling at this horizontal offset within the source region + * when pixelating the destination image in the horizontal + * direction. + */ + protected int subsamplingXOffset = 0; + + /** + * Start sampling at this vertical offset within the source region + * when pixelating the destination image in the vertical direction. + */ + protected int subsamplingYOffset = 0; + + /** + * Indicates whether or not the controller has been explicitly set + * to null. + */ + private boolean no_controller = false; + + /** + * Constructs an IIOParam object. */ protected IIOParam() { - // Do nothing here. } + /** + * Activates the parameter controller by calling its activate method + * and passing it this IIOParam. A true return value indicates that + * this IIOParam's values are ready for the next read or write + * operation. A return value of false means that this IIOParam's + * values have not been affected because the controller operations + * were cancelled. + * + * @return true if parameters were successfully set, false if + * parameters were not changed + */ public boolean activateController() { if (controller == null) - return false; - - return controller.activate(this); + { + if (defaultController == null || no_controller) + return false; + else + return defaultController.activate (this); + } + else + return controller.activate(this); } - + + /** + * Retrieve the currently set controller if one has been set, or the + * default controller, or null if the controller has been explicitly + * set to null. + * + * @return the currently used controller or null + */ public IIOParamController getController() { - return controller; + return controller == null ? + (no_controller ? null : defaultController) : controller; } + /** + * Retrieve the default controller regardless of whether or not a + * non-default controller has been set. The default controller may + * be null. + * + * @return the default controller or null + */ public IIOParamController getDefaultController() { return defaultController; } + /** + * Retrieve the offset in the destination where the upper-left + * decoded/encoded pixel should be located. (0, 0) by default. + * + * @return the destination offset + */ public Point getDestinationOffset() { return destinationOffset; } + /** + * Retrieve the currently set image-type specifier or null if none + * has been set. + * + * @return the current image-type specifier or null + */ public ImageTypeSpecifier getDestinationType() { return destinationType; } + /** + * Retrieve the current source band values or null if source band + * values have not been set. + * + * The returned array is a copy of this IIOParam's source band + * array. + * + * @return the current set of source band values or null + */ public int[] getSourceBands() { - return sourceBands; + if (sourceBands == null) + return null; + + int[] sourceBandsCopy = new int[sourceBands.length]; + System.arraycopy (sourceBands, 0, sourceBandsCopy, 0, sourceBands.length); + return sourceBandsCopy; } + /** + * Retrieve the source rectangle from which pixels should be read or + * null if no source region has been set. + * + * @return the current source region or null + */ public Rectangle getSourceRegion() { return sourceRegion; } + /** + * Retrieve the number of pixel columns to advance before taking a + * pixel sample. + * + * @return the horizontal sub-sampling interval + */ public int getSourceXSubsampling() { return sourceXSubsampling; } + /** + * Retrieve the number of pixel rows to advance before taking a + * pixel sample. + * + * @return the vertical sub-sampling interval + */ public int getSourceYSubsampling() { return sourceYSubsampling; } + /** + * Retrieve the number of pixel columns to advance before taking any + * pixel samples. + * + * @return the horizontal sub-sampling offset + */ public int getSubsamplingXOffset() { return subsamplingXOffset; } - + + /** + * Retrieve the number of pixel rows to advance before taking any + * pixel samples. + * + * @return the vertical sub-sampling offset + */ public int getSubsamplingYOffset() { return subsamplingYOffset; } + /** + * Check if a non-null controller is currently available. + * + * @return true if getController returns a non-null value, false if + * getController returns null + */ public boolean hasController() { return getController() != null; } + /** + * Sets the controller for this IIOParam. This is the controller + * that will be activated when activateController is called. The + * argument controller overrides this IIOParam's default controller. + * If the argument is null then no controller will be set, not even + * the default one. To reset the default controller call + * setController(getDefaultController()). + * + * @param controller the controller to set or null + */ public void setController(IIOParamController controller) { - this.controller = controller; + if (controller == defaultController) + { + this.controller = null; + no_controller = false; + } + else + { + no_controller = (controller == null); + this.controller = controller; + } } + /** + * Set the destination image type. + * + * If this value is set on an image reader then its read method will + * return a new BufferedImage of the specified destination type. In + * this case any destination image set using setDestination() is + * ignored. + * + * If this is set on an image writer then the destination type + * affects only the colour model of the destination image. The + * destination type's SampleModel is ignored. The destination + * type's ColorModel will override the source image's colour model. + * + * @param destinationType the sample and colour models of the + * destination image + */ + public void setDestinationType (ImageTypeSpecifier destinationType) + { + this.destinationType = destinationType; + } + + /** + * Specify the destination pixel offset. Image writers are only + * affected by this setting when ImageWriter.replacePixels is called + * in which case the offset is into the region of pixels being + * changed. + * + * @param destinationOffset the offset where pixel writing should + * begin + */ public void setDestinationOffset(Point destinationOffset) { if (destinationOffset == null) @@ -141,11 +361,43 @@ public abstract class IIOParam this.destinationOffset = destinationOffset; } + /** + * Set the indices of the source bands to be used. Duplicate + * indices are not allowed. A value of null means use all source + * bands. The argument array is copied and stored, so subsequent + * updates to it will not be reflected in this IIOParam. + * + * @param sourceBands the array of source bands to use + */ public void setSourceBands(int[] sourceBands) { - this.sourceBands = sourceBands; + int[] sourceBandsCopy = new int[sourceBands.length]; + System.arraycopy (sourceBands, 0, sourceBandsCopy, 0, sourceBands.length); + this.sourceBands = sourceBandsCopy; } + /** + * Set the source region from which to read. The number of pixels + * sampled from the source region depends on the source sub-sampling + * settings. If the combination of this sourceRegion and the + * current sub-sampling settings would result in no pixels being + * sampled then an IllegalStateException will be thrown. + * + * The source region is specified in the source image coordinate + * system which has point (0, 0) at the top-left and increases down + * and to the right. The argument source region is clipped to the + * image boundaries at read-time. + * + * A null argument sets the source region to null meaning that the + * whole image should be read. + * + * @param sourceRegion the rectangular source region + * + * @exception IllegalArgumentException if sourceRegion has width or + * height <= 0 or x or y < 0 + * @exception IllegalStateException if the given sourceRegion and + * the current sampling settings would produce zero samples + */ public void setSourceRegion(Rectangle sourceRegion) { if (sourceRegion != null @@ -154,15 +406,83 @@ public abstract class IIOParam || sourceRegion.width <= 0 || sourceRegion.height <= 0)) throw new IllegalArgumentException("illegal source region"); - - // FIXME: Throw IllegalStateException. + + if (sourceRegion != null) + { + int num_rows = + (sourceRegion.height - subsamplingYOffset + sourceYSubsampling - 1) + / sourceYSubsampling; + + int num_columns = + (sourceRegion.width - subsamplingXOffset + sourceXSubsampling - 1) + / sourceXSubsampling; + + if (num_rows <= 0 || num_columns <= 0) + throw new IllegalStateException("zero pixels in source region"); + } this.sourceRegion = sourceRegion; } + /** + * Set the source sampling intervals and offsets. Every + * sourceXSubsampling'th pixel horizontally and + * sourceYSubsampling'th pixel vertically will be sampled. Sampling + * will being a the subsamplingXOffset'th column and the + * subsamplingYOffset'th row. + * + * Horizontally, the number of sampled pixels will be: + * + * floor((width - subsamplingXOffset + sourceXSubsampling - 1) / sourceXSubsampling) + * + * Vertically: + * + * floor((height - subsamplingYOffset + sourceYSubsampling - 1) / sourceYSubsampling) + * + * If the current source region setting is such that the given + * sub-sampling arguments would produce zero pixel samples, an + * IllegalStateException is thrown. + * + * The offset parameters can be used to make source regions overlap + * when tiling across an image. This can eliminate seams and + * better-tile images whose width or height is not a multiple of the + * sampling interval. + * + * @param sourceXSubsampling the horizontal sampling interval + * @param sourceYSubsampling the vertical sampling interval + * @param subsamplingXOffset the horizontal offset of the initial + * sample + * @param subsamplingYOffset the vertical offset of the initial + * sample + * + * @exception IllegalArgumentException if either subsamplingXOffset + * or subsamplingYOffset is < 0 + * @exception IllegalStateException if the current source region + * combined with the given sub-sampling parameters would produce + * zero pixel samples + */ public void setSourceSubsampling(int sourceXSubsampling, int sourceYSubsampling, int subsamplingXOffset, int subsamplingYOffset) { + if (subsamplingXOffset < 0 || subsamplingYOffset < 0) + throw new IllegalArgumentException("subsampling offset < 0"); + + if (sourceRegion != null) + { + int num_rows = + (sourceRegion.height - subsamplingYOffset + sourceYSubsampling - 1) + / sourceYSubsampling; + + int num_columns = + (sourceRegion.width - subsamplingXOffset + sourceXSubsampling - 1) + / sourceXSubsampling; + + if (num_rows <= 0 || num_columns <= 0) + throw new IllegalStateException("subsampling parameters would" + + " produce zero pixel samples" + + " in source region"); + } + this.sourceXSubsampling = sourceXSubsampling; this.sourceYSubsampling = sourceYSubsampling; this.subsamplingXOffset = subsamplingXOffset; diff --git a/libjava/classpath/javax/imageio/IIOParamController.java b/libjava/classpath/javax/imageio/IIOParamController.java index 125520e735b..0ee54df4071 100644 --- a/libjava/classpath/javax/imageio/IIOParamController.java +++ b/libjava/classpath/javax/imageio/IIOParamController.java @@ -39,12 +39,23 @@ exception statement from your version. */ package javax.imageio; /** + * An interface to set image parameters. An IIOParamController may be + * a GUI component, a database reader, command-line parser or any + * other means of getting parameter settings. For exampe, a dialog + * box could implement IIOParamController to allow a user to adjust + * JPEG compression levels. + * + * The activate method should always behave modally; it should only + * return when the action has been either cancelled or completed. + * * @author Michael Koch (konqueror@gmx.de) */ public interface IIOParamController { /** - * Activates the controller. + * Activates the controller. A return value of false should mean + * that no changes were made to param. A return value of true + * should mean that the image is ready to be read or written. * * @param param the <code>IIOParam</code> to be modified * diff --git a/libjava/classpath/javax/imageio/ImageIO.java b/libjava/classpath/javax/imageio/ImageIO.java index 95c7c325121..3ea7e858544 100644 --- a/libjava/classpath/javax/imageio/ImageIO.java +++ b/libjava/classpath/javax/imageio/ImageIO.java @@ -52,7 +52,10 @@ import java.util.Collections; import java.util.Iterator; import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageInputStreamSpi; +import javax.imageio.spi.ImageOutputStreamSpi; import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ImageTranscoderSpi; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.spi.ServiceRegistry; import javax.imageio.stream.ImageInputStream; @@ -60,12 +63,18 @@ import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; +/** + * An uninstantiable class that provides static methods for locating + * and using image readers and writers. + */ public final class ImageIO { /** - * This class isn't intended to be instantiated. + * Construct an ImageIO. Private since ImageIO is not instantiable. */ - private ImageIO() {} + private ImageIO() + { + } private static final class ReaderFormatFilter implements ServiceRegistry.Filter { @@ -117,6 +126,35 @@ public final class ImageIO } } + private static final class ReaderObjectFilter implements ServiceRegistry.Filter + { + private Object object; + + public ReaderObjectFilter(Object object) + { + this.object = object; + } + + public boolean filter(Object provider) + { + if (provider instanceof ImageReaderSpi) + { + ImageReaderSpi spi = (ImageReaderSpi) provider; + + try + { + if (spi.canDecodeInput(object)) + return true; + } + catch (IOException e) + { + // Return false in this case + } + } + return false; + } + } + private static final class ReaderSuffixFilter implements ServiceRegistry.Filter { private String fileSuffix; @@ -217,6 +255,66 @@ public final class ImageIO } } + private static final class WriterObjectFilter implements ServiceRegistry.Filter + { + private ImageTypeSpecifier type; + private String formatName; + + public WriterObjectFilter(ImageTypeSpecifier type, + String formatName) + { + this.type = type; + this.formatName = formatName; + } + + public boolean filter(Object provider) + { + if (provider instanceof ImageWriterSpi) + { + ImageWriterSpi spi = (ImageWriterSpi) provider; + + if (spi.canEncodeImage(type)) + { + String[] formatNames = spi.getFormatNames(); + for (int i = formatNames.length - 1; i >= 0; --i) + if (formatName.equals(formatNames[i])) + return true; + } + } + + return false; + } + } + + private static final class TranscoderFilter implements ServiceRegistry.Filter + { + private ImageReader reader; + private ImageWriter writer; + + public TranscoderFilter(ImageReader reader, + ImageWriter writer) + { + this.reader = reader; + this.writer = writer; + } + + public boolean filter(Object provider) + { + if (provider instanceof ImageTranscoderSpi) + { + ImageTranscoderSpi spi = (ImageTranscoderSpi) provider; + + if (spi.getReaderServiceProviderName().equals + (reader.getOriginatingProvider().getClass().getName()) + && spi.getWriterServiceProviderName().equals + (writer.getOriginatingProvider().getClass().getName())) + return true; + } + + return false; + } + } + private static final class ImageReaderIterator implements Iterator { Iterator it; @@ -318,11 +416,26 @@ public final class ImageIO } } + /** + * Retrieve the current cache directory. + * + * @return the current cache directory or null if none is set. + */ public static File getCacheDirectory() { return cacheDirectory; } + /** + * Retrieve an iterator over all registered readers for the given + * format. + * + * @param formatName an infomal format name (e.g. "jpeg" or "bmp") + * + * @return an iterator over a collection of image readers + * + * @exception IllegalArgumentException if formatName is null + */ public static Iterator getImageReadersByFormatName(String formatName) { if (formatName == null) @@ -333,6 +446,17 @@ public final class ImageIO formatName); } + /** + * Retrieve an iterator over all registered readers for the given + * MIME type. + * + * @param MIMEType a MIME specification for an image type + * (e.g. "image/jpeg" or "image/x-bmp") + * + * @return an iterator over a collection of image readers + * + * @exception IllegalArgumentException if MIMEType is null + */ public static Iterator getImageReadersByMIMEType(String MIMEType) { if (MIMEType == null) @@ -343,6 +467,16 @@ public final class ImageIO MIMEType); } + /** + * Retrieve an iterator over all registered readers for the given + * file suffix. + * + * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp") + * + * @return an iterator over a collection of image readers + * + * @exception IllegalArgumentException if fileSuffix is null + */ public static Iterator getImageReadersBySuffix(String fileSuffix) { if (fileSuffix == null) @@ -353,6 +487,16 @@ public final class ImageIO fileSuffix); } + /** + * Retrieve an iterator over all registered writers for the given + * format. + * + * @param formatName an infomal format name (e.g. "jpeg" or "bmp") + * + * @return an iterator over a collection of image writers + * + * @exception IllegalArgumentException if formatName is null + */ public static Iterator getImageWritersByFormatName(String formatName) { if (formatName == null) @@ -363,6 +507,17 @@ public final class ImageIO formatName); } + /** + * Retrieve an iterator over all registered writers for the given + * MIME type. + * + * @param MIMEType a MIME specification for an image type + * (e.g. "image/jpeg" or "image/x-bmp") + * + * @return an iterator over a collection of image writers + * + * @exception IllegalArgumentException if MIMEType is null + */ public static Iterator getImageWritersByMIMEType(String MIMEType) { if (MIMEType == null) @@ -373,6 +528,16 @@ public final class ImageIO MIMEType); } + /** + * Retrieve an iterator over all registered writers for the given + * file suffix. + * + * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp") + * + * @return an iterator over a collection of image writers + * + * @exception IllegalArgumentException if fileSuffix is null + */ public static Iterator getImageWritersBySuffix(String fileSuffix) { if (fileSuffix == null) @@ -383,6 +548,12 @@ public final class ImageIO fileSuffix); } + /** + * Retrieve all the informal format names supported by the + * collection of registered image readers. + * + * @return an array of format names + */ public static String[] getReaderFormatNames() { try @@ -408,6 +579,12 @@ public final class ImageIO } } + /** + * Retrieve all the MIME types supported by the collection of + * registered image readers. + * + * @return an array of MIME types + */ public static String[] getReaderMIMETypes() { try @@ -438,11 +615,23 @@ public final class ImageIO return IIORegistry.getDefaultInstance(); } + /** + * Check whether or not an on-disk cache is used for image input and + * output streams. + * + * @return true if an on-disk cache is available, false otherwise + */ public static boolean getUseCache() { return useCache; } + /** + * Retrieve all the informal format names supported by the + * collection of registered image writers. + * + * @return an array of format names + */ public static String[] getWriterFormatNames() { try @@ -468,6 +657,12 @@ public final class ImageIO } } + /** + * Retrieve all the MIME types supported by the collection of + * registered image writers. + * + * @return an array of MIME types + */ public static String[] getWriterMIMETypes() { try @@ -502,8 +697,20 @@ public final class ImageIO IIORegistry.getDefaultInstance().registerApplicationClasspathSpis(); } + /** + * Set the directory to be used for caching image data. A null + * argument means to use the default system temporary directory. + * This cache directory is only used if getUseCache returns true. + * + * @param cacheDirectory the directory where image data should be + * cached + * + * @exception IllegalArgumentException if cacheDirectory is not a + * directory + */ public static void setCacheDirectory(File cacheDirectory) { + // FIXME: add SecurityManager call if (cacheDirectory != null) { if (!cacheDirectory.isDirectory()) @@ -515,37 +722,98 @@ public final class ImageIO ImageIO.cacheDirectory = cacheDirectory; } + /** + * Control whether or not an on-disk cache is used. This cache is + * used to store input or output data from an image data stream when + * data in the stream needs to be re-processed. + * + * If useCache is false the cache will be stored in memory. Doing + * so eliminates file creation and deletion overhead. The default + * is to use an on-disk cache. + * + * @param useCache true to use an on-disk cache, false otherwise + */ public static void setUseCache(boolean useCache) { ImageIO.useCache = useCache; } - /* - * "Standard" simplified entry points. + /** + * Write an image to a file using a registered writer that supports + * the given format, overwriting the file if it already exists. + * + * @param im the image data to write + * @param formatName an informal description of the output format + * @param output the file to which the image will be written + * + * @return false if no registered writer supports the given format, + * true otherwise + * + * @exception IllegalArgumentException if any argument is null + * @exception IOException if a writing error occurs */ - public static boolean write(RenderedImage im, String formatName, File output) throws IOException { + if (im == null || formatName == null || output == null) + throw new IllegalArgumentException ("null argument"); + return write(im, formatName, new FileOutputStream(output)); } + /** + * Write an image to an output stream using a registered writer that + * supports the given format. + * + * @param im the image data to write + * @param formatName an informal description of the output format + * @param output the output stream to which the image will be + * written + * + * @return false if no registered writer supports the given format, + * true otherwise + * + * @exception IllegalArgumentException if any argument is null + * @exception IOException if a writing error occurs + */ public static boolean write(RenderedImage im, String formatName, OutputStream output) throws IOException { + if (im == null || formatName == null || output == null) + throw new IllegalArgumentException ("null argument"); + return write(im, formatName, new MemoryCacheImageOutputStream(output)); } - - + + /** + * Write an image to an ImageOutputStream using a registered writer + * that supports the given format. Image data is written starting + * at the ImageOutputStream's current stream pointer, overwriting + * any existing data. + * + * @param im the image data to write + * @param formatName an informal description of the output format + * @param output the image output stream to which the image will be + * written + * + * @return false if no registered writer supports the given format, + * true otherwise + * + * @exception IllegalArgumentException if any argument is null + * @exception IOException if a writing error occurs + */ public static boolean write(RenderedImage im, String formatName, ImageOutputStream output) throws IOException { + if (im == null || formatName == null || output == null) + throw new IllegalArgumentException ("null argument"); + Iterator writers = getImageWritersByFormatName(formatName); IIOImage img = new IIOImage(im, null, null); while (writers.hasNext()) @@ -567,9 +835,27 @@ public final class ImageIO return false; } + /** + * Create a buffered image from an image input stream. An image + * reader that supports the given image data is automatically + * selected from the collection of registered readers. If no + * registered reader can handle the input format, null is returned. + * + * @param stream the image input stream from which to read image + * data + * + * @return a new buffered image created from the given image data, + * or null + * + * @exception IllegalArgumentException if stream is null + * @exception IOException if a reading error occurs + */ public static BufferedImage read(ImageInputStream stream) throws IOException { + if (stream == null) + throw new IllegalArgumentException("null argument"); + Iterator providers = getRegistry().getServiceProviders(ImageReaderSpi.class, true); while (providers.hasNext()) { @@ -583,23 +869,330 @@ public final class ImageIO } return null; } - + + /** + * Create a buffered image from a URL. An image reader that + * supports the given image data is automatically selected from the + * collection of registered readers. If no registered reader can + * handle the input format, null is returned. + * + * The image data will be cached in the current cache directory if + * caching is enabled. + * + * This method does not locate readers that read data directly from + * a URL. To locate such readers manually, use IIORegistry and + * ImageReaderSpi. + * + * @param input the URL from which to retrieve the image file + * + * @return a new buffered image created from the given image URL, or + * null + * + * @exception IllegalArgumentException if input is null + * @exception IOException if a reading error occurs + */ public static BufferedImage read(URL input) throws IOException { + if (input == null) + throw new IllegalArgumentException("null argument"); + return read(input.openStream()); } + /** + * Create a buffered image from an input stream. An image reader + * that supports the given image data is automatically selected from + * the collection of registered readers. If no registered reader + * can handle the input format, null is returned. + * + * The image data will be cached in the current cache directory if + * caching is enabled. + * + * This method does not locate readers that read data directly from + * an input stream. To locate such readers manually, use + * IIORegistry and ImageReaderSpi. + * + * @param input the input stream from which to read the image data + * + * @return a new buffered image created from the given input stream, + * or null + * + * @exception IllegalArgumentException if input is null + * @exception IOException if a reading error occurs + */ public static BufferedImage read(InputStream input) throws IOException { + if (input == null) + throw new IllegalArgumentException("null argument"); + return read(new MemoryCacheImageInputStream(input)); } + /** + * Create a buffered image from a file. An image reader that + * supports the given image data is automatically selected from the + * collection of registered readers. If no registered reader can + * handle the input format, null is returned. + * + * The image data will be cached in the current cache directory if + * caching is enabled. + * + * This method does not locate readers that read data directly from + * a file. To locate such readers manually, use IIORegistry and + * ImageReaderSpi. + * + * @param input the file from which to read image data + * + * @return a new buffered image created from the given image file, + * or null + * + * @exception IllegalArgumentException if input is null + * @exception IOException if a reading error occurs + */ public static BufferedImage read(File input) throws IOException { + if (input == null) + throw new IllegalArgumentException("null argument"); + return read(new FileInputStream(input)); } + /** + * Create an image input stream from the given object. The + * collection of ImageInputStreamSpis registered with the + * IIORegistry is searched for an image input stream that can take + * input from the given object. null is returned if no such SPI is + * registered. + * + * The image data will be cached in the current cache directory if + * caching is enabled. + * + * @param input an object from which to read image data + * + * @return an ImageInputStream that can read data from input, or + * null + * + * @exception IllegalArgumentException if input is null + * @exception IOException if caching is required but not enabled + */ + public static ImageInputStream createImageInputStream (Object input) + throws IOException + { + if (input == null) + throw new IllegalArgumentException ("null argument"); + + Iterator spis = getRegistry().getServiceProviders + (ImageInputStreamSpi.class, true); + + ImageInputStreamSpi foundSpi = null; + + while(spis.hasNext()) + { + ImageInputStreamSpi spi = (ImageInputStreamSpi) spis.next(); + + if (input.getClass().equals(spi.getInputClass())) + { + foundSpi = spi; + break; + } + } + + return foundSpi == null ? null : + foundSpi.createInputStreamInstance (input, + getUseCache(), + getCacheDirectory()); + } + + /** + * Create an image output stream from the given object. The + * collection of ImageOutputStreamSpis registered with the + * IIORegistry is searched for an image output stream that can send + * output to the given object. null is returned if no such SPI is + * registered. + * + * The image data will be cached in the current cache directory if + * caching is enabled. + * + * @param input an object to which to write image data + * + * @return an ImageOutputStream that can send data to output, or + * null + * + * @exception IllegalArgumentException if output is null + * @exception IOException if caching is required but not enabled + */ + public static ImageOutputStream createImageOutputStream (Object output) + throws IOException + { + if (output == null) + throw new IllegalArgumentException ("null argument"); + + Iterator spis = getRegistry().getServiceProviders + (ImageOutputStreamSpi.class, true); + + ImageOutputStreamSpi foundSpi = null; + + while(spis.hasNext()) + { + ImageOutputStreamSpi spi = (ImageOutputStreamSpi) spis.next(); + + if (output.getClass().equals(spi.getOutputClass())) + { + foundSpi = spi; + break; + } + } + + return foundSpi == null ? null : + foundSpi.createOutputStreamInstance (output, + getUseCache(), + getCacheDirectory()); + } + + /** + * Retrieve an image reader corresponding to an image writer, or + * null if writer is not registered or if no corresponding reader is + * registered. + * + * @param writer a registered image writer + * + * @return an image reader corresponding to writer, or null + * + * @exception IllegalArgumentException if writer is null + */ + public static ImageReader getImageReader (ImageWriter writer) + { + if (writer == null) + throw new IllegalArgumentException ("null argument"); + + ImageWriterSpi spi = (ImageWriterSpi) getRegistry() + .getServiceProviderByClass(writer.getClass()); + + String[] readerSpiNames = spi.getImageReaderSpiNames(); + + ImageReader r = null; + + if (readerSpiNames != null) + { + try + { + Class readerClass = Class.forName (readerSpiNames[0]); + r = (ImageReader) readerClass.newInstance (); + } + catch (Exception e) + { + return null; + } + } + return r; + } + + /** + * Retrieve an iterator over the collection of registered image + * readers that support reading data from the given object. + * + * @param input the object for which to retrieve image readers + * + * @return an iterator over a collection of image readers + */ + public static Iterator getImageReaders (Object input) + { + if (input == null) + throw new IllegalArgumentException ("null argument"); + + return getRegistry().getServiceProviders (ImageReaderSpi.class, + new ReaderObjectFilter(input), + true); + } + + /** + * Retrieve an iterator over the collection of registered image + * writers that support writing images of the given type and in the + * given format. + * + * @param type the output image's colour and sample models + * @param formatName the output image format + * + * @return an iterator over a collection of image writers + */ + public static Iterator getImageWriters (ImageTypeSpecifier type, + String formatName) + { + if (type == null || formatName == null) + throw new IllegalArgumentException ("null argument"); + + return getRegistry().getServiceProviders (ImageWriterSpi.class, + new WriterObjectFilter(type, + formatName), + true); + } + + /** + * Retrieve an image writer corresponding to an image reader, or + * null if reader is not registered or if no corresponding writer is + * registered. This method is useful for preserving metadata + * without needing to understand its format, since the returned + * writer will be able to write, unchanged, the metadata passed to + * it by the reader. + * + * @param reader a registered image reader + * + * @return an image writer corresponding to reader, or null + * + * @exception IllegalArgumentException if reader is null + */ + public static ImageWriter getImageWriter (ImageReader reader) + { + if (reader == null) + throw new IllegalArgumentException ("null argument"); + + ImageReaderSpi spi = (ImageReaderSpi) getRegistry() + .getServiceProviderByClass(reader.getClass()); + + String[] writerSpiNames = spi.getImageWriterSpiNames(); + + ImageWriter w = null; + + if (writerSpiNames != null) + { + try + { + Class writerClass = Class.forName (writerSpiNames[0]); + w = (ImageWriter) writerClass.newInstance (); + } + catch (Exception e) + { + return null; + } + } + return w; + } + + /** + * Retrieve an iterator over a collection of image transcoders that + * support transcoding from the given image reader's metadata format + * to the given writer's metadata format. + * + * @param reader an image reader + * @param writer an image writer + * + * @return an iterator over a collection of image transcoders + * + * @exception IllegalArgumentException if either reader or writer is + * null + */ + public static Iterator getImageTranscoders (ImageReader reader, + ImageWriter writer) + { + if (reader == null || writer == null) + throw new IllegalArgumentException ("null argument"); + + return getRegistry().getServiceProviders (ImageTranscoderSpi.class, + new TranscoderFilter (reader, + writer), + true); + } } diff --git a/libjava/classpath/javax/imageio/ImageReadParam.java b/libjava/classpath/javax/imageio/ImageReadParam.java index 889fe6cc97d..b2680f6b14e 100644 --- a/libjava/classpath/javax/imageio/ImageReadParam.java +++ b/libjava/classpath/javax/imageio/ImageReadParam.java @@ -42,6 +42,8 @@ import java.awt.Dimension; import java.awt.image.BufferedImage; /** + * DOCUMENT ME + * * @author Michel Koch (konqueror@gmx.de) */ public class ImageReadParam extends IIOParam diff --git a/libjava/classpath/javax/imageio/ImageReader.java b/libjava/classpath/javax/imageio/ImageReader.java index fdf692bd2aa..cdd77d52bad 100644 --- a/libjava/classpath/javax/imageio/ImageReader.java +++ b/libjava/classpath/javax/imageio/ImageReader.java @@ -1,5 +1,5 @@ /* ImageReader.java -- Decodes raster images. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,13 +38,19 @@ exception statement from your version. */ package javax.imageio; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.ResourceBundle; +import java.util.MissingResourceException; +import java.util.Set; import javax.imageio.event.IIOReadProgressListener; import javax.imageio.event.IIOReadUpdateListener; @@ -53,82 +59,233 @@ import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; +/** + * A class for decoding images within the ImageIO framework. + * + * An ImageReader for a given format is instantiated by an + * ImageReaderSpi for that format. ImageReaderSpis are registered + * with the IIORegistry. + * + * The ImageReader API supports reading animated images that may have + * multiple frames; to support such images many methods take an index + * parameter. + * + * Images may also be read in multiple passes, where each successive + * pass increases the level of detail in the destination image. + */ public abstract class ImageReader { private boolean aborted; - - protected Locale[] availableLocales; - protected boolean ignoreMetadata; - protected Object input; - protected Locale locale; - protected int minIndex; - protected ImageReaderSpi originatingProvider; - protected List progressListeners = new ArrayList(); - protected boolean seekForwardOnly; - protected List updateListeners = new ArrayList(); - protected List warningListeners = new ArrayList(); - protected List warningLocales = new ArrayList(); + /** + * All locales available for localization of warning messages, or + * null if localization is not supported. + */ + protected Locale[] availableLocales = null; + + /** + * true if the input source does not require metadata to be read, + * false otherwise. + */ + protected boolean ignoreMetadata = false; + + /** + * An ImageInputStream from which image data is read. + */ + protected Object input = null; + + /** + * The current locale used to localize warning messages, or null if + * no locale has been set. + */ + protected Locale locale = null; + + /** + * The minimum index at which data can be read. Constantly 0 if + * seekForwardOnly is false, always increasing if seekForwardOnly is + * true. + */ + protected int minIndex = 0; + + /** + * The image reader SPI that instantiated this reader. + */ + protected ImageReaderSpi originatingProvider = null; + + /** + * A list of installed progress listeners. Initially null, meaning + * no installed listeners. + */ + protected List progressListeners = null; + + /** + * true if this reader should only read data further ahead in the + * stream than its current location. false if it can read backwards + * in the stream. If this is true then caching can be avoided. + */ + protected boolean seekForwardOnly = false; + + /** + * A list of installed update listeners. Initially null, meaning no + * installed listeners. + */ + protected List updateListeners = null; + + /** + * A list of installed warning listeners. Initially null, meaning + * no installed listeners. + */ + protected List warningListeners = null; + + /** + * A list of warning locales corresponding with the list of + * installed warning listeners. Initially null, meaning no locales. + */ + protected List warningLocales = null; + + /** + * Construct an image reader. + * + * @param originatingProvider the provider that is constructing this + * image reader, or null + */ protected ImageReader(ImageReaderSpi originatingProvider) { this.originatingProvider = originatingProvider; } + /** + * Request that reading be aborted. The unread contents of the + * image will be undefined. + * + * Readers should clear the abort flag before starting a read + * operation, then poll it periodically during the read operation. + */ public void abort() { aborted = true; } + /** + * Check if the abort flag is set. + * + * @return true if the current read operation should be aborted, + * false otherwise + */ protected boolean abortRequested() { return aborted; } + /** + * Install a read progress listener. This method will return + * immediately if listener is null. + * + * @param listener a read progress listener or null + */ public void addIIOReadProgressListener(IIOReadProgressListener listener) { if (listener == null) return; - - progressListeners.add(listener); + if (progressListeners == null) + progressListeners = new ArrayList (); + progressListeners.add(listener); } + /** + * Install a read update listener. This method will return + * immediately if listener is null. + * + * @param listener a read update listener + */ public void addIIOReadUpdateListener(IIOReadUpdateListener listener) { if (listener == null) return; - - updateListeners.add(listener); + if (updateListeners == null) + updateListeners = new ArrayList (); + updateListeners.add(listener); } - + + /** + * Install a read warning listener. This method will return + * immediately if listener is null. Warning messages sent to this + * listener will be localized using the current locale. If the + * current locale is null then this reader will select a sensible + * default. + * + * @param listener a read warning listener + */ public void addIIOReadWarningListener(IIOReadWarningListener listener) { if (listener == null) return; - - warningListeners.add(listener); + if (warningListeners == null) + warningListeners = new ArrayList (); + warningListeners.add(listener); } + /** + * Check if this reader can handle raster data. Determines whether + * or not readRaster and readTileRaster throw + * UnsupportedOperationException. + * + * @return true if this reader supports raster data, false if not + */ public boolean canReadRaster() { return false; } + /** + * Clear the abort flag. + */ protected void clearAbortRequest() { aborted = false; } - + + /** + * Releases any resources allocated to this object. Subsequent + * calls to methods on this object will produce undefined results. + * + * The default implementation does nothing; subclasses should use + * this method ensure that native resources are released. + */ public void dispose() { // The default implementation does nothing. } - + + /** + * Returns the aspect ratio of this image, the ration of its width + * to its height. The aspect ratio is useful when resizing an image + * while keeping its proportions constant. + * + * @param imageIndex the frame index + * + * @return the image's aspect ratio + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public float getAspectRatio(int imageIndex) throws IOException { + if (input == null) + throw new IllegalStateException("input is null"); + return (float) (getWidth(imageIndex) / getHeight(imageIndex)); } + /** + * Retrieve the available locales. Return null if no locales are + * available or a clone of availableLocales. + * + * @return an array of locales or null + */ public Locale[] getAvailableLocales() { if (availableLocales == null) @@ -137,26 +294,107 @@ public abstract class ImageReader return (Locale[]) availableLocales.clone(); } + /** + * Retrieve the default read parameters for this reader's image + * format. + * + * The default implementation returns new ImageReadParam(). + * + * @return image reading parameters + */ public ImageReadParam getDefaultReadParam() { return new ImageReadParam(); } + /** + * Retrieve the format of the input source. + * + * @return the input source format name + * + * @exception IOException if a read error occurs + */ public String getFormatName() throws IOException { return originatingProvider.getFormatNames()[0]; } + /** + * Get the height of the input image in pixels. If the input image + * is resizable then a default height is returned. + * + * @param imageIndex the frame index + * + * @return the height of the input image + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public abstract int getHeight(int imageIndex) throws IOException; + /** + * Get the metadata associated with this image. If the reader is + * set to ignore metadata or does not support reading metadata, or + * if no metadata is available then null is returned. + * + * @param imageIndex the frame index + * + * @return a metadata object, or null + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public abstract IIOMetadata getImageMetadata(int imageIndex) throws IOException; + /** + * Get an iterator over the collection of image types into which + * this reader can decode image data. This method is guaranteed to + * return at least one valid image type specifier. + * + * The elements of the iterator should be ordered; the first element + * should be the most appropriate image type for this decoder, + * followed by the second-most appropriate, and so on. + * + * @param imageIndex the frame index + * + * @return an iterator over a collection of image type specifiers + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public abstract Iterator getImageTypes(int imageIndex) throws IOException; + /** + * Set the input source to the given object, specify whether this + * reader should be allowed to read input from the data stream more + * than once, and specify whether this reader should ignore metadata + * in the input stream. The input source must be set before many + * methods can be called on this reader. (see all ImageReader + * methods that throw IllegalStateException). If input is null then + * the current input source will be removed. + * + * Unless this reader has direct access with imaging hardware, input + * should be an ImageInputStream. + * + * @param input the input source object + * @param seekForwardOnly true if this reader should be allowed to + * read input from the data stream more than once, false otherwise + * @param ignoreMetadata true if this reader should ignore metadata + * associated with the input source, false otherwise + * + * @exception IllegalArgumentException if input is not a valid input + * source for this reader and is not an ImageInputStream + */ public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) @@ -183,358 +421,960 @@ public abstract class ImageReader this.minIndex = 0; } + /** + * Set the input source to the given object and specify whether this + * reader should be allowed to read input from the data stream more + * than once. The input source must be set before many methods can + * be called on this reader. (see all ImageReader methods that throw + * IllegalStateException). If input is null then the current input + * source will be removed. + * + * @param input the input source object + * @param seekForwardOnly true if this reader should be allowed to + * read input from the data stream more than once, false otherwise + * + * @exception IllegalArgumentException if input is not a valid input + * source for this reader and is not an ImageInputStream + */ public void setInput(Object in, boolean seekForwardOnly) { setInput(in, seekForwardOnly, false); } - public void setInput(Object in) + /** + * Set the input source to the given object. The input source must + * be set before many methods can be called on this reader. (see all + * ImageReader methods that throw IllegalStateException). If input + * is null then the current input source will be removed. + * + * @param input the input source object + * + * @exception IllegalArgumentException if input is not a valid input + * source for this reader and is not an ImageInputStream + */ + public void setInput(Object input) { - setInput(in, false, false); + setInput(input, false, false); } + /** + * Get this reader's image input source. null is returned if the + * image source has not been set. + * + * @return an image input source object, or null + */ public Object getInput() { return input; } + /** + * Get this reader's locale. null is returned if the locale has not + * been set. + * + * @return this reader's locale, or null + */ public Locale getLocale() { return locale; } + /** + * Return the number of images available from the image input + * source, not including thumbnails. This method will return 1 + * unless this reader is reading an animated image. + * + * Certain multi-image formats do not encode the total number of + * images. When reading images in those formats it may be necessary + * to repeatedly call read, incrementing the image index at each + * call, until an IndexOutOfBoundsException is thrown. + * + * The allowSearch parameter determines whether all images must be + * available at all times. When allowSearch is false, getNumImages + * will return -1 if the total number of images is unknown. + * Otherwise this method returns the number of images. + * + * @param allowSearch true if all images should be available at + * once, false otherwise + * + * @return -1 if allowSearch is false and the total number of images + * is currently unknown, or the number of images + * + * @exception IllegalStateException if input has not been set, or if + * seekForwardOnly is true + * @exception IOException if a read error occurs + */ public abstract int getNumImages(boolean allowSearch) throws IOException; + /** + * Get the number of thumbnails associated with an image. + * + * @param imageIndex the frame index + * + * @return the number of thumbnails associated with this image + */ public int getNumThumbnails(int imageIndex) throws IOException { return 0; } + /** + * Get the ImageReaderSpi that created this reader or null. + * + * @return an ImageReaderSpi, or null + */ public ImageReaderSpi getOriginatingProvider() { return originatingProvider; } + /** + * Get the metadata associated with the image being read. If the + * reader is set to ignore metadata or does not support reading + * metadata, or if no metadata is available then null is returned. + * This method returns metadata associated with the entirety of the + * image data, whereas getImageMetadata(int) returns metadata + * associated with a frame within a multi-image data stream. + * + * @return metadata associated with the image being read, or null + * + * @exception IOException if a read error occurs + */ public abstract IIOMetadata getStreamMetadata() throws IOException; + /** + * Get the height of a thumbnail image. + * + * @param imageIndex the frame index + * @param thumbnailIndex the thumbnail index + * + * @return the height of the thumbnail image + * + * @exception UnsupportedOperationException if this reader does not + * support thumbnails + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if either index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException { return readThumbnail(imageIndex, thumbnailIndex).getHeight(); } + /** + * Get the width of a thumbnail image. + * + * @param imageIndex the frame index + * @param thumbnailIndex the thumbnail index + * + * @return the width of the thumbnail image + * + * @exception UnsupportedOperationException if this reader does not + * support thumbnails + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if either index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException { return readThumbnail(imageIndex, thumbnailIndex).getWidth(); } + /** + * Get the X coordinate in pixels of the top-left corner of the + * first tile in this image. + * + * @param imageIndex the frame index + * + * @return the X coordinate of this image's first tile + * + * @exception IllegalStateException if input is needed but the input + * source is not set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getTileGridXOffset(int imageIndex) throws IOException { return 0; } + /** + * Get the Y coordinate in pixels of the top-left corner of the + * first tile in this image. + * + * @param imageIndex the frame index + * + * @return the Y coordinate of this image's first tile + * + * @exception IllegalStateException if input is needed but the input + * source is not set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getTileGridYOffset(int imageIndex) throws IOException { return 0; } + /** + * Get the height of an image tile. + * + * @param imageIndex the frame index + * + * @return the tile height for the given image + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getTileHeight(int imageIndex) throws IOException { return getHeight(imageIndex); } + /** + * Get the width of an image tile. + * + * @param imageIndex the frame index + * + * @return the tile width for the given image + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public int getTileWidth(int imageIndex) throws IOException { return getWidth(imageIndex); } + /** + * Get the width of the input image in pixels. If the input image + * is resizable then a default width is returned. + * + * @param imageIndex the image's index + * + * @return the width of the input image + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public abstract int getWidth(int imageIndex) throws IOException; + /** + * Check whether or not the given image has thumbnails associated + * with it. + * + * @return true if the given image has thumbnails, false otherwise + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public boolean hasThumbnails(int imageIndex) throws IOException { return getNumThumbnails(imageIndex) > 0; } + /** + * Check if this image reader ignores metadata. This method simply + * returns the value of ignoreMetadata. + * + * @return true if metadata is being ignored, false otherwise + */ public boolean isIgnoringMetadata() { return ignoreMetadata; } + /** + * Check if the given image is sub-divided into equal-sized + * non-overlapping pixel rectangles. + * + * A reader may expose tiling in the underlying format, hide it, or + * simulate tiling even if the underlying format is not tiled. + * + * @return true if the given image is tiled, false otherwise + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public boolean isImageTiled(int imageIndex) throws IOException { return false; } + /** + * Check if all pixels in this image are readily accessible. This + * method should return false for compressed formats. The return + * value is a hint as to the efficiency of certain image reader + * operations. + * + * @param imageIndex the frame index + * + * @return true if random pixel access is fast, false otherwise + * + * @exception IllegalStateException if input is null and it is + * needed to determine the return value + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds but the frame data must be accessed to determine + * the return value + * @exception IOException if a read error occurs + */ public boolean isRandomAccessEasy(int imageIndex) throws IOException { return false; } + /** + * Check if this image reader may only seek forward within the input + * stream. + * + * @return true if this reader may only seek forward, false + * otherwise + */ public boolean isSeekForwardOnly() { return seekForwardOnly; } + /** + * Notifies all installed read progress listeners that image loading + * has completed by calling their imageComplete methods. + */ protected void processImageComplete() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.imageComplete (this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.imageComplete (this); + } } } + /** + * Notifies all installed read progress listeners that a certain + * percentage of the image has been loaded, by calling their + * imageProgress methods. + * + * @param percentageDone the percentage of image data that has been + * loaded + */ protected void processImageProgress(float percentageDone) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.imageProgress(this, percentageDone); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.imageProgress(this, percentageDone); + } } } - + /** + * Notifies all installed read progress listeners, by calling their + * imageStarted methods, that image loading has started on the given + * image. + * + * @param imageIndex the frame index of the image that has started + * loading + */ protected void processImageStarted(int imageIndex) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.imageStarted(this, imageIndex); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.imageStarted(this, imageIndex); + } } } + /** + * Notifies all installed read update listeners, by calling their + * imageUpdate methods, that the set of samples has changed. + * + * @param image the buffered image that is being updated + * @param minX the X coordinate of the top-left pixel in this pass + * @param minY the Y coordinate of the top-left pixel in this pass + * @param width the total width of the rectangle covered by this + * pass, including skipped pixels + * @param height the total height of the rectangle covered by this + * pass, including skipped pixels + * @param periodX the horizontal sample interval + * @param periodY the vertical sample interval + * @param bands the affected bands in the destination + */ protected void processImageUpdate(BufferedImage image, int minX, int minY, int width, int height, int periodX, int periodY, int[] bands) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.imageUpdate(this, image, minX, minY, width, height, periodX, - periodY, bands); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.imageUpdate(this, image, minX, minY, width, height, + periodX, periodY, bands); + } } } + /** + * Notifies all installed update progress listeners, by calling + * their passComplete methods, that a progressive pass has + * completed. + * + * @param image the image that has being updated + */ protected void processPassComplete(BufferedImage image) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.passComplete(this, image); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.passComplete(this, image); + } } } + /** + * Notifies all installed read update listeners, by calling their + * passStarted methods, that a new pass has begun. + * + * @param image the buffered image that is being updated + * @param pass the current pass number + * @param minPass the pass at which decoding will begin + * @param maxPass the pass at which decoding will end + * @param minX the X coordinate of the top-left pixel in this pass + * @param minY the Y coordinate of the top-left pixel in this pass + * @param width the total width of the rectangle covered by this + * pass, including skipped pixels + * @param height the total height of the rectangle covered by this + * pass, including skipped pixels + * @param periodX the horizontal sample interval + * @param periodY the vertical sample interval + * @param bands the affected bands in the destination + */ protected void processPassStarted(BufferedImage image, int pass, int minPass, int maxPass, int minX, int minY, int periodX, int periodY, int[] bands) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.passStarted(this, image, pass, minPass, maxPass, minX, minY, - periodX, periodY, bands); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.passStarted(this, image, pass, minPass, maxPass, minX, + minY, periodX, periodY, bands); + } } } + /** + * Notifies all installed read progress listeners that image loading + * has been aborted by calling their readAborted methods. + */ protected void processReadAborted() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.readAborted(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.readAborted(this); + } } } - + /** + * Notifies all installed read progress listeners, by calling their + * sequenceComplete methods, that a sequence of images has completed + * loading. + */ protected void processSequenceComplete() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.sequenceComplete(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.sequenceComplete(this); + } } } + /** + * Notifies all installed read progress listeners, by calling their + * sequenceStarted methods, a sequence of images has started + * loading. + * + * @param minIndex the index of the first image in the sequence + */ protected void processSequenceStarted(int minIndex) { - Iterator it = progressListeners.iterator(); - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.sequenceStarted(this, minIndex); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.sequenceStarted(this, minIndex); + } } } + /** + * Notifies all installed read progress listeners, by calling their + * thumbnailComplete methods, that a thumbnail has completed + * loading. + */ protected void processThumbnailComplete() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.thumbnailComplete(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.thumbnailComplete(this); + } } } + /** + * Notifies all installed update progress listeners, by calling + * their thumbnailPassComplete methods, that a progressive pass has + * completed on a thumbnail. + * + * @param thumbnail the thumbnail that has being updated + */ protected void processThumbnailPassComplete(BufferedImage thumbnail) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.thumbnailPassComplete(this, thumbnail); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.thumbnailPassComplete(this, thumbnail); + } } } + /** + * Notifies all installed read update listeners, by calling their + * thumbnailPassStarted methods, that a new pass has begun. + * + * @param thumbnail the thumbnail that is being updated + * @param pass the current pass number + * @param minPass the pass at which decoding will begin + * @param maxPass the pass at which decoding will end + * @param minX the X coordinate of the top-left pixel in this pass + * @param minY the Y coordinate of the top-left pixel in this pass + * @param width the total width of the rectangle covered by this + * pass, including skipped pixels + * @param height the total height of the rectangle covered by this + * pass, including skipped pixels + * @param periodX the horizontal sample interval + * @param periodY the vertical sample interval + * @param bands the affected bands in the destination + */ protected void processThumbnailPassStarted(BufferedImage thumbnail, int pass, int minPass, int maxPass, int minX, int minY, int periodX, int periodY, int[] bands) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.thumbnailPassStarted(this, thumbnail, pass, minPass, maxPass, - minX, minY, periodX, periodY, bands); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.thumbnailPassStarted(this, thumbnail, pass, minPass, + maxPass, minX, minY, periodX, + periodY, bands); + } } } - + + /** + * Notifies all installed read progress listeners that a certain + * percentage of a thumbnail has been loaded, by calling their + * thumbnailProgress methods. + * + * @param percentageDone the percentage of thumbnail data that has + * been loaded + */ protected void processThumbnailProgress(float percentageDone) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.thumbnailProgress(this, percentageDone); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.thumbnailProgress(this, percentageDone); + } } } + /** + * Notifies all installed read progress listeners, by calling their + * imageStarted methods, that thumbnail loading has started on the + * given thumbnail of the given image. + * + * @param imageIndex the frame index of the image one of who's + * thumbnails has started loading + * @param thumbnailIndex the index of the thumbnail that has started + * loading + */ protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOReadProgressListener listener = (IIOReadProgressListener) it.next(); - listener.thumbnailStarted(this, imageIndex, thumbnailIndex); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOReadProgressListener listener = + (IIOReadProgressListener) it.next(); + listener.thumbnailStarted(this, imageIndex, thumbnailIndex); + } } } + /** + * Notifies all installed read update listeners, by calling their + * thumbnailUpdate methods, that the set of samples has changed. + * + * @param image the buffered image that is being updated + * @param minX the X coordinate of the top-left pixel in this pass + * @param minY the Y coordinate of the top-left pixel in this pass + * @param width the total width of the rectangle covered by this + * pass, including skipped pixels + * @param height the total height of the rectangle covered by this + * pass, including skipped pixels + * @param periodX the horizontal sample interval + * @param periodY the vertical sample interval + * @param bands the affected bands in the destination + */ protected void processThumbnailUpdate(BufferedImage image, int minX, int minY, int width, int height, int periodX, int periodY, int[] bands) { - Iterator it = updateListeners.iterator(); - - while (it.hasNext()) + if (updateListeners != null) { - IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); - listener.thumbnailUpdate(this, image, minX, minY, width, height, - periodX, periodY, bands); + Iterator it = updateListeners.iterator(); + + while (it.hasNext()) + { + IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next(); + listener.thumbnailUpdate(this, image, minX, minY, width, height, + periodX, periodY, bands); + } } } + /** + * Notifies all installed warning listeners, by calling their + * warningOccurred methods, that a warning message has been raised. + * + * @param warning the warning message + * + * @exception IllegalArgumentException if warning is null + */ protected void processWarningOccurred(String warning) { - Iterator it = warningListeners.iterator(); + if (warning == null) + throw new IllegalArgumentException ("null argument"); + if (warningListeners != null) + { + Iterator it = warningListeners.iterator(); + + while (it.hasNext()) + { + IIOReadWarningListener listener = + (IIOReadWarningListener) it.next(); + listener.warningOccurred(this, warning); + } + } + } + + /** + * Notify all installed warning listeners, by calling their + * warningOccurred methods, that a warning message has been raised. + * The warning message is retrieved from a resource bundle, using + * the given basename and keyword. + * + * @param baseName the basename of the resource from which to + * retrieve the warning message + * @param keyword the keyword used to retrieve the warning from the + * resource bundle + * + * @exception IllegalArgumentException if either baseName or keyword + * is null + * @exception IllegalArgumentException if no resource bundle is + * found using baseName + * @exception IllegalArgumentException if the given keyword produces + * no results from the resource bundle + * @exception IllegalArgumentException if the retrieved object is + * not a String + */ + protected void processWarningOccurred(String baseName, + String keyword) + { + if (baseName == null || keyword == null) + throw new IllegalArgumentException ("null argument"); + + ResourceBundle b = null; + + try + { + b = ResourceBundle.getBundle(baseName, getLocale()); + } + catch (MissingResourceException e) + { + throw new IllegalArgumentException ("no resource bundle found"); + } + + Object str = null; - while (it.hasNext()) + try { - IIOReadWarningListener listener = (IIOReadWarningListener) it.next(); - listener.warningOccurred(this, warning); + str = b.getObject(keyword); + } + catch (MissingResourceException e) + { + throw new IllegalArgumentException ("no results found for keyword"); + } + + if (! (str instanceof String)) + throw new IllegalArgumentException ("retrieved object not a String"); + + String warning = (String) str; + + if (warningListeners != null) + { + Iterator it = warningListeners.iterator(); + + while (it.hasNext()) + { + IIOReadWarningListener listener = + (IIOReadWarningListener) it.next(); + listener.warningOccurred(this, warning); + } } } + /** + * Read the given frame into a buffered image using the given read + * parameters. Listeners will be notified of image loading progress + * and warnings. + * + * @param imageIndex the index of the frame to read + * @param param the image read parameters to use when reading + * + * @return a buffered image + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public abstract BufferedImage read(int imageIndex, ImageReadParam param) throws IOException; + /** + * Check if this reader supports reading thumbnails. + * + * @return true if this reader supports reading thumbnails, false + * otherwise + */ public boolean readerSupportsThumbnails() { return false; } + /** + * Read raw raster data. The image type specifier in param is + * ignored but all other parameters are used. Offset parameters are + * translated into the raster's coordinate space. This method may + * be implemented by image readers that want to provide direct + * access to raw image data. + * + * @param imageIndex the frame index + * @param param the image read parameters + * + * @return a raster containing the read image data + * + * @exception UnsupportedOperationException if this reader doesn't + * support rasters + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ public Raster readRaster(int imageIndex, ImageReadParam param) throws IOException { throw new UnsupportedOperationException(); } + /** + * Read a thumbnail. + * + * @param imageIndex the frame index + * @param thumbnailIndex the thumbnail index + * + * @return a buffered image of the thumbnail + * + * @exception UnsupportedOperationException if this reader doesn't + * support thumbnails + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if either the frame index or + * the thumbnail index is out-of-bounds + * @exception IOException if a read error occurs + * + */ public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException { throw new UnsupportedOperationException(); } + /** + * Uninstall all read progress listeners. + */ public void removeAllIIOReadProgressListeners() { - progressListeners.clear(); + progressListeners = null; } + /** + * Uninstall all read update listeners. + */ public void removeAllIIOReadUpdateListeners() { - updateListeners.clear(); + updateListeners = null; } + /** + * Uninstall all read warning listeners. + */ public void removeAllIIOReadWarningListeners() { - warningListeners.clear(); + warningListeners = null; } - + + /** + * Uninstall the given read progress listener. + * + * @param listener the listener to remove + */ public void removeIIOReadProgressListener(IIOReadProgressListener listener) { if (listener == null) return; - - progressListeners.remove(listener); + if (progressListeners != null) + { + progressListeners.remove(listener); + } } - + + /** + * Uninstall the given read update listener. + * + * @param listener the listener to remove + */ public void removeIIOReadUpdateListener(IIOReadUpdateListener listener) { if (listener == null) return; - - updateListeners.remove(listener); + + if (updateListeners != null) + { + updateListeners.remove(listener); + } } - + + /** + * Uninstall the given read warning listener. + * + * @param listener the listener to remove + */ public void removeIIOReadWarningListener(IIOReadWarningListener listener) { if (listener == null) return; - - warningListeners.remove(listener); + if (warningListeners != null) + { + warningListeners.remove(listener); + } } - + + /** + * Set the current locale or use the default locale. + * + * @param locale the locale to set, or null + */ public void setLocale(Locale locale) { if (locale != null) @@ -553,4 +1393,644 @@ public abstract class ImageReader this.locale = locale; } + + /** + * Check that the given read parameters have valid source and + * destination band settings. If the param.getSourceBands() returns + * null, the array is assumed to include all band indices, 0 to + * numSrcBands - 1; likewise if param.getDestinationBands() returns + * null, it is assumed to be an array containing indices 0 to + * numDstBands - 1. A failure will cause this method to throw + * IllegalArgumentException. + * + * @param param the image parameters to check + * @param numSrcBands the number of input source bands + * @param numDstBands the number of ouput destination bands + * + * @exception IllegalArgumentException if either the given source or + * destination band indices are invalid + */ + protected static void checkReadParamBandSettings(ImageReadParam param, + int numSrcBands, + int numDstBands) + { + int[] srcBands = param.getSourceBands(); + int[] dstBands = param.getDestinationBands(); + boolean lengthsDiffer = false; + boolean srcOOB = false; + boolean dstOOB = false; + + if (srcBands == null) + { + if (dstBands == null) + { + if (numSrcBands != numDstBands) + lengthsDiffer = true; + } + else + { + if (numSrcBands != dstBands.length) + lengthsDiffer = true; + + for (int i = 0; i < dstBands.length; i++) + if (dstBands[i] > numSrcBands - 1) + { + dstOOB = true; + break; + } + } + } + else + { + if (dstBands == null) + { + if (srcBands.length != numDstBands) + lengthsDiffer = true; + + for (int i = 0; i < srcBands.length; i++) + if (srcBands[i] > numDstBands - 1) + { + srcOOB = true; + break; + } + } + else + { + if (srcBands.length != dstBands.length) + lengthsDiffer = true; + + for (int i = 0; i < srcBands.length; i++) + if (srcBands[i] > numDstBands - 1) + { + srcOOB = true; + break; + } + + for (int i = 0; i < dstBands.length; i++) + if (dstBands[i] > numSrcBands - 1) + { + dstOOB = true; + break; + } + } + } + + if (lengthsDiffer) + throw new IllegalArgumentException ("array lengths differ"); + + if (srcOOB) + throw new IllegalArgumentException ("source band index" + + " out-of-bounds"); + + if (dstOOB) + throw new IllegalArgumentException ("destination band index" + + " out-of-bounds"); + } + + /** + * Calcluate the source and destination regions that will be read + * from and written to, given image parameters and/or a destination + * buffered image. The source region will be clipped if any of its + * bounds are outside the destination region. Clipping will account + * for subsampling and destination offsets. Likewise, the + * destination region is clipped to the given destination image, if + * it is not null, using the given image parameters, if they are not + * null. IllegalArgumentException is thrown if either region will + * contain 0 pixels after clipping. + * + * @param image read parameters, or null + * @param srcWidth the width of the source image + * @param srcHeight the height of the source image + * @param image the destination image, or null + * @param srcRegion a rectangle whose values will be set to the + * clipped source region + * @param destRegion a rectangle whose values will be set to the + * clipped destination region + * + * @exception IllegalArgumentException if either srcRegion or + * destRegion is null + * @exception IllegalArgumentException if either of the calculated + * regions is empty + */ + protected static void computeRegions (ImageReadParam param, + int srcWidth, + int srcHeight, + BufferedImage image, + Rectangle srcRegion, + Rectangle destRegion) + { + if (srcRegion == null || destRegion == null) + throw new IllegalArgumentException ("null region"); + + if (srcWidth == 0 || srcHeight == 0) + throw new IllegalArgumentException ("zero-sized region"); + + srcRegion = getSourceRegion(param, srcWidth, srcHeight); + if (image != null) + destRegion = new Rectangle (0, 0, image.getWidth(), image.getHeight()); + else + destRegion = new Rectangle (0, 0, srcWidth, srcHeight); + + if (param != null) + { + Point offset = param.getDestinationOffset(); + + if (offset.x < 0) + { + srcRegion.x -= offset.x; + srcRegion.width += offset.x; + } + if (offset.y < 0) + { + srcRegion.y -= offset.y; + srcRegion.height += offset.y; + } + + srcRegion.width = srcRegion.width > destRegion.width + ? destRegion.width : srcRegion.width; + srcRegion.height = srcRegion.height > destRegion.height + ? destRegion.height : srcRegion.height; + + if (offset.x >= 0) + { + destRegion.x += offset.x; + destRegion.width -= offset.x; + } + if (offset.y >= 0) + { + destRegion.y += offset.y; + destRegion.height -= offset.y; + } + } + + if (srcRegion.isEmpty() || destRegion.isEmpty()) + throw new IllegalArgumentException ("zero-sized region"); + } + + /** + * Return a suitable destination buffered image. If + * param.getDestination() is non-null, then it is returned, + * otherwise a buffered image is created using + * param.getDestinationType() if it is non-null and also in the + * given imageTypes collection, or the first element of imageTypes + * otherwise. + * + * @param param image read parameters from which a destination image + * or image type is retrieved, or null + * @param imageTypes a collection of legal image types + * @param width the width of the source image + * @param height the height of the source image + * + * @return a suitable destination buffered image + * + * @exception IIOException if param.getDestinationType() does not + * return an image type in imageTypes + * @exception IllegalArgumentException if imageTypes is null or + * empty, or if a non-ImageTypeSpecifier object is retrieved from + * imageTypes + * @exception IllegalArgumentException if the resulting destination + * region is empty + * @exception IllegalArgumentException if the product of width and + * height is greater than Integer.MAX_VALUE + */ + protected static BufferedImage getDestination (ImageReadParam param, + Iterator imageTypes, + int width, + int height) + throws IIOException + { + if (imageTypes == null || !imageTypes.hasNext()) + throw new IllegalArgumentException ("imageTypes null or empty"); + + if (width < 0 || height < 0) + throw new IllegalArgumentException ("negative dimension"); + + // test for overflow + if (width * height < Math.min (width, height)) + throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE"); + + BufferedImage dest = null; + ImageTypeSpecifier destType = null; + + if (param != null) + { + dest = param.getDestination (); + if (dest == null) + { + ImageTypeSpecifier type = param.getDestinationType(); + if (type != null) + { + Iterator it = imageTypes; + + while (it.hasNext()) + { + Object o = it.next (); + if (! (o instanceof ImageTypeSpecifier)) + throw new IllegalArgumentException ("non-ImageTypeSpecifier object"); + + ImageTypeSpecifier t = (ImageTypeSpecifier) o; + if (t.equals (type)) + { + dest = t.createBufferedImage (width, height); + break; + } + if (destType == null) + throw new IIOException ("invalid destination type"); + + } + } + } + } + if (dest == null) + { + Rectangle srcRegion = new Rectangle (); + Rectangle destRegion = new Rectangle (); + + computeRegions (param, width, height, null, srcRegion, destRegion); + + if (destRegion.isEmpty()) + throw new IllegalArgumentException ("destination region empty"); + + if (destType == null) + { + Object o = imageTypes.next(); + if (! (o instanceof ImageTypeSpecifier)) + throw new IllegalArgumentException ("non-ImageTypeSpecifier" + + " object"); + + dest = ((ImageTypeSpecifier) o).createBufferedImage + (destRegion.width, destRegion.height); + } + else + dest = destType.createBufferedImage + (destRegion.width, destRegion.height); + } + return dest; + } + + /** + * Get the metadata associated with this image. If the reader is + * set to ignore metadata or does not support reading metadata, or + * if no metadata is available then null is returned. + * + * This more specific version of getImageMetadata(int) can be used + * to restrict metadata retrieval to specific formats and node + * names, which can limit the amount of data that needs to be + * processed. + * + * @param imageIndex the frame index + * @param formatName the format of metadata requested + * @param nodeNames a set of Strings specifiying node names to be + * retrieved + * + * @return a metadata object, or null + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IllegalArgumentException if formatName is null + * @exception IllegalArgumentException if nodeNames is null + * @exception IOException if a read error occurs + */ + public IIOMetadata getImageMetadata (int imageIndex, + String formatName, + Set nodeNames) + throws IOException + { + if (formatName == null || nodeNames == null) + throw new IllegalArgumentException ("null argument"); + + return getImageMetadata (imageIndex); + } + + /** + * Get the index at which the next image will be read. If + * seekForwardOnly is true then the returned value will increase + * monotonically each time an image frame is read. If + * seekForwardOnly is false then the returned value will always be + * 0. + * + * @return the current frame index + */ + public int getMinIndex() + { + return minIndex; + } + + /** + * Get the image type specifier that most closely represents the + * internal data representation used by this reader. This value + * should be included in the return value of getImageTypes. + * + * @param imageIndex the frame index + * + * @return an image type specifier + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ + public ImageTypeSpecifier getRawImageType (int imageIndex) + throws IOException + { + return (ImageTypeSpecifier) getImageTypes(imageIndex).next(); + } + + /** + * Calculate a source region based on the given source image + * dimensions and parameters. Subsampling offsets and a source + * region are taken from the given image read parameters and used to + * clip the given image dimensions, returning a new rectangular + * region as a result. + * + * @param param image parameters, or null + * @param srcWidth the width of the source image + * @param srcHeight the height of the source image + * + * @return a clipped rectangle + */ + protected static Rectangle getSourceRegion (ImageReadParam param, + int srcWidth, + int srcHeight) + { + Rectangle clippedRegion = new Rectangle (0, 0, srcWidth, srcHeight); + + if (param != null) + { + Rectangle srcRegion = param.getSourceRegion(); + + if (srcRegion != null) + { + clippedRegion.x = srcRegion.x > clippedRegion.x + ? srcRegion.x : clippedRegion.x; + clippedRegion.y = srcRegion.y > clippedRegion.y + ? srcRegion.y : clippedRegion.y; + clippedRegion.width = srcRegion.width > clippedRegion.width + ? srcRegion.width : clippedRegion.width; + clippedRegion.height = srcRegion.height > clippedRegion.height + ? srcRegion.height : clippedRegion.height; + } + + int xOffset = param.getSubsamplingXOffset(); + + clippedRegion.x += xOffset; + clippedRegion.width -= xOffset; + + int yOffset = param.getSubsamplingYOffset(); + + clippedRegion.y += yOffset; + clippedRegion.height -= yOffset; + } + return clippedRegion; + } + + /** + * Get the metadata associated with the image being read. If the + * reader is set to ignore metadata or does not support reading + * metadata, or if no metadata is available then null is returned. + * This method returns metadata associated with the entirety of the + * image data, whereas getStreamMetadata() returns metadata + * associated with a frame within a multi-image data stream. + * + * This more specific version of getStreamMetadata() can be used to + * restrict metadata retrieval to specific formats and node names, + * which can limit the amount of data that needs to be processed. + * + * @param formatName the format of metadata requested + * @param nodeNames a set of Strings specifiying node names to be + * retrieved + * + * @return metadata associated with the image being read, or null + * + * @exception IllegalArgumentException if formatName is null + * @exception IllegalArgumentException if nodeNames is null + * @exception IOException if a read error occurs + */ + public IIOMetadata getStreamMetadata (String formatName, + Set nodeNames) + throws IOException + { + if (formatName == null || nodeNames == null) + throw new IllegalArgumentException ("null argument"); + + return getStreamMetadata(); + } + + /** + * Read the given frame all at once, using default image read + * parameters, and return a buffered image. + * + * The returned image will be formatted according to the + * currently-preferred image type specifier. + * + * Installed read progress listeners, update progress listeners and + * warning listeners will be notified of read progress, changes in + * sample sets and warnings respectively. + * + * @param the index of the image frame to read + * + * @return a buffered image + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IOException if a read error occurs + */ + public BufferedImage read (int imageIndex) + throws IOException + { + return read (imageIndex, null); + } + + /** + * Read the given frame all at once, using the given image read + * parameters, and return an IIOImage. The IIOImage will contain a + * buffered image as returned by getDestination. + * + * Installed read progress listeners, update progress listeners and + * warning listeners will be notified of read progress, changes in + * sample sets and warnings respectively. + * + * The source and destination band settings are checked with a call + * to checkReadParamBandSettings. + * + * @param the index of the image frame to read + * @param the image read parameters + * + * @return an IIOImage + * + * @exception IllegalStateException if input has not been set + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IllegalArgumentException if param.getSourceBands() and + * param.getDestinationBands() are incompatible + * @exception IllegalArgumentException if either the source or + * destination image regions are empty + * @exception IOException if a read error occurs + */ + public IIOImage readAll (int imageIndex, + ImageReadParam param) + throws IOException + { + checkReadParamBandSettings (param, + param.getSourceBands().length, + param.getDestinationBands().length); + + List l = new ArrayList (); + + for (int i = 0; i < getNumThumbnails (imageIndex); i++) + l.add (readThumbnail(imageIndex, i)); + + return new IIOImage (getDestination(param, getImageTypes(imageIndex), + getWidth(imageIndex), + getHeight(imageIndex)), + l, + getImageMetadata (imageIndex)); + } + + /** + * Read all image frames all at once, using the given image read + * parameters iterator, and return an iterator over a collection of + * IIOImages. Each IIOImage in the collection will contain a + * buffered image as returned by getDestination. + * + * Installed read progress listeners, update progress listeners and + * warning listeners will be notified of read progress, changes in + * sample sets and warnings respectively. + * + * Each set of source and destination band settings are checked with + * a call to checkReadParamBandSettings. + * + * @param an iterator over the image read parameters + * + * @return an IIOImage + * + * @exception IllegalStateException if input has not been set + * @exception IllegalArgumentException if a non-ImageReadParam is + * found in params + * @exception IllegalArgumentException if param.getSourceBands() and + * param.getDestinationBands() are incompatible + * @exception IllegalArgumentException if either the source or + * destination image regions are empty + * @exception IOException if a read error occurs + */ + public Iterator readAll (Iterator params) + throws IOException + { + List l = new ArrayList (); + int index = 0; + + while (params.hasNext()) + { + if (params != null && ! (params instanceof ImageReadParam)) + throw new IllegalArgumentException ("non-ImageReadParam found"); + + l.add (readAll(index++, (ImageReadParam) params.next ())); + } + + return l.iterator(); + } + + /** + * Read a rendered image. This is a more general counterpart to + * read (int, ImageReadParam). All image data may not be read + * before this method returns and so listeners will not necessarily + * be notified. + * + * @param the index of the image frame to read + * @param the image read parameters + * + * @return a rendered image + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IllegalArgumentException if param.getSourceBands() and + * param.getDestinationBands() are incompatible + * @exception IllegalArgumentException if either the source or + * destination image regions are empty + * @exception IOException if a read error occurs + */ + public RenderedImage readAsRenderedImage (int imageIndex, + ImageReadParam param) + throws IOException + { + return read (imageIndex, param); + } + + /** + * Read the given tile into a buffered image. If the tile + * coordinates are out-of-bounds an exception is thrown. If the + * image is not tiled then the coordinates 0, 0 are expected and the + * entire image will be read. + * + * @param imageIndex the frame index + * @param tileX the horizontal tile coordinate + * @param tileY the vertical tile coordinate + * + * @return the contents of the tile as a buffered image + * + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IllegalArgumentException if the tile coordinates are + * out-of-bounds + * @exception IOException if a read error occurs + */ + public BufferedImage readTile (int imageIndex, int tileX, int tileY) + throws IOException + { + if (tileX != 0 || tileY != 0) + throw new IllegalArgumentException ("tileX not 0 or tileY not 0"); + + return read (imageIndex); + } + + /** + * Read the given tile into a raster containing the raw image data. + * If the tile coordinates are out-of-bounds an exception is thrown. + * If the image is not tiled then the coordinates 0, 0 are expected + * and the entire image will be read. + * + * @param imageIndex the frame index + * @param tileX the horizontal tile coordinate + * @param tileY the vertical tile coordinate + * + * @return the contents of the tile as a raster + * + * @exception UnsupportedOperationException if rasters are not + * supported + * @exception IllegalStateException if input is null + * @exception IndexOutOfBoundsException if the frame index is + * out-of-bounds + * @exception IllegalArgumentException if the tile coordinates are + * out-of-bounds + * @exception IOException if a read error occurs + */ + public Raster readTileRaster (int imageIndex, int tileX, int tileY) + throws IOException + { + if (!canReadRaster()) + throw new UnsupportedOperationException ("cannot read rasters"); + + if (tileX != 0 || tileY != 0) + throw new IllegalArgumentException ("tileX not 0 or tileY not 0"); + + return readRaster (imageIndex, null); + } + + /** + * Reset this reader's internal state. + */ + public void reset () + { + setInput (null, false); + setLocale (null); + removeAllIIOReadUpdateListeners (); + removeAllIIOReadWarningListeners (); + removeAllIIOReadProgressListeners (); + clearAbortRequest (); + } } + diff --git a/libjava/classpath/javax/imageio/ImageTranscoder.java b/libjava/classpath/javax/imageio/ImageTranscoder.java index ccc99316269..1f9195f5816 100644 --- a/libjava/classpath/javax/imageio/ImageTranscoder.java +++ b/libjava/classpath/javax/imageio/ImageTranscoder.java @@ -41,14 +41,62 @@ package javax.imageio; import javax.imageio.metadata.IIOMetadata; /** + * An ImageTranscoder translates IIOMetadata objects provided by an + * ImageReader into corresponding IIOMetadata objects that can be + * understood by a given ImageWriter. + * + * Usually an ImageWriter will implement ImageTranscoder directly in + * which case the conversion methods will return IIOMetadata objects + * appropriate for this ImageWriter. + * + * Independent transcoders are also allowed; they must have knowledge + * of both the source IIOMetadata provided by the reader and the + * returned IIOMetadata expected by the writer. + * * @author Michael Koch (konqueror@gmx.de) */ public interface ImageTranscoder { + /** + * Converts IIOMetadata from an input reader format, returning an + * IIOMetadata suitable for use by an image writer. + * + * The ImageTypeSpecifier specifies the destination image type. + * + * An optional ImageWriteParam argument is available in case the + * image writing parameters affect the metadata conversion. + * + * @param inData the metadata coming from an image reader + * @param imageType the output image type of the writer + * @param param the image writing parameters or null + * + * @return the converted metadata that should be used by the image + * writer, or null if this ImageTranscoder has no knowledge of the + * input metadata + * + * @exception IllegalArgumentException if either inData or imageType + * is null + */ IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param); + /** + * Converts IIOMetadata from an input stream format, returning an + * IIOMetadata suitable for use by an image writer. + * + * An optional ImageWriteParam argument is available in case the + * image writing parameters affect the metadata conversion. + * + * @param inData the metadata coming from an input image stream + * @param param the image writing parameters or null + * + * @return the converted metadata that should be used by the image + * writer, or null if this ImageTranscoder has no knowledge of the + * input metadata + * + * @exception IllegalArgumentException if inData is null + */ IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param); } diff --git a/libjava/classpath/javax/imageio/ImageTypeSpecifier.java b/libjava/classpath/javax/imageio/ImageTypeSpecifier.java index 0751e376757..05b3a26d493 100644 --- a/libjava/classpath/javax/imageio/ImageTypeSpecifier.java +++ b/libjava/classpath/javax/imageio/ImageTypeSpecifier.java @@ -38,15 +38,47 @@ exception statement from your version. */ package javax.imageio; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.image.DataBuffer; +import java.awt.image.BandedSampleModel; +import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; +/** + * ImageTypeSpecifier store the color and sample models associated + * with an IIOImage. + */ public class ImageTypeSpecifier { + /** + * The image's color model. + */ protected ColorModel colorModel; + + /** + * The image's sample model. + */ protected SampleModel sampleModel; + /** + * Construct an image type specifier with the given models. + * + * @param colorModel the color model + * @param sampleModel the sample model + * + * @exception IllegalArgumentException if either model argument is + * null + * @exception IllegalArgumentException if the models are + * incompatible with one another + */ public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel) { if (colorModel == null) @@ -63,6 +95,14 @@ public class ImageTypeSpecifier this.sampleModel = sampleModel; } + /** + * Construct an image type specifier that describes the given + * rendered image. + * + * @param image a rendered image + * + * @exception IllegalArgumentException if image is null + */ public ImageTypeSpecifier(RenderedImage image) { if (image == null) @@ -72,21 +112,448 @@ public class ImageTypeSpecifier this.sampleModel = image.getSampleModel(); } + /** + * Create an image type specifier for a banded image using a + * component color model and a banded sample model. + * + * @param colorSpace the color space + * @param bankIndices the bank indices at which each band will be + * stored + * @param bandOffsets the starting band offset for each band within + * its bank + * @param dataType the data type, a DataBuffer constant + * @param hasAlpha true if this image type specifier should have an + * alpha component, false otherwise + * @param isAlphaPremultiplied true if other color components should + * be premultiplied by the alpha component, false otherwise + * + * @return a banded image type specifier + * + * @exception IllegalArgumentException if any of colorSpace, + * bankIndices or bankOffsets is null + * @exception IllegalArgumentException if bankIndices and + * bankOffsets differ in length + * @excpetion IllegalArgumentException if the number of color space + * components, including the alpha component if requested, is + * different from bandOffsets.length + * @exception if dataType is not a valid DataBuffer constant + */ + public static ImageTypeSpecifier createBanded (ColorSpace colorSpace, + int[] bankIndices, + int[] bankOffsets, + int dataType, + boolean hasAlpha, + boolean isAlphaPremultiplied) + { + if (colorSpace == null || bankIndices == null || bankOffsets == null) + throw new IllegalArgumentException ("null argument"); + + if (bankIndices.length != bankOffsets.length) + throw new IllegalArgumentException ("array lengths differ"); + + if (bankOffsets.length != (colorSpace.getNumComponents() + (hasAlpha ? 1 : 0))) + throw new IllegalArgumentException ("invalid bankOffsets length"); + + return new ImageTypeSpecifier (new ComponentColorModel (colorSpace, + hasAlpha, + isAlphaPremultiplied, + hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE, + dataType), + new BandedSampleModel (dataType, 1, 1, 1, + bankIndices, + bankOffsets)); + } + + /** + * Create a buffered image with the given dimensions using that has + * the characteristics specified by this image type specifier. + * + * @param the width of the buffered image, in pixels + * @param the height of the buffered image, in pixels + * + * @return a buffered image + * + * @exception IllegalArgumentException if either width or height is + * less than or equal to zero + * @exception IllegalArgumentException if width * height is greater + * than Integer.MAX_VALUE or if the storage required is greater than + * Integer.MAX_VALUE + */ + public BufferedImage createBufferedImage (int width, int height) + { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException ("dimension <= 0"); + + // test for overflow + if (width * height < Math.min (width, height)) + throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE"); + + if (width * height * sampleModel.getNumBands() < Math.min (width, height)) + throw new IllegalArgumentException ("storage required >" + + " Integer.MAX_VALUE"); + + // FIXME: this is probably wrong: + return new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB); + } + + /** + * Create an image type specifier that describes the given buffered + * image type. + * + * @param bufferedImageType the buffered image type to represent + * with the returned image type specifier + * + * @return a new image type specifier + * + * @exception IllegalArgumentException if bufferedImageType is not a + * BufferedImage constant or is BufferedImage.TYPE_CUSTOM + */ + public static ImageTypeSpecifier createFromBufferedImageType (int bufferedImageType) + { + if (bufferedImageType <= BufferedImage.TYPE_CUSTOM + || bufferedImageType > BufferedImage.TYPE_BYTE_INDEXED) + throw new IllegalArgumentException ("invalid buffered image type"); + + return new ImageTypeSpecifier (new BufferedImage (1, 1, bufferedImageType)); + } + + /** + * Create an image type specifier that describes the given rendered + * image's type. + * + * @param image the rendered image + * + * @return a new image type specifier + * + * @exception IllegalArgumentException if image is null + */ + public static ImageTypeSpecifier createFromRenderedImage (RenderedImage image) + { + if (image == null) + throw new IllegalArgumentException ("image null"); + + return new ImageTypeSpecifier (image); + } + + /** + * Create a grayscale image type specifier, given the number of + * bits, data type and whether or not the data is signed. + * + * @param bits the number of bits used to specify a greyscale value + * @param dataType a DataBuffer type constant + * @param isSigned true if this type specifier should support + * negative values, false otherwise + * + * @return a greyscal image type specifier + * + * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or 16 + * @exception IllegalArgumentException if dataType is not + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or + * DataBuffer.TYPE_USHORT + * @exception if bits is larger than the number of bits in the given + * data type + */ + public static ImageTypeSpecifier createGrayscale (int bits, int dataType, boolean isSigned) + { + return createGrayscale (bits, dataType, isSigned, false); + } + + /** + * Create a grayscale image type specifier, given the number of + * bits, data type and whether or not the data is signed. + * + * @param bits the number of bits used to specify a greyscale value + * @param dataType a DataBuffer type constant + * @param isSigned true if this type specifier should support + * negative values, false otherwise + * + * @return a greyscal image type specifier + * + * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or + * 16 + * @exception IllegalArgumentException if dataType is not + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or + * DataBuffer.TYPE_USHORT + * @exception if bits is larger than the number of bits in the given + * data type + */ + public static ImageTypeSpecifier createGrayscale (int bits, int dataType, + boolean isSigned, + boolean isAlphaPremultiplied) + { + if (bits != 1 && bits != 2 && bits != 4 && bits != 8 && bits != 16) + throw new IllegalArgumentException ("invalid bit size"); + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT + && dataType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException ("invalid data type"); + + if (dataType == DataBuffer.TYPE_BYTE && bits > 8) + throw new IllegalArgumentException ("number of bits too large for data type"); + + // FIXME: this is probably wrong: + return new ImageTypeSpecifier (new DirectColorModel (bits, 0xff, 0x0, + 0x0, 0xff), + new MultiPixelPackedSampleModel (dataType, + 1, 1, + bits)); + } + + /** + * Return an image type specifier for an image that uses an indexed + * colour model where each colour value has the specified number of + * bits and type and where the colour tables are those given. + * + * @param redLUT the red index values + * @param greenLUT the green index values + * @param blueLUT the blue index values + * @param alphaLUT the alpha index values + * @param bits the number of bits per index value + * @param dataType the type of each index value + * + * @return an indexed image type specifier + * + * @exception IllegalArgumentException if any of the colour arrays, + * not including alphaLUT, is null + * @exception IllegalArgumentException if bits is not 1, 2, 4, 8 or + * 16 + * @exception IllegalArgumentException if dataType is not + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or + * DataBuffer.TYPE_USHORT + * @exception if bits is larger than the number of bits in the given + * data type + */ + public static ImageTypeSpecifier createIndexed (byte[] redLUT, + byte[] greenLUT, + byte[] blueLUT, + byte[] alphaLUT, + int bits, + int dataType) + { + if (redLUT == null || greenLUT == null || blueLUT == null) + throw new IllegalArgumentException ("null colour table"); + + if (bits != 1 && bits != 2 && bits != 4 && bits != 8 && bits != 16) + throw new IllegalArgumentException ("invalid bit size"); + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT + && dataType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException ("invalid data type"); + + if (dataType == DataBuffer.TYPE_BYTE && bits > 8) + throw new IllegalArgumentException ("number of bits too large for data type"); + + // FIXME: this is probably wrong: + return new ImageTypeSpecifier (new IndexColorModel (bits, redLUT.length, + redLUT, greenLUT, blueLUT, + alphaLUT), + new MultiPixelPackedSampleModel (dataType, + 1, 1, + bits)); + } + + /** + * Create an image type specifier that uses a component colour model + * and a pixel interleaved sample model. Each pixel component will + * be stored in a separate value of the given data type. + * + * @param colorSpace the colour space used by the colour model + * @param bandOffsets the starting band offset for each band within + * its bank + * @param dataType the type of each pixel value + * @param hasAlpha true if an alpha channel should be specified, + * false otherwise + * @param isAlphaPremultiplied true if other colour channels should + * be premultiplied by the alpha value, false otherwise + * + * @return an interleaved image type specifier + * + * @exception IllegalArgumentException if either colorSpace or + * bandOffsets is null + * @excpetion IllegalArgumentException if the number of color space + * components, including the alpha component if requested, is + * different from bandOffsets.length + * @exception if dataType is not a valid DataBuffer constant + */ + public static ImageTypeSpecifier createInterleaved (ColorSpace colorSpace, + int[] bandOffsets, + int dataType, + boolean hasAlpha, + boolean isAlphaPremultiplied) + { + if (colorSpace == null || bandOffsets == null) + throw new IllegalArgumentException ("null argument"); + + if (bandOffsets.length != (colorSpace.getNumComponents() + (hasAlpha ? 1 : 0))) + throw new IllegalArgumentException ("invalid bankOffsets length"); + + return new ImageTypeSpecifier (new ComponentColorModel (colorSpace, + hasAlpha, + isAlphaPremultiplied, + hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE, + dataType), + new PixelInterleavedSampleModel (dataType, 1, 1, 1, 1, + bandOffsets)); + } + + /** + * Create an image type specifier using a direct color model and a + * packed sample model. All pixel components will be packed into + * one value of the given data type. + * + * @param colorSpace the color space to use in the color model + * @param redMask the bitmask for the red bits + * @param greenMask the bitmask for the green bits + * @param blueMask the bitmask for the blue bits + * @param alphaMask the bitmask for the alpha bits + * @param transferType the data type used to store pixel values + * @param isAlphaPremultiplied true if other colour channels should + * be premultiplied by the alpha value, false otherwise + * + * @return a packed image type specifier + * + * @exception IllegalArgumentException if colorSpace is null + * @exception IllegalArgumentException if colorSpace does not have + * type ColorSpace.TYPE_RGB + * @exception IllegalArgumentException if all masks are 0 + * @exception IllegalArgumentException if dataType is not + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT or + * DataBuffer.TYPE_INT + */ + public static ImageTypeSpecifier createPacked (ColorSpace colorSpace, + int redMask, + int greenMask, + int blueMask, + int alphaMask, + int transferType, + boolean isAlphaPremultiplied) + { + if (colorSpace == null) + throw new IllegalArgumentException ("null color space"); + + if (colorSpace.getType() != ColorSpace.TYPE_RGB) + throw new IllegalArgumentException ("invalid color space type"); + + if (redMask == 0 && greenMask == 0 && blueMask == 0 && alphaMask == 0) + throw new IllegalArgumentException ("no non-zero mask"); + + if (transferType != DataBuffer.TYPE_BYTE && transferType != DataBuffer.TYPE_USHORT + && transferType != DataBuffer.TYPE_INT) + throw new IllegalArgumentException ("invalid data type"); + + // Assume DataBuffer.TYPE_BYTE. + int numBits = 8; + + if (transferType == DataBuffer.TYPE_SHORT) + numBits = 16; + else if (transferType == DataBuffer.TYPE_INT) + numBits = 32; + + return new ImageTypeSpecifier (new DirectColorModel (colorSpace, + numBits, + redMask, + greenMask, + blueMask, + alphaMask, + isAlphaPremultiplied, + transferType), + new MultiPixelPackedSampleModel (transferType, + 1, 1, numBits)); + } + + /** + * Get the number of bits per sample in the given band. + * + * @param band the band from which to get the number of bits + * + * @return the number of bits in the given band + * + * @exception IllegalArgumentException if band is out-of-bounds + */ + public int getBitsPerBand (int band) + { + if (band < 0 || band > sampleModel.getNumBands()) + throw new IllegalArgumentException ("band out-of-bounds"); + + return sampleModel.getSampleSize (band); + } + + /** + * Get the buffered image constant specified by this image type + * specifier. + * + * @return a buffered image constant + */ + public int getBufferedImageType () + { + // FIXME: + return BufferedImage.TYPE_INT_RGB; + } + + /** + * Create a sample model that is compatible with the one specified + * by this image type specifier, with the given dimensions. + * + * @param width the width of the returned sample model + * @param height the height of the returned sample model + * + * @return a sample model compatible with the one in this image type + * specifier, with the given dimensions + * + * @exception IllegalArgumentException if either width or height is + * less than or equal to 0 + * @exception IllegalArgumentException if width * height is greater + * than Intere.MAX_VALUE + */ + public SampleModel getSampleModel (int width, int height) + { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException ("invalid dimension"); + + // test for overflow + if (width * height < Math.min (width, height)) + throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE"); + + return sampleModel.createCompatibleSampleModel (width, height); + } + + /** + * Get the color model specified by this image type specifier. + * + * @return the color model + */ public ColorModel getColorModel() { return colorModel; } + /** + * Get the number of bands specified by this image type specifier's + * sample model. + * + * @return the number of bands in the sample model + */ public int getNumBands() { return sampleModel.getNumBands(); } + /** + * Get the number of components specified by this image type + * specifier's color model. + * + * @return the number of color components per pixel + */ public int getNumComponents() { return colorModel.getNumComponents(); } + /** + * Get the sample model specified by this image type specifier. + * + * @return the sample model + */ public SampleModel getSampleModel() { return sampleModel; diff --git a/libjava/classpath/javax/imageio/ImageWriteParam.java b/libjava/classpath/javax/imageio/ImageWriteParam.java index 08f4885a8d1..84b257e04eb 100644 --- a/libjava/classpath/javax/imageio/ImageWriteParam.java +++ b/libjava/classpath/javax/imageio/ImageWriteParam.java @@ -41,6 +41,9 @@ package javax.imageio; import java.awt.Dimension; import java.util.Locale; +/** + * DOCUMENT ME + */ public class ImageWriteParam extends IIOParam { public static final int MODE_DISABLED = 0; diff --git a/libjava/classpath/javax/imageio/ImageWriter.java b/libjava/classpath/javax/imageio/ImageWriter.java index 7479c3074f7..ef352154164 100644 --- a/libjava/classpath/javax/imageio/ImageWriter.java +++ b/libjava/classpath/javax/imageio/ImageWriter.java @@ -1,5 +1,5 @@ /* ImageWriter.java -- Encodes raster images. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,11 +39,16 @@ exception statement from your version. */ package javax.imageio; import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.ResourceBundle; +import java.util.MissingResourceException; import javax.imageio.event.IIOWriteProgressListener; import javax.imageio.event.IIOWriteWarningListener; @@ -51,56 +56,161 @@ import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageWriterSpi; +/** + * A class for encoding images within the ImageIO framework. + * + * An ImageWriter for a given format is instantiated by an + * ImageWriterSpi for that format. ImageWriterSpis are registered + * with the IIORegistry. + * + * The ImageWriter API supports writing animated images that may have + * multiple frames; to support such images many methods take an index + * parameter. + * + * Images may also be written in multiple passes, where each + * successive pass increases the level of detail in the destination + * image. + */ public abstract class ImageWriter implements ImageTranscoder { private boolean aborted; - protected Locale[] availableLocales; - protected Locale locale; - protected ImageWriterSpi originatingProvider; - protected Object output; - protected List progressListeners = new ArrayList(); - protected List warningListeners = new ArrayList(); - protected List warningLocales = new ArrayList(); + /** + * All locales available for localization of warning messages, or + * null if localization is not supported. + */ + protected Locale[] availableLocales = null; + /** + * The current locale used to localize warning messages, or null if + * no locale has been set. + */ + protected Locale locale = null; + + /** + * The image writer SPI that instantiated this writer. + */ + protected ImageWriterSpi originatingProvider = null; + + /** + * An ImageInputStream to which image data is written. + */ + protected Object output = null; + + /** + * A list of installed progress listeners. Initially null, meaning + * no installed listeners. + */ + protected List progressListeners = null; + + /** + * A list of installed warning listeners. Initially null, meaning + * no installed listeners. + */ + protected List warningListeners = null; + + /** + * A list of warning locales corresponding with the list of + * installed warning listeners. Initially null, meaning no locales. + */ + protected List warningLocales = null; + + /** + * Construct an image writer. + * + * @param originatingProvider the provider that is constructing this + * image writer, or null + */ protected ImageWriter(ImageWriterSpi originatingProvider) { this.originatingProvider = originatingProvider; } + /** + * Throw an IllegalStateException if output is null. + * + * @exception IllegalStateException if output is null + */ private void checkOutputSet() { if (output == null) throw new IllegalStateException("no output set"); } + /** + * Request that writing be aborted. The unwritten portions of the + * destination image will be undefined. + * + * Writers should clear the abort flag before starting a write + * operation, then poll it periodically during the write operation. + */ public void abort() { aborted = true; } + /** + * Check if the abort flag is set. + * + * @return true if the current write operation should be aborted, + * false otherwise + */ protected boolean abortRequested() { return aborted; } + /** + * Install a write progress listener. This method will return + * immediately if listener is null. + * + * @param listener a write progress listener or null + */ public void addIIOWriteProgressListener(IIOWriteProgressListener listener) { if (listener == null) return; - + if (progressListeners == null) + progressListeners = new ArrayList (); progressListeners.add(listener); } - + + /** + * Install a write warning listener. This method will return + * immediately if listener is null. Warning messages sent to this + * listener will be localized using the current locale. If the + * current locale is null then this writer will select a sensible + * default. + * + * @param listener a write warning listener + */ public void addIIOWriteWarningListener (IIOWriteWarningListener listener) { if (listener == null) return; - + if (warningListeners == null) + warningListeners = new ArrayList (); warningListeners.add(listener); } + /** + * Check whether a new empty image can be inserted at the given + * frame index. Pixel values may be filled in later using the + * replacePixels methods. Indices greater than the insertion index + * will be incremented. If imageIndex is -1, the image will be + * appended at the end of the current image list. + * + * @param imageIndex the frame index + * + * @return true if an empty image can be inserted at imageIndex, + * false otherwise + * + * @exception IllegalStateException if output is null + * @exception IndexOutOfBoundsException if imageIndex is less than + * -1 or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ public boolean canInsertEmpty(int imageIndex) throws IOException { @@ -108,6 +218,22 @@ public abstract class ImageWriter return false; } + /** + * Check whether an image can be inserted at the given frame index. + * Indices greater than the insertion index will be incremented. If + * imageIndex is -1, the image will be appended at the end of the + * current image list. + * + * @param imageIndex the frame index + * + * @return true if an image can be inserted at imageIndex, false + * otherwise + * + * @exception IllegalStateException if output is null + * @exception IndexOutOfBoundsException if imageIndex is less than + * -1 or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ public boolean canInsertImage(int imageIndex) throws IOException { @@ -115,6 +241,20 @@ public abstract class ImageWriter return false; } + /** + * Check whether an image can be removed from the given frame index. + * Indices greater than the removal index will be decremented. + * + * @param imageIndex the frame index + * + * @return true if an image can be removed from imageIndex, false + * otherwise + * + * @exception IllegalStateException if output is null + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ public boolean canRemoveImage(int imageIndex) throws IOException { @@ -122,6 +262,20 @@ public abstract class ImageWriter return false; } + /** + * Check whether the metadata associated the image at the given + * frame index can be replaced. + * + * @param imageIndex the frame index + * + * @return true if the metadata associated with the image at + * imageIndex can be replaced, false otherwise + * + * @exception IllegalStateException if output is null + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ public boolean canReplaceImageMetadata(int imageIndex) throws IOException { @@ -129,6 +283,20 @@ public abstract class ImageWriter return false; } + /** + * Check whether the pixels within the image at the given index can + * be replaced. + * + * @param imageIndex the frame index + * + * @return true if the pixels in the image at imageIndex can be + * replaced, false otherwise + * + * @exception IllegalStateException if output is null + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ public boolean canReplacePixels(int imageIndex) throws IOException { @@ -136,6 +304,16 @@ public abstract class ImageWriter return false; } + /** + * Check whether the metadata associated the entire image stream can + * be replaced. + * + * @return true if the stream metadata can be replaced, false + * otherwise + * + * @exception IllegalStateException if output is null + * @exception IOException if a write error occurs + */ public boolean canReplaceStreamMetadata() throws IOException { @@ -143,6 +321,18 @@ public abstract class ImageWriter return false; } + /** + * Check whether an entire empty image, including empty metadata and + * empty thumbnails, can be written to the output stream, leaving + * pixel values to be filled in later using the replacePixels + * methods. + * + * @return true if an entire empty image can be written before its + * contents are filled in, false otherwise + * + * @exception IllegalStateException if output is null + * @exception IOException if a write error occurs + */ public boolean canWriteEmpty() throws IOException { @@ -150,68 +340,217 @@ public abstract class ImageWriter return false; } + /** + * Check if IIOImages containing raster data are supported. + * + * @return true if raster IIOImages are supported, false otherwise + */ public boolean canWriteRasters() { return false; } + /** + * Check if an image can be appended at the end of the current list + * of images even if prior images have already been written. + * + * @return true if sequences of images can be written, false + * otherwise + */ public boolean canWriteSequence() { return false; } + /** + * Clear the abort flag. + */ protected void clearAbortRequest() { aborted = false; } - + + /** + * Convert IIOMetadata from an input reader format, returning an + * IIOMetadata suitable for use by an image writer. + * + * The ImageTypeSpecifier specifies the destination image type. + * + * An optional ImageWriteParam argument is available in case the + * image writing parameters affect the metadata conversion. + * + * @param inData the metadata coming from an image reader + * @param imageType the output image type of the writer + * @param param the image writing parameters or null + * + * @return the converted metadata that should be used by the image + * writer, or null if this ImageTranscoder has no knowledge of the + * input metadata + * + * @exception IllegalArgumentException if either inData or imageType + * is null + */ public abstract IIOMetadata convertImageMetadata (IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param); + /** + * Convert IIOMetadata from an input stream format, returning an + * IIOMetadata suitable for use by an image writer. + * + * An optional ImageWriteParam argument is available in case the + * image writing parameters affect the metadata conversion. + * + * @param inData the metadata coming from an input image stream + * @param param the image writing parameters or null + * + * @return the converted metadata that should be used by the image + * writer, or null if this ImageTranscoder has no knowledge of the + * input metadata + * + * @exception IllegalArgumentException if inData is null + */ public abstract IIOMetadata convertStreamMetadata (IIOMetadata inData, ImageWriteParam param); + /** + * Releases any resources allocated to this object. Subsequent + * calls to methods on this object will produce undefined results. + * + * The default implementation does nothing; subclasses should use + * this method ensure that native resources are released. + */ public void dispose() { // The default implementation is empty. Subclasses have to overwrite it. } + /** + * Retrieve the available locales. Return null if no locales are + * available or a clone of availableLocales. + * + * @return an array of locales or null + */ public Locale[] getAvailableLocales() { return availableLocales; } + /** + * Get a metadata object appropriate for encoding an image specified + * by the given image type specifier and optional image write + * parameters. + * + * @param imageType an image type specifier + * @param param image writing parameters, or null + * + * @return a metadata object appropriate for encoding an image of + * the given type with the given parameters + */ public abstract IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType, ImageWriteParam param); + /** + * Get a metadata object appropriate for encoding the default image + * type handled by this writer, optionally considering image write + * parameters. + * + * @param param image writing parameters, or null + * + * @return a metadata object appropriate for encoding an image of + * the default type with the given parameters + */ public abstract IIOMetadata getDefaultStreamMetadata (ImageWriteParam param); + /** + * Retrieve the default write parameters for this writer's image + * format. + * + * The default implementation returns new ImageWriteParam(). + * + * @return image writing parameters + */ public ImageWriteParam getDefaultWriteParam() { return new ImageWriteParam(getLocale()); } + /** + * Get this writer's locale. null is returned if the locale has not + * been set. + * + * @return this writer's locale, or null + */ public Locale getLocale() { return locale; } - public int getNumThumbnailsSupported (ImageTypeSpecifier imageType, ImageWriteParam param, - IIOMetadata streamMetadata, IIOMetadata imageMetadata) + /** + * Get the number of thumbnails supported by this image writer, + * based on the given image type, image writing parameters, and + * stream and image metadata. The image writing parameters are + * optional, in case they affect the number of thumbnails supported. + * + * @param imageType an image type specifier, or null + * @param param image writing parameters, or null + * @param streamMetadata the metadata associated with this stream, + * or null + * @param imageMetadata the metadata associated with this image, or + * null + * + * @return the number of thumbnails that this writer supports + * writing or -1 if the given information is insufficient + */ + public int getNumThumbnailsSupported (ImageTypeSpecifier imageType, + ImageWriteParam param, + IIOMetadata streamMetadata, + IIOMetadata imageMetadata) { return 0; } + /** + * Get the ImageWriterSpi that created this writer or null. + * + * @return an ImageWriterSpi, or null + */ public ImageWriterSpi getOriginatingProvider() { return originatingProvider; } + /** + * Get this reader's image output destination. null is returned if + * the image destination has not been set. + * + * @return an image output destination object, or null + */ public Object getOutput() { return output; } + /** + * Get the preferred sizes for thumbnails based on the given image + * type, image writing parameters, and stream and image metadata. + * The preferred sizes are returned in pairs of dimension values; + * the first value in the array is a dimension object representing + * the minimum thumbnail size, the second value is a dimension + * object representing a maximum thumbnail size. The writer can + * select a size within the range given by each pair, or it can + * ignore these size hints. + * + * @param imageType an image type specifier, or null + * @param param image writing parameters, or null + * @param streamMetadata the metadata associated with this stream, + * or null + * @param imageMetadata the metadata associated with this image, or + * null + * + * @return an array of dimension pairs whose length is a multiple of + * 2, or null if there is no preferred size (any size is allowed) or + * if the size is unknown (insufficient information was provided) + */ public Dimension[] getPreferredThumbnailSizes (ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, @@ -220,120 +559,305 @@ public abstract class ImageWriter return null; } + /** + * Notifies all installed write progress listeners that image + * loading has completed by calling their imageComplete methods. + */ protected void processImageComplete() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.imageComplete(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.imageComplete(this); + } } } + /** + * Notifies all installed write progress listeners that a certain + * percentage of the image has been loaded, by calling their + * imageProgress methods. + * + * @param percentageDone the percentage of image data that has been + * loaded + */ protected void processImageProgress(float percentageDone) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.imageProgress(this, percentageDone); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.imageProgress(this, percentageDone); + } } } + /** + * Notifies all installed write progress listeners, by calling their + * imageStarted methods, that image loading has started on the given + * image. + * + * @param imageIndex the frame index of the image that has started + * loading + */ protected void processImageStarted(int imageIndex) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.imageStarted(this, imageIndex); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.imageStarted(this, imageIndex); + } } } + /** + * Notifies all installed write progress listeners, by calling their + * thumbnailComplete methods, that a thumbnail has completed + * loading. + */ protected void processThumbnailComplete() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.thumbnailComplete(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.thumbnailComplete(this); + } } } + /** + * Notifies all installed write progress listeners that a certain + * percentage of a thumbnail has been loaded, by calling their + * thumbnailProgress methods. + * + * @param percentageDone the percentage of thumbnail data that has + * been loaded + */ protected void processThumbnailProgress(float percentageDone) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.thumbnailProgress(this, percentageDone); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.thumbnailProgress(this, percentageDone); + } } } + /** + * Notifies all installed write progress listeners, by calling their + * imageStarted methods, that thumbnail loading has started on the + * given thumbnail of the given image. + * + * @param imageIndex the frame index of the image one of who's + * thumbnails has started loading + * @param thumbnailIndex the index of the thumbnail that has started + * loading + */ protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.thumbnailStarted(this, imageIndex, thumbnailIndex); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.thumbnailStarted(this, imageIndex, thumbnailIndex); + } } } + /** + * Notifies all installed warning listeners, by calling their + * warningOccurred methods, that a warning message has been raised. + * + * @param imageIndex the index of the image that was being written + * when the warning was raised + * @param warning the warning message + * + * @exception IllegalArgumentException if warning is null + */ protected void processWarningOccurred(int imageIndex, String warning) { - Iterator it = warningListeners.iterator(); + if (warningListeners != null) + { + Iterator it = warningListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteWarningListener listener = + (IIOWriteWarningListener) it.next(); + listener.warningOccurred(this, imageIndex, warning); + } + } + } + + /** + * Notify all installed warning listeners, by calling their + * warningOccurred methods, that a warning message has been raised. + * The warning message is retrieved from a resource bundle, using + * the given basename and keyword. + * + * @param imageIndex the index of the image that was being written + * when the warning was raised + * @param baseName the basename of the resource from which to + * retrieve the warning message + * @param keyword the keyword used to retrieve the warning from the + * resource bundle + * + * @exception IllegalArgumentException if either baseName or keyword + * is null + * @exception IllegalArgumentException if no resource bundle is + * found using baseName + * @exception IllegalArgumentException if the given keyword produces + * no results from the resource bundle + * @exception IllegalArgumentException if the retrieved object is + * not a String + */ + protected void processWarningOccurred(int imageIndex, + String baseName, + String keyword) + { + if (baseName == null || keyword == null) + throw new IllegalArgumentException ("null argument"); + + ResourceBundle b = null; - while (it.hasNext()) + try { - IIOWriteWarningListener listener = (IIOWriteWarningListener) it.next(); - listener.warningOccurred(this, imageIndex, warning); + b = ResourceBundle.getBundle(baseName, getLocale()); + } + catch (MissingResourceException e) + { + throw new IllegalArgumentException ("no resource bundle found"); + } + + Object str = null; + + try + { + str = b.getObject(keyword); + } + catch (MissingResourceException e) + { + throw new IllegalArgumentException ("no results found for keyword"); + } + + if (! (str instanceof String)) + throw new IllegalArgumentException ("retrieved object not a String"); + + String warning = (String) str; + + if (warningListeners != null) + { + Iterator it = warningListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteWarningListener listener = + (IIOWriteWarningListener) it.next(); + listener.warningOccurred(this, imageIndex, warning); + } } } + /** + * Notifies all installed write progress listeners that image + * loading has been aborted by calling their writeAborted methods. + */ protected void processWriteAborted() { - Iterator it = progressListeners.iterator(); - - while (it.hasNext()) + if (progressListeners != null) { - IIOWriteProgressListener listener = (IIOWriteProgressListener) it.next(); - listener.writeAborted(this); + Iterator it = progressListeners.iterator(); + + while (it.hasNext()) + { + IIOWriteProgressListener listener = + (IIOWriteProgressListener) it.next(); + listener.writeAborted(this); + } } } + /** + * Uninstall all write progress listeners. + */ public void removeAllIIOWriteProgressListeners() { - progressListeners.clear(); + if (progressListeners != null) + { + progressListeners.clear(); + } } + /** + * Uninstall all write warning listeners. + */ public void removeAllIIOWriteWarningListeners() { - progressListeners.clear(); + if (progressListeners != null) + { + progressListeners.clear(); + } } - - public void removeIIOWriteProgressListener (IIOWriteProgressListener listener) + + /** + * Uninstall the given write progress listener. + * + * @param listener the listener to remove + */ + public void removeIIOWriteProgressListener (IIOWriteProgressListener listener) { if (listener == null) return; - - progressListeners.remove(listener); + if (progressListeners != null) + { + progressListeners.remove(listener); + } } - + /** + * Uninstall the given write warning listener. + * + * @param listener the listener to remove + */ public void removeIIOWriteWarningListener (IIOWriteWarningListener listener) { if (listener == null) return; - - warningListeners.remove(listener); + if (warningListeners != null) + { + warningListeners.remove(listener); + } } - + /** + * Reset this writer's internal state. + */ public void reset() { setOutput(null); @@ -343,6 +867,11 @@ public abstract class ImageWriter clearAbortRequest(); } + /** + * Set the current locale or use the default locale. + * + * @param locale the locale to set, or null + */ public void setLocale(Locale locale) { if (locale != null) @@ -362,6 +891,18 @@ public abstract class ImageWriter this.locale = locale; } + /** + * Set the output destination of the given object. The output + * destination must be set before many methods can be called on this + * writer. (see all ImageWriter methods that throw + * IllegalStateException). If input is null then the current input + * source will be removed. + * + * @param input the output destination object + * + * @exception IllegalArgumentException if input is not a valid input + * source for this writer and is not an ImageInputStream + */ public void setOutput(Object output) { if (output != null) @@ -385,6 +926,464 @@ public abstract class ImageWriter this.output = output; } + /** + * Write an image stream, including thumbnails and metadata to the + * output stream. The output must have been set prior to this + * method being called. Metadata associated with the stream may be + * supplied, or it can be left null. IIOImage may contain raster + * data if this writer supports rasters, or it will contain a + * rendered image. Thumbnails are resized if need be. Image + * writing parameters may be specified to affect writing, or may be + * left null. + * + * @param streamMetadata metadata associated with this stream, or + * null + * @param image an IIOImage containing image data, metadata and + * thumbnails to be written + * @param param image writing parameters, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if image contains raster + * data but this writer does not support rasters + * @exception IllegalArgumentException if image is null + * @exception IOException if a write error occurs + */ public abstract void write (IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException; + + /** + * Complete inserting an empty image in the output stream. + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if inserting empty + * images is not supported + * @exception IllegalArgumentException if a call to + * prepareInsertEmpty was not called previous to this method being + * called (a sequence of prepareInsertEmpty calls must be terminated + * by a call to endInsertEmpty) + * @exception IllegalArgumentException if prepareWriteEmpty was + * called before this method being called (without a terminating + * call to endWriteEmpty) + * @exception IllegalArgumentException if prepareReplacePixels was + * called before this method being called (without a terminating + * call to endReplacePixels) + * @exception IOException if a write error occurs + */ + public void endInsertEmpty () + throws IOException + { + if (!canInsertEmpty(0)) + throw new UnsupportedOperationException(); + } + + /** + * Complete replacing pixels in an image in the output stream. + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing pixels is + * not supported by this writer + * @exception IllegalArgumentException if prepareReplacePixels was + * not called before this method being called + * @exception IOException if a write error occurs + */ + public void endReplacePixels () + throws IOException + { + if (!canReplacePixels(0)) + throw new UnsupportedOperationException(); + } + + /** + * Complete writing an empty image to the image output stream. + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if writing empty images + * is not supported + * @exception IllegalArgumentException if a call to + * prepareWriteEmpty was not called previous to this method being + * called (a sequence of prepareWriteEmpty calls must be terminated + * by a call to endWriteEmpty) + * @exception IllegalArgumentException if prepareInsertEmpty was + * called before this method being called (without a terminating + * call to endInsertEmpty) + * @exception IllegalArgumentException if prepareReplacePixels was + * called before this method being called (without a terminating + * call to endReplacePixels) + * @exception IOException if a write error occurs + */ + public void endWriteEmpty () + throws IOException + { + if (!canWriteEmpty()) + throw new UnsupportedOperationException(); + } + + /** + * Complete writing a sequence of images to the output stream. This + * method may patch header data and write out footer data. + * + * @exception IllegalStateException if output is null + * @exception IllegalStateException if prepareWriteSequence has not + * been called + * @exception UnsupportedOperationException if writing a sequence of + * images is not supported + * @exception IOException if a write error occurs + */ + public void endWriteSequence () + throws IOException + { + checkOutputSet(); + if (!canWriteSequence()) + throw new UnsupportedOperationException(); + } + + /** + * Start inserting an empty image in the image output stream. All + * indices after the specified index are incremented. An index of + * -1 implies that the empty image should be appended to the end of + * the current image list. + * + * The insertion that this method call starts is not complete until + * endInsertEmpty is called. prepareInsertEmpty cannot be called + * again until endInsertEmpty is called and calls to + * prepareWriteEmpty and prepareInsertEmpty may not be intersperced. + * + * @param imageIndex the image index + * @param imageType the image type specifier + * @param width the image width + * @param height the image height + * @param imageMetadata the image metadata, or null + * @param thumbnails a list of thumbnails, or null + * @param param image write parameters, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if inserting empty + * images is not supported + * @exception IndexOutOfBoundsException if imageIndex is less than + * -1 or greater than the last index in the current image list + * @exception IllegalStateException if a previous call to + * prepareInsertEmpty was made (without a terminating call to + * endInsertEmpty) + * @exception IllegalStateException if a previous call to + * prepareWriteEmpty was made (without a terminating call to + * endWriteEmpty) + * @exception IllegalArgumentException if imageType is null or + * thumbnails contain non-BufferedImage objects + * @exception IllegalArgumentException if either width or height is + * less than 1 + * @exception IOException if a write error occurs + */ + public void prepareInsertEmpty (int imageIndex, ImageTypeSpecifier imageType, + int width, int height, + IIOMetadata imageMetadata, + List thumbnails, + ImageWriteParam param) + throws IOException + { + if (!canInsertEmpty(imageIndex)) + throw new UnsupportedOperationException(); + } + + /** + * Start the replacement of pixels within an image in the output + * stream. Output pixels will be clipped to lie within region. + * + * @param imageIndex the index of the image in which pixels are + * being replaced + * @param region the rectangle to which to limit pixel replacement + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing pixels is + * not supported + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IllegalStateException if a previous call to + * prepareReplacePixels was made (without a terminating call to + * endReplacePixels) + * @exception IllegalArgumentException if either region.width or + * region.height is less than 1, or if region is null + * @exception IOException if a write error occurs + */ + public void prepareReplacePixels (int imageIndex, Rectangle region) + throws IOException + { + if (canReplacePixels(imageIndex)) + throw new UnsupportedOperationException(); + } + + /** + * Start writing an empty image to the end of the image output + * stream. + * + * The writing that this method call starts is not complete until + * endWriteEmpty is called. prepareWritetEmpty cannot be called + * again until endWriteEmpty is called and calls to + * prepareWriteEmpty and prepareInsertEmpty may not be intersperced. + * + * @param streamMetadata metadata associated with the stream, or null + * @param imageType the image type specifier + * @param width the image width + * @param height the image height + * @param imageMetadata the image metadata, or null + * @param thumbnails a list of thumbnails, or null + * @param param image write parameters, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if writing empty images + * is not supported + * @exception IndexOutOfBoundsException if imageIndex is less than + * -1 or greater than the last index in the current image list + * @exception IllegalStateException if a previous call to + * prepareInsertEmpty was made (without a terminating call to + * endInsertEmpty) + * @exception IllegalStateException if a previous call to + * prepareWriteEmpty was made (without a terminating call to + * endWriteEmpty) + * @exception IllegalArgumentException if imageType is null or + * thumbnails contain non-BufferedImage objects + * @exception IllegalArgumentException if either width or height is + * less than 1 + * @exception IOException if a write error occurs + */ + public void prepareWriteEmpty (IIOMetadata streamMetadata, + ImageTypeSpecifier imageType, + int width, int height, + IIOMetadata imageMetadata, + List thumbnails, + ImageWriteParam param) + throws IOException + { + if (!canWriteEmpty()) + throw new UnsupportedOperationException(); + } + + /** + * Start the writing of a sequence of images. + * + * @param streamMetadata the stream metadata, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if writing sequences of + * images is not supported + * @exception IOException if a write error occurs + */ + public void prepareWriteSequence (IIOMetadata streamMetadata) + throws IOException + { + checkOutputSet(); + if (!canWriteSequence()) + throw new UnsupportedOperationException(); + } + + /** + * Remove the image at the specified index from the output stream. + * + * @param imageIndex the frame index from which to remove the image + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if removing this image + * is not supported + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ + public void removeImage (int imageIndex) + throws IOException + { + if (!canRemoveImage(imageIndex)) + throw new UnsupportedOperationException(); + } + + /** + * Replace the metadata associated with the image at the given + * index. + * + * @param imageIndex the index of the image whose metadata should be + * replaced + * @param imageMetadata the metadata, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing this + * image's metadata is not supported + * @exception IndexOutOfBoundsException if imageIndex is less than 0 + * or greater than the last index in the current image list + * @exception IOException if a write error occurs + */ + public void replaceImageMetadata (int imageIndex, IIOMetadata imageMetadata) + throws IOException + { + if (!canReplaceImageMetadata(imageIndex)) + throw new UnsupportedOperationException(); + } + + /** + * Replace a region of an image in the output stream with a portion + * of the given rendered image. The image data must be of the same + * type as that in the output stream. The destination region is + * given by the image writing parameters and the source region is + * the one given to prepareReplacePixels. + * + * @param image the rendered image with which to overwrite the image + * region in the stream + * @param param the image writing parameters + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing pixels is + * not supported + * @exception IllegalStateException if prepareReplacePixels was not + * called before this method was called + * @exception IllegalArgumentException if image is null or if param + * is null or if the overlap of the source and destination regions + * contains no pixels or if the image types differ and no conversion + * is possible + * @exception IOException if a write error occurs + */ + public void replacePixels (RenderedImage image, + ImageWriteParam param) + throws IOException + { + if (!canReplacePixels(0)) + throw new UnsupportedOperationException(); + } + + /** + * Replace a region of an image in the output stream with a portion + * of the given raster data. The image data must be of the same + * type as that in the output stream. The destination region is + * given by the image writing parameters and the source region is + * the one given to prepareReplacePixels. + * + * @param raster the raster data with which to overwrite the image + * region in the stream + * @param param the image writing parameters + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing pixels is + * not supported + * @exception IllegalStateException if prepareReplacePixels was not + * called before this method was called + * @exception UnsupportedOperationException if raster data is not + * supported + * @exception IllegalArgumentException if raster is null or if param + * is null or if the overlap of the source and destination regions + * contains no pixels or if the image types differ and no conversion + * is possible + * @exception IOException if a write error occurs + */ + public void replacePixels (Raster raster, ImageWriteParam param) + throws IOException + { + if (!canReplacePixels(0)) + throw new UnsupportedOperationException(); + } + + /** + * Replace the metadata associated with this image stream. + * + * @param streamMetadata the stream metadata, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if replacing the stream + * metadata is not supported + * @exception IOException if a write error occurs + */ + public void replaceStreamMetadata (IIOMetadata streamMetadata) + throws IOException + { + if (!canReplaceStreamMetadata()) + throw new UnsupportedOperationException(); + } + + /** + * Write a rendered image to the output stream. + * + * @param image a rendered image containing image data to be written + * + * @exception IllegalStateException if output is null + * @exception IllegalArgumentException if image is null + * @exception IOException if a write error occurs + */ + public void write (RenderedImage image) + throws IOException + { + checkOutputSet(); + write (null, new IIOImage(image, null, null), null); + } + + /** + * Write a image data, metadata and thumbnails to the output stream. + * + * @param image image data, metadata and thumbnails to be written + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if image contains raster + * data but this writer does not support rasters + * @exception IllegalArgumentException if image is null + * @exception IOException if a write error occurs + */ + public void write (IIOImage image) + throws IOException + { + checkOutputSet(); + write (null, image, null); + } + + /** + * Insert an image into the output stream. Indices greater than the + * specified index are incremented accordingly. Specifying an index + * of -1 causes the image to be appended at the end of the current + * image list. + * + * @param imageIndex the frame index at which to insert the image + * @param image the image data, metadata and thumbnails to be + * inserted + * @param the image write parameters, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if image insertion is + * not supported + * @exception IllegalArgumentException if image is null + * @exception IndexOutOfBoundsException if imageIndex is less than + * -1 or greater than the last index in the current image list + * @exception UnsupportedOperationException if image contains raster + * data but this writer does not support rasters + * @exception IOException if a write error occurs + */ + public void writeInsert (int imageIndex, IIOImage image, ImageWriteParam param) + throws IOException + { + if (!canInsertImage(imageIndex)) + throw new UnsupportedOperationException(); + } + + /** + * Write a sequence of images, including thumbnails and metadata, to + * the output stream. The output must have been set prior to this + * method being called. Metadata associated with the stream may be + * supplied, or it can be left null. IIOImage may contain raster + * data if this writer supports rasters, or it will contain a + * rendered image. Thumbnails are resized if need be. Image + * writing parameters may be specified to affect writing, or may be + * left null. + * + * @param streamMetadata metadata associated with this stream, or + * null + * @param image an IIOImage containing image data, metadata and + * thumbnails to be written + * @param param image writing parameters, or null + * + * @exception IllegalStateException if output is null + * @exception UnsupportedOperationException if writing sequences of + * images is not supported + * @exception IllegalArgumentException if image is null + * @exception UnsupportedOperationException if image contains raster + * data but this writer does not support rasters + * @exception IOException if a write error occurs + */ + public void writeToSequence (IIOImage image, ImageWriteParam param) + throws IOException + { + if (!canWriteSequence()) + throw new UnsupportedOperationException(); + } } diff --git a/libjava/classpath/javax/imageio/metadata/IIOAttr.java b/libjava/classpath/javax/imageio/metadata/IIOAttr.java deleted file mode 100644 index 0c1d3d2ef3f..00000000000 --- a/libjava/classpath/javax/imageio/metadata/IIOAttr.java +++ /dev/null @@ -1,378 +0,0 @@ -/* IIOAttr.java -- - Copyright (C) 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 javax.imageio.metadata; - -import org.w3c.dom.Attr; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.TypeInfo; -import org.w3c.dom.UserDataHandler; - -/** - * Simple Attr node for metadata trees - * - * @author jlquinn - */ -class IIOAttr implements Attr -{ - String name; - String value; - IIOMetadataNode owner; - - public IIOAttr(String name, String value, IIOMetadataNode owner) - { - this.name = name; - this.value = value; - this.owner = owner; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Attr#getName() - */ - public String getName() - { - return name; - } - - public TypeInfo getSchemaTypeInfo() - { - throw new Error("not implemented"); - } - - /* (non-Javadoc) - * @see org.w3c.dom.Attr#getSpecified() - */ - public boolean getSpecified() - { - // I don't think there can be default attrs in metadata - return true; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Attr#getValue() - */ - public String getValue() - { - return value; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Attr#setValue(java.lang.String) - */ - public void setValue(String value) throws DOMException - { - this.value = value; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Attr#getOwnerElement() - */ - public Element getOwnerElement() - { - return owner; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getNodeName() - */ - public String getNodeName() - { - return name; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getNodeValue() - */ - public String getNodeValue() throws DOMException - { - return value; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#setNodeValue(java.lang.String) - */ - public void setNodeValue(String nodeValue) throws DOMException - { - this.value = nodeValue; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getNodeType() - */ - public short getNodeType() - { - return ATTRIBUTE_NODE; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getParentNode() - */ - public Node getParentNode() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getChildNodes() - */ - public NodeList getChildNodes() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getFirstChild() - */ - public Node getFirstChild() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getLastChild() - */ - public Node getLastChild() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getPreviousSibling() - */ - public Node getPreviousSibling() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getNextSibling() - */ - public Node getNextSibling() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getAttributes() - */ - public NamedNodeMap getAttributes() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getOwnerDocument() - */ - public Document getOwnerDocument() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node) - */ - public Node insertBefore(Node newChild, Node refChild) throws DOMException - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node) - */ - public Node replaceChild(Node newChild, Node oldChild) throws DOMException - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#removeChild(org.w3c.dom.Node) - */ - public Node removeChild(Node oldChild) throws DOMException - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#appendChild(org.w3c.dom.Node) - */ - public Node appendChild(Node newChild) throws DOMException - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#hasChildNodes() - */ - public boolean hasChildNodes() - { - return false; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#cloneNode(boolean) - */ - public Node cloneNode(boolean deep) - { - return new IIOAttr(name, value, owner); - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#normalize() - */ - public void normalize() - { - } - - public boolean isDefaultNamespace(String namespaceURI) - { - throw new Error("not implemented"); - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String) - */ - public boolean isSupported(String feature, String version) - { - return false; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getNamespaceURI() - */ - public String getNamespaceURI() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getPrefix() - */ - public String getPrefix() - { - return null; - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#setPrefix(java.lang.String) - */ - public void setPrefix(String prefix) throws DOMException - { - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#getLocalName() - */ - public String getLocalName() - { - return name; - } - - public Object getUserData(String key) - { - throw new Error("not implemented"); - } - - /* (non-Javadoc) - * @see org.w3c.dom.Node#hasAttributes() - */ - public boolean hasAttributes() - { - return false; - } - - public boolean isId() - { - throw new Error("not implemented"); - } - - public String lookupNamespaceURI(String prefix) - { - throw new Error("not implemented"); - } - - public String lookupPrefix(String namespaceURI) - { - throw new Error("not implemented"); - } - - public Object setUserData(String key, Object data, UserDataHandler handler) - { - throw new Error("not implemented"); - } - - public String getBaseURI() - { - throw new Error("not implemented"); - } - - public String getTextContent() - { - throw new Error("not implemented"); - } - - public void setTextContent(String textContent) - { - throw new Error("not implemented"); - } - - public short compareDocumentPosition(Node other) - throws DOMException - { - throw new Error("not implemented"); - } - - public Object getFeature(String feature, String version) - { - throw new Error("not implemented"); - } - - public boolean isEqualNode(Node other) - { - throw new Error("not implemented"); - } - - public boolean isSameNode(Node other) - { - throw new Error("not implemented"); - } -} diff --git a/libjava/classpath/javax/imageio/metadata/IIOMetadata.java b/libjava/classpath/javax/imageio/metadata/IIOMetadata.java index d727e1d1e51..e5105de2caf 100644 --- a/libjava/classpath/javax/imageio/metadata/IIOMetadata.java +++ b/libjava/classpath/javax/imageio/metadata/IIOMetadata.java @@ -38,8 +38,41 @@ exception statement from your version. */ package javax.imageio.metadata; +import org.w3c.dom.Node; + /** + * Represents metadata that describe an image or an image stream. + * Each ImageIO plugin will represent image data using an opaque + * object but all such objects should expose their internal + * information as a tree of IIOMetadataNodes. + * + * There are three formats of metadata that a plugin can support: + * + * <ul> + * <li>a "native" format</li> + * <li>a custom format</li> + * <li>a standard plugin-neutral format</li> + * </ul> + * + * If a plugin supports more than one format of metadata, the other + * formats can be retrieved by calling getMetadataFormatNames. + * + * The native format is used to transfer metadata from one image to + * another image of the same type, losslessly. + * + * The custom format describes the image metadata and exposes a tree + * of IIOMetadataNodes but its internal representation is specific to + * this plugin. + * + * The plugin-neutral format uses a generic tree structure as its + * internal representation. + * + * ImageTranscoders may be used to convert metadata understood by one + * plugin to metadata understood by another, however the conversion + * may be lossy. + * * @author Michael Koch (konqueror@gmx.de) + * @author Thomas Fitzsimmons (fitzsim@redhat.com) */ public abstract class IIOMetadata { @@ -52,7 +85,7 @@ public abstract class IIOMetadata protected boolean standardFormatSupported; /** - * Creates a <code>IIOMetaData</code> object. + * Construct an IIOMetadata object. */ protected IIOMetadata() { @@ -60,7 +93,7 @@ public abstract class IIOMetadata } /** - * Creates a <code>IIOMetaData</code> object with the given arguments. + * Construct an IIOMetadata object. * * @param standardMetadataFormatSupported * @param nativeMetadataFormatName @@ -210,4 +243,81 @@ public abstract class IIOMetadata { this.controller = controller; } + + public abstract Node getAsTree (String formatName); + + protected IIOMetadataNode getStandardChromaNode () + { + return null; + } + + protected IIOMetadataNode getStandardCompressionNode () + { + return null; + } + + protected IIOMetadataNode getStandardDataNode () + { + return null; + } + + protected IIOMetadataNode getStandardDimensionNode () + { + return null; + } + + protected IIOMetadataNode getStandardDocumentNode () + { + return null; + } + + protected IIOMetadataNode getStandardTextNode () + { + return null; + } + + protected IIOMetadataNode getStandardTileNode () + { + return null; + } + + protected IIOMetadataNode getStandardTransparencyNode () + { + return null; + } + + private void appendChild (IIOMetadataNode node, + IIOMetadataNode child) + { + if (child != null) + node.appendChild(child); + } + + protected final IIOMetadataNode getStandardTree () + { + IIOMetadataNode node = new IIOMetadataNode(); + + appendChild (node, getStandardChromaNode()); + appendChild (node, getStandardCompressionNode()); + appendChild (node, getStandardDataNode()); + appendChild (node, getStandardDimensionNode()); + appendChild (node, getStandardDocumentNode()); + appendChild (node, getStandardTextNode()); + appendChild (node, getStandardTileNode()); + appendChild (node, getStandardTransparencyNode()); + + return node; + } + + public abstract void mergeTree (String formatName, + Node root) + throws IIOInvalidTreeException; + + public void setFromTree (String formatName, Node root) + throws IIOInvalidTreeException + { + reset(); + + mergeTree (formatName, root); + } } diff --git a/libjava/classpath/javax/imageio/metadata/IIOMetadataFormatImpl.java b/libjava/classpath/javax/imageio/metadata/IIOMetadataFormatImpl.java index 2ce8f9c3d4b..aad30447c2f 100644 --- a/libjava/classpath/javax/imageio/metadata/IIOMetadataFormatImpl.java +++ b/libjava/classpath/javax/imageio/metadata/IIOMetadataFormatImpl.java @@ -38,6 +38,848 @@ exception statement from your version. */ package javax.imageio.metadata; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.UserDataHandler; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.MissingResourceException; +import javax.imageio.ImageTypeSpecifier; + public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat { + /** + * The standard metadata format name constant set to + * "javax_imageio_1.0". + */ + public static final String standardMetadataFormatName = "javax_imageio_1.0"; + + private String rootName; + + // These maps assume that each element name is unique. + + private Map nodes = new HashMap(); + + // A mapping from element name to child policy. + private Map childPolicies = new HashMap(); + + // A mapping from element name to the permissible number of + // children. Values in this map are length-two integer arrays; the + // first index is the minimum bound, the second index is the maximum + // bound. + private Map childRanges = new HashMap(); + + private String resourceBaseName; + + // Package-private so that it may be used in IIOMetadataNode. + static class IIOMetadataNodeAttr extends IIOMetadataNode + implements Attr + { + protected Element owner; + protected String name; + protected int dataType; + protected boolean required; + protected String defaultValue; + + public IIOMetadataNodeAttr (Element owner, + String name, + String defaultValue) + { + this (owner, name, IIOMetadataFormat.DATATYPE_STRING, + true, defaultValue); + } + + public IIOMetadataNodeAttr (Element owner, + String name, + int dataType, + boolean required, + String defaultValue) + { + this.owner = owner; + this.name = name; + this.dataType = dataType; + this.required = required; + this.defaultValue = defaultValue; + } + + public String getName () + { + return name; + } + + public Element getOwnerElement () + { + return owner; + } + + public int getDataType () + { + return dataType; + } + + public TypeInfo getSchemaTypeInfo () + { + return null; + } + + public boolean getSpecified () + { + return false; + } + + public String getValue () + { + return defaultValue; + } + + public boolean isId() + { + return false; + } + + public void setValue (String value) + { + } + + // new methods + + public boolean isRequired () + { + return required; + } + } + + private class IIOMetadataNodeAttrEnumerated extends IIOMetadataNodeAttr + { + protected List enumeratedValues; + + public IIOMetadataNodeAttrEnumerated (Element owner, + String name, + int dataType, + boolean required, + String defaultValue, + List enumeratedValues) + { + super (owner, name, dataType, required, defaultValue); + this.enumeratedValues = new ArrayList (enumeratedValues); + } + + public Object[] getEnumerations () + { + return enumeratedValues.toArray (); + } + } + + private class IIOMetadataNodeAttrBounded extends IIOMetadataNodeAttr + { + protected String minValue; + protected String maxValue; + protected boolean minInclusive; + protected boolean maxInclusive; + + public IIOMetadataNodeAttrBounded (Element owner, + String name, + int dataType, + boolean required, + String defaultValue, + String minValue, + String maxValue, + boolean minInclusive, + boolean maxInclusive) + { + super (owner, name, dataType, required, defaultValue); + this.minValue = minValue; + this.maxValue = maxValue; + this.minInclusive = minInclusive; + this.maxInclusive = maxInclusive; + } + + public String getMinValue () + { + return minValue; + } + + public String getMaxValue () + { + return maxValue; + } + } + + private class IIOMetadataNodeAttrList extends IIOMetadataNodeAttr + { + protected int listMinLength; + protected int listMaxLength; + + public IIOMetadataNodeAttrList (Element owner, + String name, + int dataType, + boolean required, + int listMinLength, + int listMaxLength) + { + super (owner, name, dataType, required, null); + this.listMinLength = listMinLength; + this.listMaxLength = listMaxLength; + } + + public int getListMinLength () + { + return listMinLength; + } + + public int getListMaxLength () + { + return listMaxLength; + } + } + + private class NodeObject + { + protected Element owner; + protected Class classType; + protected boolean required; + protected Object defaultValue; + protected int valueType; + + public NodeObject (Element owner, + Class classType, + boolean required, + Object defaultValue) + { + this.owner = owner; + this.classType = classType; + this.required = required; + this.defaultValue = defaultValue; + valueType = IIOMetadataFormat.VALUE_ARBITRARY; + } + + public int getValueType () + { + return valueType; + } + + public Class getClassType () + { + return classType; + } + + public Element getOwnerElement () + { + return owner; + } + + public Object getDefaultValue () + { + return defaultValue; + } + + public boolean isRequired () + { + return required; + } + } + + private class NodeObjectEnumerated extends NodeObject + { + protected List enumeratedValues; + + public NodeObjectEnumerated (Element owner, + Class classType, + boolean required, + Object defaultValue, + List enumeratedValues) + { + super (owner, classType, false, defaultValue); + this.enumeratedValues = enumeratedValues; + valueType = IIOMetadataFormat.VALUE_ENUMERATION; + } + + public Object[] getEnumerations () + { + return enumeratedValues.toArray(); + } + } + + private class NodeObjectBounded extends NodeObject + { + protected Comparable minValue; + protected Comparable maxValue; + protected boolean minInclusive; + protected boolean maxInclusive; + + public NodeObjectBounded (Element owner, + Class classType, + Object defaultValue, + Comparable minValue, + Comparable maxValue, + boolean minInclusive, + boolean maxInclusive) + { + super (owner, classType, false, defaultValue); + this.minValue = minValue; + this.maxValue = maxValue; + this.minInclusive = minInclusive; + this.maxInclusive = maxInclusive; + if (minInclusive) + { + if (maxInclusive) + valueType = IIOMetadataFormat.VALUE_RANGE_MIN_MAX_INCLUSIVE; + else + valueType = IIOMetadataFormat.VALUE_RANGE_MIN_INCLUSIVE; + } + else + { + if (maxInclusive) + valueType = IIOMetadataFormat.VALUE_RANGE_MAX_INCLUSIVE; + else + valueType = IIOMetadataFormat.VALUE_RANGE; + } + } + + public Comparable getMinValue () + { + return minValue; + } + + public Comparable getMaxValue () + { + return maxValue; + } + } + + private class NodeObjectArray extends NodeObject + { + protected Integer arrayMinLength; + protected Integer arrayMaxLength; + + public NodeObjectArray (Element owner, + Class classType, + int arrayMinLength, + int arrayMaxLength) + { + super (owner, classType, false, null); + this.arrayMinLength = new Integer (arrayMinLength); + this.arrayMaxLength = new Integer (arrayMaxLength); + valueType = IIOMetadataFormat.VALUE_LIST; + } + + public Comparable getArrayMinLength () + { + return arrayMinLength; + } + + public Comparable getArrayMaxLength () + { + return arrayMaxLength; + } + } + + /** + * Construct a blank IIOMetadataFormatImpl with the given root name + * and child policy. + * + * @param rootName the root element name + * @param childPolicy the child policy of the root element + * + * @exception IllegalArgumentException if rootName is null + * @exception IllegalArgumentException if childPolicy is + * CHILD_POLICY_REPEAT or if childPolicy is not a CHILD_POLICY + * constant + */ + public IIOMetadataFormatImpl (String rootName, int childPolicy) + { + if (rootName == null) + throw new IllegalArgumentException ("null argument"); + + if (childPolicy < IIOMetadataFormat.CHILD_POLICY_ALL + || childPolicy > IIOMetadataFormat.CHILD_POLICY_SOME + || childPolicy == IIOMetadataFormat.CHILD_POLICY_REPEAT) + throw new IllegalArgumentException ("wrong child policy"); + + nodes.put (rootName, new IIOMetadataNode (rootName)); + childPolicies.put (rootName, new Integer (childPolicy)); + this.rootName = rootName; + } + + /** + * Construct a blank IIOMetadataFormatImpl with the given root name, + * a child policy of CHILD_POLICY_REPEAT and the given minimum and + * maximum limits on the number of root element children. + * + * @param rootName the root element name + * @param minChildren the minimum number of children that this node + * can have + * @param maxChildren the maximum number of children that this node + * can have + * + * @exception IllegalArgumentException if rootName is null + * @exception IllegalArgumentException if minChildren is less than + * zero or greater than maxChildren + */ + public IIOMetadataFormatImpl (String rootName, + int minChildren, + int maxChildren) + { + if (rootName == null) + throw new IllegalArgumentException ("null argument"); + + if (minChildren < 0 || maxChildren < minChildren) + throw new IllegalArgumentException ("invalid min or max children argument"); + + nodes.put (rootName, new IIOMetadataNode (rootName)); + childPolicies.put (rootName, new Integer (IIOMetadataFormat.CHILD_POLICY_REPEAT)); + childRanges.put (rootName, new int [] { minChildren, maxChildren }); + this.rootName = rootName; + } + + protected void addAttribute (String elementName, + String attrName, + int dataType, + boolean required, + String defaultValue) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + node.setAttributeNode (new IIOMetadataNodeAttr (node, + attrName, + dataType, + required, + defaultValue)); + } + + protected void addAttribute (String elementName, + String attrName, + int dataType, + boolean required, + String defaultValue, + List enumeratedValues) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + node.setAttributeNode (new IIOMetadataNodeAttrEnumerated (node, + attrName, + dataType, + required, + defaultValue, + enumeratedValues)); + } + + protected void addAttribute (String elementName, + String attrName, + int dataType, + boolean required, + String defaultValue, + String minValue, + String maxValue, + boolean minInclusive, + boolean maxInclusive) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + node.setAttributeNode (new IIOMetadataNodeAttrBounded (node, + attrName, + dataType, + required, + defaultValue, + minValue, + maxValue, + minInclusive, + maxInclusive)); + } + + protected void addAttribute (String elementName, + String attrName, + int dataType, + boolean required, + int listMinLength, + int listMaxLength) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + node.setAttributeNode (new IIOMetadataNodeAttrList (node, + attrName, + dataType, + required, + listMinLength, + listMaxLength)); + } + + protected void addBooleanAttribute (String elementName, + String attrName, + boolean hasDefaultValue, + boolean defaultValue) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + + List enumeratedValues = new ArrayList(); + enumeratedValues.add ("TRUE"); + enumeratedValues.add ("FALSE"); + + node.setAttributeNode (new IIOMetadataNodeAttrEnumerated (node, + attrName, + IIOMetadataFormat.DATATYPE_BOOLEAN, + hasDefaultValue, + defaultValue ? "TRUE" : "FALSE", + enumeratedValues)); + } + + protected void addChildElement (String elementName, String parentName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (parentName); + + node.appendChild (new IIOMetadataNode (elementName)); + childPolicies.put (elementName, new Integer (IIOMetadataFormat.CHILD_POLICY_REPEAT)); + } + + protected void addElement (String elementName, String parentName, int childPolicy) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (parentName); + + node.appendChild (new IIOMetadataNode (elementName)); + childPolicies.put (elementName, new Integer (childPolicy)); + } + + protected void addElement (String elementName, String parentName, + int minChildren, int maxChildren) + { + addChildElement (elementName, parentName); + childRanges.put (elementName, new int [] { minChildren, maxChildren }); + } + + private void addNodeObject (IIOMetadataNode node, NodeObject o) + { + node.setUserObject (o); + } + + private NodeObject getNodeObject (IIOMetadataNode node) + { + return (NodeObject) node.getUserObject (); + } + + private void removeNodeObject (IIOMetadataNode node) + { + node.setUserObject (null); + } + + protected void addObjectValue (String elementName, Class classType, + boolean required, Object defaultValue) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + addNodeObject (node, new NodeObject (node, + classType, + required, + defaultValue)); + } + + protected void addObjectValue (String elementName, Class classType, + boolean required, Object defaultValue, + List enumeratedValues) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + addNodeObject (node, new NodeObjectEnumerated (node, + classType, + required, + defaultValue, + enumeratedValues)); + } + + protected void addObjectValue (String elementName, Class classType, + Object defaultValue, + Comparable minValue, + Comparable maxValue, + boolean minInclusive, + boolean maxInclusive) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + addNodeObject (node, new NodeObjectBounded (node, + classType, + defaultValue, + minValue, + maxValue, + minInclusive, + maxInclusive)); + } + + protected void addObjectValue (String elementName, Class classType, + int arrayMinLength, int arrayMaxLength) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + addNodeObject (node, new NodeObjectArray (node, + classType, + arrayMinLength, + arrayMaxLength)); + } + + public String getRootName () + { + return rootName; + } + + protected String getResourceBaseName () + { + return resourceBaseName; + } + + public static IIOMetadataFormat getStandardFormatInstance () + { + // FIXME: populate this with the standard metadata format + return new IIOMetadataFormatImpl (standardMetadataFormatName, + IIOMetadataFormat.CHILD_POLICY_ALL) + { + public boolean canNodeAppear (String elementName, + ImageTypeSpecifier specifier) + { + return true; + } + }; + } + + public abstract boolean canNodeAppear (String elementName, + ImageTypeSpecifier specifier); + + protected void removeAttribute (String elementName, + String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + node.removeAttribute (attrName); + } + + protected void removeElement (String elementName) + { + nodes.remove (elementName); + } + + protected void removeObjectValue (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + removeNodeObject (node); + } + + protected void setResourceBaseName (String resourceBaseName) + { + this.resourceBaseName = resourceBaseName; + } + + public int getAttributeDataType (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttr attr = (IIOMetadataNodeAttr) node.getAttributeNode (attrName); + return attr.getDataType (); + } + + public String getAttributeDefaultValue (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttr attr = (IIOMetadataNodeAttr) node.getAttributeNode (attrName); + return attr.getValue(); + } + + public String getAttributeDescription (String elementName, String attrName, Locale locale) + { + return getDescription (elementName + "/" + attrName, locale); + } + + public String[] getAttributeEnumerations (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttrEnumerated attr = + (IIOMetadataNodeAttrEnumerated) node.getAttributeNode (attrName); + + Object[] attrEnums = attr.getEnumerations(); + + String[] attrNames = new String[attrEnums.length]; + + for (int i = 0; i < attrEnums.length; i++) + { + attrNames[i] = (String) attrEnums[i]; + } + + return attrNames; + } + + public int getAttributeListMaxLength (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttrList attr = + (IIOMetadataNodeAttrList) node.getAttributeNode (attrName); + return attr.getListMaxLength(); + } + + public int getAttributeListMinLength (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttrList attr = + (IIOMetadataNodeAttrList) node.getAttributeNode (attrName); + return attr.getListMinLength(); + } + + public String getAttributeMaxValue (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttrBounded attr = + (IIOMetadataNodeAttrBounded) node.getAttributeNode (attrName); + return attr.getMaxValue(); + } + + public String getAttributeMinValue (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttrBounded attr = + (IIOMetadataNodeAttrBounded) node.getAttributeNode (attrName); + return attr.getMinValue(); + } + + public String[] getAttributeNames (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + + NamedNodeMap attrNodes = node.getAttributes(); + + String[] attrNames = new String[attrNodes.getLength()]; + + for (int i = 0; i < attrNodes.getLength(); i++) + { + attrNames[i] = attrNodes.item (i).getLocalName(); + } + + return attrNames; + } + + public int getAttributeValueType (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + IIOMetadataNodeAttr attr = (IIOMetadataNodeAttr) node.getAttributeNode (attrName); + return attr.getDataType(); + } + + public String[] getChildNames (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + + NodeList childNodes = node.getChildNodes(); + + String[] childNames = new String[childNodes.getLength()]; + + for (int i = 0; i < childNodes.getLength(); i++) + { + childNames[i] = childNodes.item (i).getLocalName(); + } + + return childNames; + } + + public int getChildPolicy (String elementName) + { + return ((Integer) childPolicies.get (elementName)).intValue(); + } + + private String getDescription (String resourceName, Locale locale) + { + if (resourceBaseName == null) + return null; + + Locale l = locale; + + if (l == null) + l = Locale.getDefault(); + + ResourceBundle bundle = ResourceBundle.getBundle (resourceBaseName, locale); + + String desc = null; + + if (bundle == null) + { + try + { + desc = bundle.getString (resourceName); + } + catch (MissingResourceException e) + { + desc = null; + } + } + + return desc; + } + + public String getElementDescription (String elementName, Locale locale) + { + return getDescription (elementName, locale); + } + + public int getElementMaxChildren (String elementName) + { + return ((int[]) childRanges.get (elementName))[1]; + } + + public int getElementMinChildren (String elementName) + { + return ((int[]) childRanges.get (elementName))[0]; + } + + public int getObjectArrayMaxLength (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((Integer) ((NodeObjectArray) getNodeObject (node)).getArrayMaxLength ()).intValue(); + } + + public int getObjectArrayMinLength (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((Integer) ((NodeObjectArray) getNodeObject (node)).getArrayMinLength ()).intValue(); + } + + public Class getObjectClass (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return getNodeObject (node).getClassType (); + } + + public Object getObjectDefaultValue (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return getNodeObject (node).getDefaultValue (); + } + + public Object[] getObjectEnumerations (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((NodeObjectEnumerated) getNodeObject (node)).getEnumerations (); + } + + public Comparable getObjectMaxValue (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((NodeObjectBounded) getNodeObject (node)).getMaxValue (); + } + + public Comparable getObjectMinValue (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((NodeObjectBounded) getNodeObject (node)).getMinValue (); + } + + public int getObjectValueType (String elementName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + NodeObject n = getNodeObject (node); + + if (n == null) + return IIOMetadataFormat.VALUE_NONE; + else + return n.getValueType (); + } + + public boolean isAttributeRequired (String elementName, String attrName) + { + IIOMetadataNode node = (IIOMetadataNode) nodes.get (elementName); + return ((IIOMetadataNodeAttr) node.getAttributeNode (attrName)).isRequired(); + } } diff --git a/libjava/classpath/javax/imageio/metadata/IIOMetadataNode.java b/libjava/classpath/javax/imageio/metadata/IIOMetadataNode.java index d9e0983e94a..2d52e467078 100644 --- a/libjava/classpath/javax/imageio/metadata/IIOMetadataNode.java +++ b/libjava/classpath/javax/imageio/metadata/IIOMetadataNode.java @@ -52,6 +52,7 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.TypeInfo; import org.w3c.dom.UserDataHandler; +import javax.imageio.metadata.IIOMetadataFormatImpl.IIOMetadataNodeAttr; public class IIOMetadataNode implements Element, NodeList @@ -61,7 +62,127 @@ public class IIOMetadataNode private List children = new ArrayList(); private IIOMetadataNode parent; private Object obj; + + /** + * Simple NamedNodeMap class for IIOMetadataNode. + * + * @author jlquinn + */ + private class IIONamedNodeMap implements NamedNodeMap + { + HashMap attrs; + + /** + * @param attrs + * @param node + */ + public IIONamedNodeMap(HashMap attrs) + { + this.attrs = attrs; + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#getNamedItem(java.lang.String) + */ + public Node getNamedItem(String name) + { + return (Node)attrs.get(name); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#setNamedItem(org.w3c.dom.Node) + */ + public Node setNamedItem(Node arg) throws DOMException + { + if (arg instanceof IIOMetadataNodeAttr) + { + IIOMetadataNodeAttr attr = (IIOMetadataNodeAttr) arg; + // The only code that can successfully do this is in this package. + if (attr.owner != null) + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, ""); + return (Node)attrs.put(attr.name, attr); + } + // Anything else gets treated as an invalid op. + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, ""); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#removeNamedItem(java.lang.String) + */ + public Node removeNamedItem(String name) throws DOMException + { + return (Node)attrs.remove(name); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#item(int) + */ + public Node item(int index) + { + return (Node)attrs.values().toArray()[index]; + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#getLength() + */ + public int getLength() + { + return attrs.size(); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#getNamedItemNS(java.lang.String, java.lang.String) + */ + public Node getNamedItemNS(String namespaceURI, String localName) + { + return getNamedItem(localName); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#setNamedItemNS(org.w3c.dom.Node) + */ + public Node setNamedItemNS(Node arg) throws DOMException + { + return setNamedItem(arg); + } + + /* (non-Javadoc) + * @see org.w3c.dom.NamedNodeMap#removeNamedItemNS(java.lang.String, java.lang.String) + */ + public Node removeNamedItemNS(String namespaceURI, String localName) + throws DOMException + { + return removeNamedItem(localName); + } + } + + /** + * Simple NodeList implementation for IIOMetadataNode. + * + * @author jlquinn + * + */ + private class IIONodeList implements NodeList + { + List children = new ArrayList(); + /* (non-Javadoc) + * @see org.w3c.dom.NodeList#item(int) + */ + public Node item(int index) + { + return (index < children.size()) ? (Node)children.get(index) : null; + } + + /* (non-Javadoc) + * @see org.w3c.dom.NodeList#getLength() + */ + public int getLength() + { + return children.size(); + } + } + public IIOMetadataNode() { // Do nothing here. @@ -71,12 +192,12 @@ public class IIOMetadataNode { name = nodename; } - + public Object getUserObject() { return obj; } - + public void setUserObject(Object o) { obj = o; @@ -85,7 +206,7 @@ public class IIOMetadataNode public short compareDocumentPosition(Node other) throws DOMException { - throw new Error("not implemented"); + return Element.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; } /* (non-Javadoc) @@ -104,7 +225,7 @@ public class IIOMetadataNode { String val = getAttribute(name); if (val != null) - return new IIOAttr(name, val, this); + return new IIOMetadataNodeAttr(this, name, val); return null; } @@ -126,7 +247,7 @@ public class IIOMetadataNode public String getBaseURI() { - throw new Error("not implemented"); + return null; } // Recursive function for assembling a node list. @@ -217,7 +338,7 @@ public class IIOMetadataNode if (attr != null) attr.setValue(value); else - attrs.put(name, new IIOAttr(name, value, this)); + attrs.put(name, new IIOMetadataNodeAttr(this, name, value)); } /* (non-Javadoc) @@ -295,7 +416,7 @@ public class IIOMetadataNode // clone attrs for (Iterator it = attrs.values().iterator(); it.hasNext();) { - IIOAttr attr = (IIOAttr)it.next(); + IIOMetadataNodeAttr attr = (IIOMetadataNodeAttr)it.next(); newnode.attrs.put(attr.name, attr.cloneNode(deep)); attr.owner = newnode; } @@ -321,7 +442,7 @@ public class IIOMetadataNode public Object getFeature(String feature, String version) { - throw new Error("not implemented"); + return null; } /* (non-Javadoc) @@ -432,18 +553,18 @@ public class IIOMetadataNode public TypeInfo getSchemaTypeInfo() { - throw new Error("not implemented"); + return null; } public String getTextContent() throws DOMException { - throw new Error("not implemented"); + return null; } public Object getUserData(String key) { - throw new Error("not implemented"); + return null; } /* (non-Javadoc) @@ -482,12 +603,12 @@ public class IIOMetadataNode public boolean isDefaultNamespace(String namespaceURI) { - throw new Error("not implemented"); + return true; } public boolean isEqualNode(Node arg) { - throw new Error("not implemented"); + return true; } public boolean isSameNode(Node other) @@ -506,12 +627,12 @@ public class IIOMetadataNode public String lookupNamespaceURI(String prefix) { - throw new Error("not implemented"); + return null; } public String lookupPrefix(String namespaceURI) { - throw new Error("not implemented"); + return null; } /* (non-Javadoc) @@ -550,19 +671,16 @@ public class IIOMetadataNode public void setIdAttribute(String name, boolean isId) throws DOMException { - throw new Error("not implemented"); } public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { - throw new Error("not implemented"); } public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { - throw new Error("not implemented"); } /* (non-Javadoc) @@ -582,11 +700,10 @@ public class IIOMetadataNode public void setTextContent(String textContent) throws DOMException { - throw new Error("not implemented"); } public Object setUserData(String key, Object data, UserDataHandler handler) { - throw new Error("not implemented"); + return null; } } diff --git a/libjava/classpath/javax/imageio/metadata/IIONamedNodeMap.java b/libjava/classpath/javax/imageio/metadata/IIONamedNodeMap.java deleted file mode 100644 index 92da28d5bb2..00000000000 --- a/libjava/classpath/javax/imageio/metadata/IIONamedNodeMap.java +++ /dev/null @@ -1,138 +0,0 @@ -/* IIONamedNodeMap.java -- - Copyright (C) 2004 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 javax.imageio.metadata; - -import java.util.HashMap; - -import org.w3c.dom.DOMException; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -/** - * Simple NamedNodeMap class for IIOMetadataNode. - * - * @author jlquinn - */ -class IIONamedNodeMap implements NamedNodeMap -{ - HashMap attrs; - - /** - * @param attrs - * @param node - */ - public IIONamedNodeMap(HashMap attrs) - { - this.attrs = attrs; - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#getNamedItem(java.lang.String) - */ - public Node getNamedItem(String name) - { - return (Node)attrs.get(name); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#setNamedItem(org.w3c.dom.Node) - */ - public Node setNamedItem(Node arg) throws DOMException - { - if (arg instanceof IIOAttr) - { - IIOAttr attr = (IIOAttr) arg; - // The only code that can successfully do this is in this package. - if (attr.owner != null) - throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, ""); - return (Node)attrs.put(attr.name, attr); - } - // Anything else gets treated as an invalid op. - throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, ""); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#removeNamedItem(java.lang.String) - */ - public Node removeNamedItem(String name) throws DOMException - { - return (Node)attrs.remove(name); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#item(int) - */ - public Node item(int index) - { - return (Node)attrs.values().toArray()[index]; - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#getLength() - */ - public int getLength() - { - return attrs.size(); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#getNamedItemNS(java.lang.String, java.lang.String) - */ - public Node getNamedItemNS(String namespaceURI, String localName) - { - return getNamedItem(localName); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#setNamedItemNS(org.w3c.dom.Node) - */ - public Node setNamedItemNS(Node arg) throws DOMException - { - return setNamedItem(arg); - } - - /* (non-Javadoc) - * @see org.w3c.dom.NamedNodeMap#removeNamedItemNS(java.lang.String, java.lang.String) - */ - public Node removeNamedItemNS(String namespaceURI, String localName) - throws DOMException - { - return removeNamedItem(localName); - } - -} diff --git a/libjava/classpath/javax/imageio/package.html b/libjava/classpath/javax/imageio/package.html index ce36a7b44bd..f6a604de8fc 100644 --- a/libjava/classpath/javax/imageio/package.html +++ b/libjava/classpath/javax/imageio/package.html @@ -40,7 +40,48 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.imageio</title></head> <body> -<p></p> - +<p> +This package provides image input/output APIs. +</p> +<p> +The standard class library provides other ways of loading images (@see +java.awt.Toolkit, @see java.awt.Component)) but the ImageIO package is +more powerful. +</p> +<p> +The static ImageIO class supports reading and writing images in many +different formats along with most other basic image I/O operations. +</p> +<p> +Other classes provide finer control of image-related operations; +reading is controlled by ImageReader, ImageReadParam and +ImageTypeSpecifyer, writing by ImageWriter and ImageWriteParam. +ImageTranscoder allows fine-grained control over how images are +converted between formats and IIOException reports errors. IIOImage +describes an image file in detail including metadata and thumbnails. +</p> +<h2>Supported Formats</h2> +<p> +The default GNU Classpath ImageIO backend uses ImageMagick and so +supports the following formats: +<table> +<tr> +<th></th> <th>Read</th> <th>Write</th> +</tr> +<tr><td>JPEG</td><td>yes</td><td>yes</td></tr> +<tr><td>PNG</td><td>yes</td><td>yes</td></tr> +<tr><td>BMP</td><td>yes</td><td>yes</td></tr> +<tr><td>WBMP</td><td>yes</td><td>yes</td></tr> +<tr><td>GIF</td><td>yes</td><td>yes</td></tr> +<tr><td>TIFF</td><td>yes</td><td>yes</td></tr> +<tr><td>XPM</td><td>yes</td><td>yes</td></tr> +<tr><td>TGA</td><td>yes</td><td>yes</td></tr> +<tr><td>PDF</td><td>yes</td><td>no</td></tr> +<tr><td>SVG</td><td>yes</td><td>no</td></tr> +<table> +</p> +<p> +@since 1.4 +</p> </body> </html> diff --git a/libjava/classpath/javax/imageio/spi/ImageReaderWriterSpi.java b/libjava/classpath/javax/imageio/spi/ImageReaderWriterSpi.java index 4aa7fd41272..40d44e3d0e2 100644 --- a/libjava/classpath/javax/imageio/spi/ImageReaderWriterSpi.java +++ b/libjava/classpath/javax/imageio/spi/ImageReaderWriterSpi.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.imageio.spi; +import javax.imageio.metadata.IIOMetadataFormat; +import javax.imageio.metadata.IIOMetadataFormatImpl; /** * An abstract superclass that contains the common parts of {@link @@ -422,4 +424,88 @@ public abstract class ImageReaderWriterSpi { return extraImageMetadataFormatNames; } + + /** + * Returns an IIOMetadataFormat object that represents the requested + * stream metadata format or null if the given format is supported + * but no IIOMetadataFormat can be created for it. + * + * @param formatName the requested stream metadata format name + * + * @return an IIOMetadataFormat object or null + * + * @throws IllegalArgumentException if formatName is null or is not + * one of the standard metadata format or this provider's native or + * extra stream metadata formats + */ + public IIOMetadataFormat getStreamMetadataFormat (String formatName) + { + if (formatName == null) + throw new IllegalArgumentException ("null stream metadata format name"); + + if (!formatName.equals (getNativeStreamMetadataFormatName()) + && !formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) + { + String[] extraNames = getExtraStreamMetadataFormatNames (); + boolean foundName = false; + for (int i = 0; i < extraNames.length; i++) + { + if (formatName.equals(extraNames[i])) + { + foundName = true; + break; + } + } + if (!foundName) + throw new IllegalArgumentException ("unsupported stream metadata format name"); + } + + if (formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) + return IIOMetadataFormatImpl.getStandardFormatInstance (); + else + // Default implementation returns null. + return null; + } + + /** + * Returns an IIOMetadataFormat object that represents the requested + * image metadata format or null if the given format is supported + * but no IIOMetadataFormat can be created for it. + * + * @param formatName the requested image metadata format name + * + * @return an IIOMetadataFormat object or null + * + * @throws IllegalArgumentException if formatName is null or is not + * one of the standard metadata format or this provider's native or + * extra image metadata formats + */ + public IIOMetadataFormat getImageMetadataFormat (String formatName) + { + if (formatName == null) + throw new IllegalArgumentException ("null image metadata format name"); + + if (!formatName.equals (getNativeImageMetadataFormatName()) + && !formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) + { + String[] extraNames = getExtraImageMetadataFormatNames (); + boolean foundName = false; + for (int i = 0; i < extraNames.length; i++) + { + if (formatName.equals(extraNames[i])) + { + foundName = true; + break; + } + } + if (!foundName) + throw new IllegalArgumentException ("unsupported image metadata format name"); + } + + if (formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) + return IIOMetadataFormatImpl.getStandardFormatInstance (); + else + // Default implementation returns null. + return null; + } } diff --git a/libjava/classpath/javax/naming/CompoundName.java b/libjava/classpath/javax/naming/CompoundName.java index 4b30557f8d3..b23736fa723 100644 --- a/libjava/classpath/javax/naming/CompoundName.java +++ b/libjava/classpath/javax/naming/CompoundName.java @@ -38,6 +38,9 @@ exception statement from your version. */ package javax.naming; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.NoSuchElementException; @@ -48,9 +51,6 @@ import java.util.Vector; * @author Tom Tromey (tromey@redhat.com) * @date May 16, 2001 * - * FIXME: must write readObject and writeObject to conform to - * serialization spec. - * * FIXME: this class is underspecified. For instance, the `flat' * direction is never described. If it means that the CompoundName * can only have a single element, then the Enumeration-based @@ -469,6 +469,25 @@ public class CompoundName implements Name, Cloneable, Serializable "false")).booleanValue (); } + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + mySyntax = (Properties) s.readObject(); + int count = s.readInt(); + elts = new Vector(count); + for (int i = 0; i < count; i++) + elts.addElement((String) s.readObject()); + } + + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.writeObject(mySyntax); + s.writeInt(elts.size()); + for (int i = 0; i < elts.size(); i++) + s.writeObject(elts.elementAt(i)); + } + // The spec specifies this but does not document it in any way (it // is a package-private class). It is useless as far as I can tell. // So we ignore it. diff --git a/libjava/classpath/javax/naming/Name.java b/libjava/classpath/javax/naming/Name.java index f8592d9ea3d..f0475766a97 100644 --- a/libjava/classpath/javax/naming/Name.java +++ b/libjava/classpath/javax/naming/Name.java @@ -1,5 +1,5 @@ /* Name.java -- Name build up from different components - Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 2000, 2001, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -60,7 +60,7 @@ import java.util.Enumeration; * @author Anthony Green (green@redhat.com) * @author Mark Wielaard (mark@klomp.org) */ -public interface Name extends Cloneable, Serializable +public interface Name extends Cloneable, Serializable, Comparable { long serialVersionUID = -3617482732056931635L; diff --git a/libjava/classpath/javax/print/DocFlavor.java b/libjava/classpath/javax/print/DocFlavor.java index 732823fbeb2..1e96a70c024 100644 --- a/libjava/classpath/javax/print/DocFlavor.java +++ b/libjava/classpath/javax/print/DocFlavor.java @@ -54,6 +54,8 @@ public class DocFlavor implements Cloneable, Serializable public static class BYTE_ARRAY extends DocFlavor { + private static final long serialVersionUID = -9065578006593857475L; + public static final BYTE_ARRAY AUTOSENSE = new BYTE_ARRAY("application/octet-stream"); public static final BYTE_ARRAY GIF = new BYTE_ARRAY("image/gif"); public static final BYTE_ARRAY JPEG = new BYTE_ARRAY("image/jpeg"); @@ -103,6 +105,8 @@ public class DocFlavor implements Cloneable, Serializable public static class INPUT_STREAM extends DocFlavor { + private static final long serialVersionUID = -7045842700749194127L; + public static final INPUT_STREAM AUTOSENSE = new INPUT_STREAM("application/octet-stream"); public static final INPUT_STREAM GIF = new INPUT_STREAM("image/gif"); public static final INPUT_STREAM JPEG = new INPUT_STREAM("image/jpeg"); diff --git a/libjava/classpath/javax/print/attribute/Attribute.java b/libjava/classpath/javax/print/attribute/Attribute.java index fcaa7d84cfe..7ce0247cea1 100644 --- a/libjava/classpath/javax/print/attribute/Attribute.java +++ b/libjava/classpath/javax/print/attribute/Attribute.java @@ -1,5 +1,5 @@ /* Attribute.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,11 +40,27 @@ package javax.print.attribute; import java.io.Serializable; /** - * @author Michael Koch + * Base interface of every printing attribute of the Java Print Service API. + * + * @author Michael Koch (konqueror@gmx.de) */ public interface Attribute extends Serializable { + /** + * Returns the category of the printing attribute which is the specific + * attribute class implementing this interface. + * + * @return The concrete {@link Class} instance of the attribute class. + */ Class getCategory (); + /** + * Returns the descriptive name of the attribute category. + * + * Implementations of the <code>Attribute</code> interfaces providing equal + * category values have to return equal name values. + * + * @return The name of the attribute category. + */ String getName (); } diff --git a/libjava/classpath/javax/print/attribute/AttributeSet.java b/libjava/classpath/javax/print/attribute/AttributeSet.java index cdc7a8e4876..b4bdecad254 100644 --- a/libjava/classpath/javax/print/attribute/AttributeSet.java +++ b/libjava/classpath/javax/print/attribute/AttributeSet.java @@ -1,5 +1,5 @@ /* AttributeSet.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,40 +38,159 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * <code>AttributeSet</code> is the top-level interface for sets of printing + * attributes in the Java Print Service API. + * <p> + * There are no duplicate values allowed in an attribute set and there is + * at most one attribute object contained per category type. Based on the + * {@link java.util.Map} interface the values of attribute sets are objects + * of type {@link javax.print.attribute.Attribute} and the entries are the + * categories as {@link java.lang.Class} instances. + * </p> + * <p> + * The following specialized types of <code>AttributeSet</code> are available: + * <ul> + * <li>{@link javax.print.attribute.DocAttributeSet}</li> + * <li>{@link javax.print.attribute.PrintRequestAttributeSet}</li> + * <li>{@link javax.print.attribute.PrintJobAttributeSet}</li> + * <li>{@link javax.print.attribute.PrintServiceAttributeSet}</li> + * </ul> + * </p> + * <p> + * Attribute sets may be unmodifiable depending on the context of usage. If + * used as read-only attribute set modifying operations throw an + * {@link javax.print.attribute.UnmodifiableSetException}. + * </p> + * <p> + * The Java Print Service API provides implementation classes for the existing + * attribute set interfaces but applications may use their own implementations. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface AttributeSet { /** - * Adds the specified attribute value to this attribute set + * Adds the specified attribute value to this attribute set * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute to the set. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ boolean add (Attribute attribute); /** - * Adds all of the elements in the specified set to this attribute. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ boolean addAll (AttributeSet attributes); - + + /** + * Removes all attributes from this attribute set. + * + * @throws UnmodifiableSetException if the set does not support modification. + */ void clear (); - + + /** + * Checks if this attributes set contains an attribute with the given + * category. + * + * @param category the category to test for. + * @return <code>true</code> if an attribute of the category is contained + * in the set, <code>false</code> otherwise. + */ boolean containsKey (Class category); - + + /** + * Checks if this attribute set contains the given attribute. + * + * @param attribute the attribute to test for. + * @return <code>true</code> if the attribute is contained in the set, + * <code>false</code> otherwise. + */ boolean containsValue (Attribute attribute); + /** + * Tests this set for equality with the given object. <code>true</code> is + * returned, if the given object is also of type <code>AttributeSet</code> + * and the contained attributes are the same as in this set. + * + * @param obj the Object to test. + * @return <code>true</code> if equal, false otherwise. + */ boolean equals (Object obj); - - Attribute get (Class Category); - + + /** + * Returns the attribute object contained in this set for the given attribute + * category. + * + * @param category the category of the attribute. A <code>Class</code> + * instance of a class implementing the <code>Attribute</code> interface. + * @return The attribute for this category or <code>null</code> if no + * attribute is contained for the given category. + * @throws NullPointerException if category is null. + * @throws ClassCastException if category is not implementing + * <code>Attribute</code>. + */ + Attribute get (Class category); + + /** + * Returns the hashcode value. The hashcode value is the sum of all hashcodes + * of the attributes contained in this set. + * + * @return The hashcode for this attribute set. + */ int hashCode (); - + + /** + * Checks if the attribute set is empty. + * + * @return <code>true</code> if the attribute set is empty, false otherwise. + */ boolean isEmpty (); + /** + * Removes the given attribute from the set. If the given attribute is <code>null</code> + * nothing is done and <code>false</code> is returned. + * + * @param attribute the attribute to remove. + * @return <code>true</code> if removed, false in all other cases. + * @throws UnmodifiableSetException if the set does not support modification. + */ boolean remove (Attribute attribute); - + + /** + * Removes the attribute entry of the given category from the set. If the given + * category is <code>null</code> nothing is done and <code>false</code> is returned. + * + * @param category the category of the entry to be removed. + * @return <code>true</code> if an attribute is removed, false in all other cases. + * @throws UnmodifiableSetException if the set does not support modification. + */ boolean remove (Class category); - + + /** + * Returns the number of elements in this attribute set. + * + * @return The number of elements. + */ int size (); - + + /** + * Returns the content of the attribute set as an array + * + * @return An array of attributes. + */ Attribute[] toArray (); } diff --git a/libjava/classpath/javax/print/attribute/AttributeSetUtilities.java b/libjava/classpath/javax/print/attribute/AttributeSetUtilities.java index 6f0ffc10d7f..5d97c66f21a 100644 --- a/libjava/classpath/javax/print/attribute/AttributeSetUtilities.java +++ b/libjava/classpath/javax/print/attribute/AttributeSetUtilities.java @@ -39,12 +39,41 @@ package javax.print.attribute; import java.io.Serializable; +/** + * <code>AttributeSetUtilities</code> provides static methods for working + * with <code>AttributeSet</code>s. + * <p> + * For every type of an attribute set available in the Java Print Service API + * are methods provided to get an unmodifiable view of an attribute set. + * This unmodifiable view provides a read-only version of the attribute + * set which throws {@link javax.print.attribute.UnmodifiableSetException}s + * if state changing methods are invoked. + * </p> + * <p> + * Methods for getting a synchronized view of an attribute set are also + * available. This view provides synchronized (thread safe) access to the + * underlying wrapped attribute set. + * </P> + * <p> + * Three static methods for the implementation of own AttributeSets + * are provided, which verify that: + * <ul> + * <li>the given object is an attribute of the given interface.</li> + * <li>the category of given attribute is equals to a given category.</li> + * <li>the given object is a <code>Class</code> that implements the given + * interface name.</li> + * </ul> + * + */ public final class AttributeSetUtilities { /** * This class isn't intended to be instantiated. */ - private AttributeSetUtilities() {} + private AttributeSetUtilities() + { + // only static methods + } private static class UnmodifiableAttributeSet implements AttributeSet, Serializable @@ -287,7 +316,8 @@ public final class AttributeSetUtilities /** * Returns a synchronized view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to synchronize. + * @return The sychronized attribute set. */ public static AttributeSet synchronizedView(AttributeSet attributeSet) { @@ -297,7 +327,8 @@ public final class AttributeSetUtilities /** * Returns a synchronized view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to synchronize. + * @return The sychronized attribute set. */ public static DocAttributeSet synchronizedView(DocAttributeSet attributeSet) { @@ -307,7 +338,8 @@ public final class AttributeSetUtilities /** * Returns a synchronized view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to synchronize. + * @return The sychronized attribute set. */ public static PrintJobAttributeSet synchronizedView(PrintJobAttributeSet attributeSet) { @@ -317,7 +349,8 @@ public final class AttributeSetUtilities /** * Returns a synchronized view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to synchronize. + * @return The sychronized attribute set. */ public static PrintRequestAttributeSet synchronizedView(PrintRequestAttributeSet attributeSet) { @@ -327,7 +360,8 @@ public final class AttributeSetUtilities /** * Returns a synchronized view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to synchronize. + * @return The sychronized attribute set. */ public static PrintServiceAttributeSet synchronizedView(PrintServiceAttributeSet attributeSet) { @@ -337,7 +371,8 @@ public final class AttributeSetUtilities /** * Returns an unmodifiable view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to make unmodifiable. + * @return The unmodifiable attribute set. */ public static AttributeSet unmodifiableView(AttributeSet attributeSet) { @@ -347,7 +382,8 @@ public final class AttributeSetUtilities /** * Returns an unmodifiable view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to make unmodifiable. + * @return The unmodifiable attribute set. */ public static DocAttributeSet unmodifiableView(DocAttributeSet attributeSet) { @@ -357,7 +393,8 @@ public final class AttributeSetUtilities /** * Returns an unmodifiable view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to make unmodifiable. + * @return The unmodifiable attribute set. */ public static PrintJobAttributeSet unmodifiableView(PrintJobAttributeSet attributeSet) { @@ -367,7 +404,8 @@ public final class AttributeSetUtilities /** * Returns an unmodifiable view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to make unmodifiable. + * @return The unmodifiable attribute set. */ public static PrintRequestAttributeSet unmodifiableView(PrintRequestAttributeSet attributeSet) { @@ -377,7 +415,8 @@ public final class AttributeSetUtilities /** * Returns an unmodifiable view of the given attribute set. * - * @return the sychronized attribute set + * @param attributeSet the set to make unmodifiable. + * @return The unmodifiable attribute set. */ public static PrintServiceAttributeSet unmodifiableView(PrintServiceAttributeSet attributeSet) { @@ -386,8 +425,10 @@ public final class AttributeSetUtilities /** * Verifies that the given object is a <code>Class</code> that - * implements the given interface name. - * + * implements the given interface name and returns it casted. + * + * @param object the object to test. + * @param interfaceName the <code>Class</code> to verify against. * @return object casted to <code>Class</code> * * @exception ClassCastException if object is not a <code>Class</code> @@ -410,7 +451,10 @@ public final class AttributeSetUtilities /** * Verifies that the given object is an attribute of the given interface. - * + * and returns it casted to the interface type. + * + * @param object the object to test. + * @param interfaceName the <code>Class</code> to verify against. * @return the object casted to <code>Attribute</code> * * @exception ClassCastException if object is no instance of interfaceName. @@ -429,10 +473,11 @@ public final class AttributeSetUtilities } /** - * Verifies that the category of attribute is equals to category. - * - * @param category the category the atteribute should be - * @param attribute the attribute to verify + * Verifies that the category of attribute is equals to the given category + * class. + * + * @param category the category to test. + * @param attribute the attribute to verify. * * @exception IllegalArgumentException if the categories are not equal * @exception NullPointerException if category is null @@ -440,10 +485,10 @@ public final class AttributeSetUtilities public static void verifyCategoryForValue(Class category, Attribute attribute) { - if (category == null) - throw new NullPointerException("object may not be null"); + if (category == null || attribute == null) + throw new NullPointerException("category or attribute may not be null"); - if (category.equals(attribute.getCategory())) + if (!category.equals(attribute.getCategory())) throw new IllegalArgumentException ("category of attribute not equal to category"); } diff --git a/libjava/classpath/javax/print/attribute/DateTimeSyntax.java b/libjava/classpath/javax/print/attribute/DateTimeSyntax.java index 0e583e0b990..d59193265e2 100644 --- a/libjava/classpath/javax/print/attribute/DateTimeSyntax.java +++ b/libjava/classpath/javax/print/attribute/DateTimeSyntax.java @@ -1,5 +1,5 @@ /* DateTimeSyntax.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,7 +41,10 @@ import java.io.Serializable; import java.util.Date; /** - * @author Michael Koch + * <code>DateTimeSyntax</code> is the abstract base class of all attribute + * classes having a date and a time as value. + * + * @author Michael Koch (konqueror@gmx.de) */ public abstract class DateTimeSyntax implements Cloneable, Serializable { @@ -52,7 +55,7 @@ public abstract class DateTimeSyntax implements Cloneable, Serializable /** * Creates a <code>DateTimeSyntax</code> with a given value. * - * @param value the value for this syntax + * @param value the date for this syntax * * @exception NullPointerException if value is null */ @@ -67,7 +70,7 @@ public abstract class DateTimeSyntax implements Cloneable, Serializable /** * Returns the date value of this object. * - * @return the date value + * @return The date value. */ public Date getValue() { @@ -79,7 +82,8 @@ public abstract class DateTimeSyntax implements Cloneable, Serializable * * @param obj the object to test * - * @return True if both objects are equal, false otherwise. + * @return <code>true</code> if both objects are equal, + * <code>false</code> otherwise. */ public boolean equals(Object obj) { @@ -92,7 +96,7 @@ public abstract class DateTimeSyntax implements Cloneable, Serializable /** * Returns the hashcode for this object. * - * @return the hashcode + * @return The hashcode. */ public int hashCode() { diff --git a/libjava/classpath/javax/print/attribute/DocAttribute.java b/libjava/classpath/javax/print/attribute/DocAttribute.java index 669d7d98251..9af3a7052af 100644 --- a/libjava/classpath/javax/print/attribute/DocAttribute.java +++ b/libjava/classpath/javax/print/attribute/DocAttribute.java @@ -1,5 +1,5 @@ /* DocAttribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,23 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * Marker interface for all attribute classes describing attributes of + * a {@link javax.print.Doc} object. + * <p> + * Instances of implementing attribute classes may be collected in a + * {@link javax.print.attribute.DocAttributeSet}. + * </p><p> + * Attributes attached to a {@link javax.print.Doc} instance specify how the + * data should be printed. + * For example {@link javax.print.attribute.standard.Chromaticity} can be + * used to specify that a doc should be printed in color or monochrome. + * </p> + * + * @see javax.print.attribute.DocAttributeSet + * + * @author Michael Koch (konqueror@gmx.de) */ public interface DocAttribute extends Attribute { + // Marker interface } diff --git a/libjava/classpath/javax/print/attribute/DocAttributeSet.java b/libjava/classpath/javax/print/attribute/DocAttributeSet.java index 72cd6d88a00..d8d09eb4857 100644 --- a/libjava/classpath/javax/print/attribute/DocAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/DocAttributeSet.java @@ -1,5 +1,5 @@ /* DocAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,18 +38,45 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * <code>DocAttributeSet</code> specifies an attribute set which only + * allows printing attributes of type + * {@link javax.print.attribute.DocAttribute}. + * <p> + * The methods {@link #add(Attribute)} and {@link #addAll(AttributeSet)} are + * respecified in this interface to indicate that only + * <code>DocAttribute</code> instances are allowed in this set. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface DocAttributeSet extends AttributeSet { /** - * Adds the specified attribute value to this attribute set + * Adds the specified attribute value to this attribute set * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if attribute is not of type + * <code>DocAttribute</code>. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ boolean add (Attribute attribute); /** - * Adds all of the elements in the specified set to this attribute. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if one of the attributes is not of type + * <code>DocAttribute</code>. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ boolean addAll (AttributeSet attributes); } diff --git a/libjava/classpath/javax/print/attribute/EnumSyntax.java b/libjava/classpath/javax/print/attribute/EnumSyntax.java index 3ed79fc5f68..9a5e62d458f 100644 --- a/libjava/classpath/javax/print/attribute/EnumSyntax.java +++ b/libjava/classpath/javax/print/attribute/EnumSyntax.java @@ -1,5 +1,5 @@ /* EnumSyntax.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,10 +37,69 @@ exception statement from your version. */ package javax.print.attribute; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; import java.io.Serializable; /** - * @author Michael Koch + * <code>EnumSyntax</code> is the abstract base class of all enumeration + * classes in the Java Print Service API. + * <p> + * Every enumeration class which extends from EnumSyntax provides several + * enumeration objects as singletons of its class. + * </p> + * <p> + * Notes for implementing subclasses: + * <ul> + * <li> + * The values of all enumeration singelton instances have to be in a + * sequence which may start at any value. See: {@link #getOffset()} + * </li> + * <li> + * Subclasses have to override {@link #getEnumValueTable()} and should + * override {@link #getStringTable()} for correct serialization. + * </li> + * </ul> + * </p> + * Example: + * <pre> + * public class PrinterState extends EnumSyntax + * { + * public static final PrinterState IDLE = new PrinterState(1); + * public static final PrinterState PROCESSING = new PrinterState(2); + * public static final PrinterState STOPPED = new PrinterState(3); + * + * protected PrinterState(int value) + * { + * super(value); + * } + * + * // Overridden because values start not at zero ! + * protected int getOffset() + * { + * return 1; + * } + * + * private static final String[] stringTable = { "idle", "processing", + * "stopped" }; + * + * protected String[] getStringTable() + * { + * return stringTable; + * } + * + * private static final PrinterState[] enumValueTable = { IDLE, + * PROCESSING, STOPPED}; + * + * protected EnumSyntax[] getEnumValueTable() + * { + * return enumValueTable; + * } + * } + * </pre> + * + * @author Michael Koch (konqueror@gmx.de) + * @author Wolfgang Baer (WBaer@gmx.de) */ public abstract class EnumSyntax implements Cloneable, Serializable { @@ -51,7 +110,7 @@ public abstract class EnumSyntax implements Cloneable, Serializable /** * Creates a <code>EnumSyntax</code> object. * - * @param value the value to set + * @param value the value to set. */ protected EnumSyntax(int value) { @@ -59,9 +118,9 @@ public abstract class EnumSyntax implements Cloneable, Serializable } /** - * Returns the value of this object. + * Returns the value of this enumeration object. * - * @return the value + * @return The value. */ public int getValue() { @@ -71,7 +130,7 @@ public abstract class EnumSyntax implements Cloneable, Serializable /** * Clones this object. * - * @return a clone of this object + * @return A clone of this object. */ public Object clone() { @@ -87,9 +146,10 @@ public abstract class EnumSyntax implements Cloneable, Serializable } /** - * Returns the hashcode for this object. + * Returns the hashcode for this object. + * The hashcode is the value of this enumeration object. * - * @return the hashcode + * @return The hashcode. */ public int hashCode() { @@ -98,8 +158,11 @@ public abstract class EnumSyntax implements Cloneable, Serializable /** * Returns the string representation for this object. + * The string value from <code>getStringTable()</code> method is returned + * if subclasses override this method. Otherwise the value of this object + * as a string is returned. * - * @return the string representation + * @return The string representation. */ public String toString() { @@ -118,9 +181,10 @@ public abstract class EnumSyntax implements Cloneable, Serializable * Returns a table with the enumeration values represented as strings * for this object. * - * The default implementation just returns null. + * The default implementation just returns null. Subclasses should + * override this method. * - * @return the enumeration values as strings + * @return The enumeration values as strings. */ protected String[] getStringTable() { @@ -128,17 +192,49 @@ public abstract class EnumSyntax implements Cloneable, Serializable } /** + * Needed for singelton semantics during deserialisation. + * + * Subclasses must not override this class. Subclasses have to override + * <code>getEnumValueTable()</code> and should override + * <code>getStringTable()</code> for correct serialization. + * + * @return The Object at index <code>value - getOffset()</code> + * in getEnumValueTable. + * @throws ObjectStreamException if getEnumValueTable() returns null. + */ + protected Object readResolve() throws ObjectStreamException + { + EnumSyntax[] table = getEnumValueTable(); + if (table == null) + throw new InvalidObjectException("Null enumeration value table " + + "for class " + + this.getClass().toString()); + + return table[value - getOffset()]; + } + + /** * Returns a table with the enumeration values for this object. * - * The default implementation just returns null. + * The default implementation just returns null. Subclasses have to + * to override this method for serialization. * - * @return the enumeration values + * @return The enumeration values. */ protected EnumSyntax[] getEnumValueTable() { return null; } + /** + * Returns the lowest used value by the enumerations of this class. + * + * The default implementation returns 0. This is enough if enumerations + * start with a zero value. Otherwise subclasses need to override this + * method for serialization and return the lowest value they use. + * . + * @return The lowest used value used. + */ protected int getOffset() { return 0; diff --git a/libjava/classpath/javax/print/attribute/HashAttributeSet.java b/libjava/classpath/javax/print/attribute/HashAttributeSet.java index c5fbe5ea88a..0db81bae540 100644 --- a/libjava/classpath/javax/print/attribute/HashAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/HashAttributeSet.java @@ -1,5 +1,5 @@ /* HashAttributeSet.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,10 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; +/** + * <code>HashAttributeSet</code> provides an implementation of + * {@link javax.print.attribute.AttributeSet}. + */ public class HashAttributeSet implements AttributeSet, Serializable { private static final long serialVersionUID = 5311560590283707917L; @@ -73,9 +77,11 @@ public class HashAttributeSet implements AttributeSet, Serializable * Creates a <code>HashAttributeSet</code> object with the given * attributes in it. * - * @param attributes the attributes to put into the set + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. * - * @exception NullPointerException If attributes is null + * @exception NullPointerException if one of the attributes of the given + * array is null. */ public HashAttributeSet(Attribute[] attributes) { @@ -83,12 +89,11 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Creates a <code>HashAttributeSet</code> object with the given - * attributes in it. - * - * @param attributes the attributes to put into the set + * Creates a <code>HashAttributeSet</code> object with attributes + * of the given attributes set in it. * - * @exception NullPointerException If attributes is null + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. */ public HashAttributeSet(AttributeSet attributes) { @@ -111,7 +116,11 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Creates an empty <code>HashAttributeSet</code> object. + * Creates a <code>HashAttributeSet</code> object with the given + * attribute in it. + * + * @param attribute the attribute to put into the set. + * @param interfaceName the interface that all members must implement. * * @exception ClassCastException if attribute is not an interface of * interfaceName @@ -128,7 +137,12 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Creates an empty <code>HashAttributeSet</code> object. + * Creates a <code>HashAttributeSet</code> object with the given + * attributes in it. + * + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. + * @param interfaceName the interface that all members must implement. * * @exception ClassCastException if any element of attributes is not an * interface of interfaceName @@ -138,15 +152,20 @@ public class HashAttributeSet implements AttributeSet, Serializable { this(interfaceName); - if (attributes == null) - throw new NullPointerException(); - - for (int index = 0; index < attributes.length; index++) - addInternal(attributes[index], interfaceName); + if (attributes != null) + { + for (int index = 0; index < attributes.length; index++) + addInternal(attributes[index], interfaceName); + } } /** - * Creates an empty <code>HashAttributeSet</code> object. + * Creates a <code>HashAttributeSet</code> object with attributes + * of the given attributes set in it. + * + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. + * @param interfaceName the interface that all members must implement. * * @exception ClassCastException if any element of attributes is not an * interface of interfaceName @@ -160,15 +179,16 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Adds the given attribute to the set. - * - * @param attribute the attribute to add - * - * @return true if the attribute set has changed, false otherwise - * - * @exception NullPointerException if attribute is null - * @exception UnmodifiableSetException if this attribute set does not - * support this action. + * Adds the specified attribute value to this attribute set + * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute to the set. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ public boolean add(Attribute attribute) { @@ -190,14 +210,13 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Adds the given attributes to the set. - * - * @param attributes the attributes to add - * - * @return true if the attribute set has changed, false otherwise - * - * @exception UnmodifiableSetException if this attribute set does not - * support this action. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ public boolean addAll(AttributeSet attributes) { @@ -218,9 +237,8 @@ public class HashAttributeSet implements AttributeSet, Serializable /** * Removes all attributes from this attribute set. - * - * @exception UnmodifiableSetException if this attribute set does not - * support this action. + * + * @throws UnmodifiableSetException if the set does not support modification. */ public void clear() { @@ -228,11 +246,12 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Checks if this attribute set contains an entry with the given category. - * - * @param category the category to test for - * - * @return true if the category exists in this attribute set, false otherwise. + * Checks if this attributes set contains an attribute with the given + * category. + * + * @param category the category to test for. + * @return <code>true</code> if an attribute of the category is contained + * in the set, <code>false</code> otherwise. */ public boolean containsKey(Class category) { @@ -240,12 +259,11 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Checks if this attribute set contains an entry with the given attribute. - * - * @param attribute the attribute to test for - * - * @return true if the attribute exists in this attribute set, - * false otherwise. + * Checks if this attribute set contains the given attribute. + * + * @param attribute the attribute to test for. + * @return <code>true</code> if the attribute is contained in the set, + * <code>false</code> otherwise. */ public boolean containsValue(Attribute attribute) { @@ -253,11 +271,12 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Tests of obj is equal to this object. - * - * @param obj the object to test - * - * @return true if both objects are equal, false otherwise. + * Tests this set for equality with the given object. <code>true</code> is + * returned, if the given object is also of type <code>AttributeSet</code> + * and the contained attributes are the same as in this set. + * + * @param obj the Object to test. + * @return <code>true</code> if equal, false otherwise. */ public boolean equals(Object obj) { @@ -268,33 +287,45 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Returns the attribute value that is connected to the given attribute - * category. If the attribute set does not contains the given category null - * will be returned. - * - * @param category the attribute category to return the attribute value for - * - * @return the attribute associated to category, or null + * Returns the attribute object contained in this set for the given attribute + * category. + * + * @param category the category of the attribute. A <code>Class</code> + * instance of a class implementing the <code>Attribute</code> interface. + * @return The attribute for this category or <code>null</code> if no + * attribute is contained for the given category. + * @throws NullPointerException if category is null. + * @throws ClassCastException if category is not implementing + * <code>Attribute</code>. */ public Attribute get(Class category) { + if (category == null) + throw new NullPointerException("category may not be null"); + return (Attribute) attributeMap.get(category); } /** - * Returns the hashcode for this object. - * - * @return the hashcode + * Returns the hashcode value. The hashcode value is the sum of all hashcodes + * of the attributes contained in this set. + * + * @return The hashcode for this attribute set. */ public int hashCode() { - return attributeMap.hashCode() + interfaceName.hashCode(); + int hashcode = 0; + Iterator it = attributeMap.values().iterator(); + while (it.hasNext()) + hashcode = hashcode + it.next().hashCode(); + + return hashcode; } /** * Checks if the attribute set is empty. * - * @return true if the attribute set is empty, false otherwise + * @return <code>true</code> if the attribute set is empty, false otherwise. */ public boolean isEmpty() { @@ -302,14 +333,12 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Removes the entry with the given attribute in it. - * - * @param attribute the attribute value of the entry to be removed - * - * @return true if the attribute set has changed, false otherwise. - * - * @exception UnmodifiableSetException if this attribute set does not - * support this action. + * Removes the given attribute from the set. If the given attribute is <code>null</code> + * nothing is done and <code>false</code> is returned. + * + * @param attribute the attribute to remove. + * @return <code>true</code> if removed, false in all other cases. + * @throws UnmodifiableSetException if the set does not support modification. */ public boolean remove(Attribute attribute) { @@ -320,11 +349,12 @@ public class HashAttributeSet implements AttributeSet, Serializable } /** - * Removes the entry with the given category in it. - * - * @param category the category value of the entry to be removed - * - * @return true if the attribute set has changed, false otherwise. + * Removes the attribute entry of the given category from the set. If the given + * category is <code>null</code> nothing is done and <code>false</code> is returned. + * + * @param category the category of the entry to be removed. + * @return <code>true</code> if an attribute is removed, false in all other cases. + * @throws UnmodifiableSetException if the set does not support modification. */ public boolean remove(Class category) { @@ -337,7 +367,7 @@ public class HashAttributeSet implements AttributeSet, Serializable /** * Returns the number of elements in this attribute set. * - * @return the number of elements. + * @return The number of elements. */ public int size() { @@ -347,12 +377,12 @@ public class HashAttributeSet implements AttributeSet, Serializable /** * Returns the content of the attribute set as an array * - * @return an array of attributes + * @return An array of attributes. */ public Attribute[] toArray() { int index = 0; - Iterator it = attributeMap.entrySet().iterator(); + Iterator it = attributeMap.values().iterator(); Attribute[] array = new Attribute[size()]; while (it.hasNext()) diff --git a/libjava/classpath/javax/print/attribute/HashDocAttributeSet.java b/libjava/classpath/javax/print/attribute/HashDocAttributeSet.java index 1647ae2f999..2317db3bc1e 100644 --- a/libjava/classpath/javax/print/attribute/HashDocAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/HashDocAttributeSet.java @@ -1,5 +1,5 @@ /* HashDocAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.attribute; import java.io.Serializable; +/** + * <code>HashDocAttributeSet</code> provides an implementation of + * {@link javax.print.attribute.DocAttributeSet}. + */ public class HashDocAttributeSet extends HashAttributeSet implements DocAttributeSet, Serializable { @@ -56,7 +60,7 @@ public class HashDocAttributeSet extends HashAttributeSet * Creates a <code>HashDocAttributeSet</code> object with the given * attribute in it. * - * @param attribute the attriute tu put into the attribute set + * @param attribute the attribute to put into the attribute set * * @exception NullPointerException if attribute is null */ @@ -69,9 +73,11 @@ public class HashDocAttributeSet extends HashAttributeSet * Creates a <code>HashDocAttributeSet</code> object with the given * attributes in it. * - * @param attributes the attributes to put into the attribute set + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. * - * @exception NullPointerException if attributes is null + * @exception NullPointerException if one of the attributes of the given + * array is null. */ public HashDocAttributeSet(DocAttribute[] attributes) { @@ -79,11 +85,11 @@ public class HashDocAttributeSet extends HashAttributeSet } /** - * Creates a <code>HashDocAttributeSet</code> object with the given - * attributes in it. - * - * @param attributes the attributes to put into the attribute set + * Creates a <code>HashDocAttributeSet</code> object with the attributes + * of the given attributes set in it. * + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. * @exception ClassCastException if any element of attributes is not * an instance of <code>DocAttribute</code> */ diff --git a/libjava/classpath/javax/print/attribute/HashPrintJobAttributeSet.java b/libjava/classpath/javax/print/attribute/HashPrintJobAttributeSet.java index 84fa7ec5d6a..ac4c902c332 100644 --- a/libjava/classpath/javax/print/attribute/HashPrintJobAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/HashPrintJobAttributeSet.java @@ -1,5 +1,5 @@ /* HashPrintJobAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.attribute; import java.io.Serializable; +/** + * <code>HashPrintJobAttributeSet</code> provides an implementation of + * {@link javax.print.attribute.PrintJobAttributeSet}. + */ public class HashPrintJobAttributeSet extends HashAttributeSet implements Serializable, PrintJobAttributeSet { @@ -56,7 +60,7 @@ public class HashPrintJobAttributeSet extends HashAttributeSet * Creates a <code>HashPrintJobAttributeSet</code> object with the given * attribute in it. * - * @param attribute the attriute tu put into the attribute set + * @param attribute the attribute to put into the attribute set * * @exception NullPointerException if attribute is null */ @@ -69,9 +73,11 @@ public class HashPrintJobAttributeSet extends HashAttributeSet * Creates a <code>HashPrintJobAttributeSet</code> object with the given * attributes in it. * - * @param attributes the attributes to put into the attribute set + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. * - * @exception NullPointerException if attributes is null + * @exception NullPointerException if one of the attributes of the given + * array is null. */ public HashPrintJobAttributeSet(PrintJobAttribute[] attributes) { @@ -79,11 +85,11 @@ public class HashPrintJobAttributeSet extends HashAttributeSet } /** - * Creates a <code>HashPrintJobAttributeSet</code> object with the given - * attributes in it. - * - * @param attributes the attributes to put into the attribute set + * Creates a <code>HashPrintJobAttributeSet</code> object with the attributes + * of the given attributes set in it. * + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. * @exception ClassCastException if any element of attributes is not * an instance of <code>PrintJobAttribute</code> */ diff --git a/libjava/classpath/javax/print/attribute/HashPrintRequestAttributeSet.java b/libjava/classpath/javax/print/attribute/HashPrintRequestAttributeSet.java index 29a17861f7b..e74c0e00fbe 100644 --- a/libjava/classpath/javax/print/attribute/HashPrintRequestAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/HashPrintRequestAttributeSet.java @@ -1,5 +1,5 @@ /* HashPrintRequestAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.attribute; import java.io.Serializable; +/** + * <code>HashPrintRequestAttributeSet</code> provides an implementation of + * {@link javax.print.attribute.PrintRequestAttributeSet}. + */ public class HashPrintRequestAttributeSet extends HashAttributeSet implements Serializable, PrintRequestAttributeSet { @@ -56,7 +60,7 @@ public class HashPrintRequestAttributeSet extends HashAttributeSet * Creates a <code>HashPrintRequestAttributeSet</code> object with the given * attribute in it. * - * @param attribute the attriute tu put into the attribute set + * @param attribute the attribute to put into the attribute set * * @exception NullPointerException if attribute is null */ @@ -69,9 +73,11 @@ public class HashPrintRequestAttributeSet extends HashAttributeSet * Creates a <code>HashPrintRequestAttributeSet</code> object with the given * attributes in it. * - * @param attributes the attributes to put into the attribute set + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. * - * @exception NullPointerException if attributes is null + * @exception NullPointerException if one of the attributes of the given + * array is null. */ public HashPrintRequestAttributeSet(PrintRequestAttribute[] attributes) { @@ -79,11 +85,11 @@ public class HashPrintRequestAttributeSet extends HashAttributeSet } /** - * Creates a <code>HashPrintRequestAttributeSet</code> object with the given - * attributes in it. - * - * @param attributes the attributes to put into the attribute set + * Creates a <code>HashPrintRequestAttributeSet</code> object with the attributes + * of the given attributes set in it. * + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. * @exception ClassCastException if any element of attributes is not * an instance of <code>PrintRequestAttribute</code> */ diff --git a/libjava/classpath/javax/print/attribute/HashPrintServiceAttributeSet.java b/libjava/classpath/javax/print/attribute/HashPrintServiceAttributeSet.java index 60e12bff0d1..155514f2b01 100644 --- a/libjava/classpath/javax/print/attribute/HashPrintServiceAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/HashPrintServiceAttributeSet.java @@ -1,5 +1,5 @@ /* HashPrintServiceAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.attribute; import java.io.Serializable; +/** + * <code>HashPrintServiceAttributeSet</code> provides an implementation of + * {@link javax.print.attribute.PrintServiceAttributeSet}. + */ public class HashPrintServiceAttributeSet extends HashAttributeSet implements Serializable, PrintServiceAttributeSet { @@ -56,7 +60,7 @@ public class HashPrintServiceAttributeSet extends HashAttributeSet * Creates a <code>HashPrintServiceAttributeSet</code> object with the given * attribute in it. * - * @param attribute the attriute tu put into the attribute set + * @param attribute the attribute to put into the attribute set * * @exception NullPointerException if attribute is null */ @@ -69,9 +73,11 @@ public class HashPrintServiceAttributeSet extends HashAttributeSet * Creates a <code>HashPrintServiceAttributeSet</code> object with the given * attributes in it. * - * @param attributes the attributes to put into the attribute set + * @param attributes the array of attributes to put into the set. If + * <code>null</code> an empty set is created. * - * @exception NullPointerException if attributes is null + * @exception NullPointerException if one of the attributes of the given + * array is null. */ public HashPrintServiceAttributeSet(PrintServiceAttribute[] attributes) { @@ -79,11 +85,11 @@ public class HashPrintServiceAttributeSet extends HashAttributeSet } /** - * Creates a <code>HashPrintServiceAttributeSet</code> object with the given - * attributes in it. - * - * @param attributes the attributes to put into the attribute set + * Creates a <code>HashPrintServiceAttributeSet</code> object with the attributes + * of the given attributes set in it. * + * @param attributes the attributes set to put into the set. If + * <code>null</code> an empty set is created. * @exception ClassCastException if any element of attributes is not * an instance of <code>PrintServiceAttribute</code> */ diff --git a/libjava/classpath/javax/print/attribute/IntegerSyntax.java b/libjava/classpath/javax/print/attribute/IntegerSyntax.java index d5500b4ca8a..c2f9224a2a2 100644 --- a/libjava/classpath/javax/print/attribute/IntegerSyntax.java +++ b/libjava/classpath/javax/print/attribute/IntegerSyntax.java @@ -1,5 +1,5 @@ /* IntegerSyntax.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,7 +40,10 @@ package javax.print.attribute; import java.io.Serializable; /** - * @author Michael Koch + * <code>IntegerSyntax</code> is the abstract base class of all attribute + * classes having an integer as value. + * + * @author Michael Koch (konqueror@gmx.de) */ public abstract class IntegerSyntax implements Cloneable, Serializable { @@ -49,7 +52,7 @@ public abstract class IntegerSyntax implements Cloneable, Serializable /** * Creates a <code>IntegerSyntax</code> with the given value. * - * @param value the value to set + * @param value the integer to set */ protected IntegerSyntax(int value) { @@ -57,9 +60,10 @@ public abstract class IntegerSyntax implements Cloneable, Serializable } /** - * Creates a <code>IntegerSyntax</code> with the given arguments. + * Creates a <code>IntegerSyntax</code> with the given integer value + * and checks if the value lies inside the given bounds.. * - * @param value the value to set + * @param value the integer to set * @param lowerBound the lower bound for the value * @param upperBound the upper bound for the value * @@ -78,7 +82,7 @@ public abstract class IntegerSyntax implements Cloneable, Serializable /** * Returns the value of this object. * - * @return the value + * @return The integer value. */ public int getValue() { @@ -86,11 +90,12 @@ public abstract class IntegerSyntax implements Cloneable, Serializable } /** - * Tests of obj is equal to this object. + * Tests if the given object is equal to this object. * * @param obj the object to test * - * @return true if both objects are equal, false otherwise. + * @return <code>true</code> if both objects are equal, + * <code>false</code> otherwise. */ public boolean equals(Object obj) { @@ -103,7 +108,7 @@ public abstract class IntegerSyntax implements Cloneable, Serializable /** * Returns the hashcode for this object. * - * @return the hashcode + * @return The hashcode. */ public int hashCode() { @@ -113,7 +118,7 @@ public abstract class IntegerSyntax implements Cloneable, Serializable /** * Returns the string representation for this object. * - * @return the string representation + * @return The string representation. */ public String toString() { diff --git a/libjava/classpath/javax/print/attribute/PrintJobAttribute.java b/libjava/classpath/javax/print/attribute/PrintJobAttribute.java index ba3a737b523..fd3663496c8 100644 --- a/libjava/classpath/javax/print/attribute/PrintJobAttribute.java +++ b/libjava/classpath/javax/print/attribute/PrintJobAttribute.java @@ -1,5 +1,5 @@ /* PrintJobAttribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,23 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * Marker interface for all attribute classes describing attributes or the + * status of a ({@link javax.print.DocPrintJob} object. + * <p> + * Instances of implementing attribute classes may be collected in a + * {@link javax.print.attribute.PrintJobAttributeSet}. + * </p><p> + * A print service uses attributes of this type to inform about the status + * of a print job. + * For example {@link javax.print.attribute.standard.DateTimeAtProcessing} + * is used to report at which date and time a job has started processing. + * </p> + * + * @see javax.print.attribute.PrintJobAttributeSet + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintJobAttribute extends Attribute { + // Marker interface } diff --git a/libjava/classpath/javax/print/attribute/PrintJobAttributeSet.java b/libjava/classpath/javax/print/attribute/PrintJobAttributeSet.java index 905d53c2d71..6283ae14276 100644 --- a/libjava/classpath/javax/print/attribute/PrintJobAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/PrintJobAttributeSet.java @@ -1,5 +1,5 @@ /* PrintJobAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,18 +38,45 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * <code>PrintJobAttributeSet</code> specifies an attribute set which only + * allows printing attributes of type + * {@link javax.print.attribute.PrintJobAttribute}. + * <p> + * The methods {@link #add(Attribute)} and {@link #addAll(AttributeSet)} are + * respecified in this interface to indicate that only + * <code>PrintJobAttribute</code> instances are allowed in this set. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintJobAttributeSet extends AttributeSet { /** - * Adds the specified attribute value to this attribute set + * Adds the specified attribute value to this attribute set * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if attribute is not of type + * <code>PrintJobAttribute</code>. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ boolean add (Attribute attribute); /** - * Adds all of the elements in the specified set to this attribute. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if one of the attributes is not of type + * <code>PrintJobAttribute</code>. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ boolean addAll (AttributeSet attributes); } diff --git a/libjava/classpath/javax/print/attribute/PrintRequestAttribute.java b/libjava/classpath/javax/print/attribute/PrintRequestAttribute.java index 756350020d2..8a05b75d3cd 100644 --- a/libjava/classpath/javax/print/attribute/PrintRequestAttribute.java +++ b/libjava/classpath/javax/print/attribute/PrintRequestAttribute.java @@ -1,5 +1,5 @@ /* PrintRequestAttribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,18 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * Marker interface for all attribute classes which specify a requested + * attribute of {@link javax.print.DocPrintJob} object. + * <p> + * Instances of implementing attribute classes may be collected in a + * {@link javax.print.attribute.PrintRequestAttributeSet}. + * </p> + * + * @see javax.print.attribute.PrintRequestAttributeSet + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintRequestAttribute extends Attribute { + // Marker interface } diff --git a/libjava/classpath/javax/print/attribute/PrintRequestAttributeSet.java b/libjava/classpath/javax/print/attribute/PrintRequestAttributeSet.java index d72d2d71c47..350d9a644da 100644 --- a/libjava/classpath/javax/print/attribute/PrintRequestAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/PrintRequestAttributeSet.java @@ -1,5 +1,5 @@ /* PrintRequestAttributeSet.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,18 +38,45 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * <code>PrintRequestAttributeSet</code> specifies an attribute set which only + * allows printing attributes of type + * {@link javax.print.attribute.PrintRequestAttribute}. + * <p> + * The methods {@link #add(Attribute)} and {@link #addAll(AttributeSet)} are + * respecified in this interface to indicate that only + * <code>PrintRequestAttribute</code> instances are allowed in this set. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintRequestAttributeSet extends AttributeSet { /** - * Adds the specified attribute value to this attribute set + * Adds the specified attribute value to this attribute set * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if attribute is not of type + * <code>PrintRequestAttribute</code>. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ boolean add (Attribute attribute); /** - * Adds all of the elements in the specified set to this attribute. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if one of the attributes is not of type + * <code>PrintRequestAttribute</code>. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ boolean addAll (AttributeSet attributes); } diff --git a/libjava/classpath/javax/print/attribute/PrintServiceAttribute.java b/libjava/classpath/javax/print/attribute/PrintServiceAttribute.java index 3cf8825f560..213f43796bb 100644 --- a/libjava/classpath/javax/print/attribute/PrintServiceAttribute.java +++ b/libjava/classpath/javax/print/attribute/PrintServiceAttribute.java @@ -1,5 +1,5 @@ /* PrintServiceAttribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,23 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * Marker interface for all attribute classes describing parameters + * or the status of a {@link javax.print.PrintService}. + * <p> + * Instances of implementing attribute classes may be collected in a + * {@link javax.print.attribute.PrintServiceAttributeSet}. + * </p><p> + * A print service uses attributes of this type to inform about the status + * or the specific capabilities of itself. + * For example {@link javax.print.attribute.standard.PagesPerMinute} is used + * to specify the average printable pages per minute of the print service. + * </p> + * + * @see javax.print.attribute.PrintServiceAttributeSet + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintServiceAttribute extends Attribute { + // Marker interface } diff --git a/libjava/classpath/javax/print/attribute/PrintServiceAttributeSet.java b/libjava/classpath/javax/print/attribute/PrintServiceAttributeSet.java index d67c9af55f0..fa22ee0d922 100644 --- a/libjava/classpath/javax/print/attribute/PrintServiceAttributeSet.java +++ b/libjava/classpath/javax/print/attribute/PrintServiceAttributeSet.java @@ -1,5 +1,5 @@ /* PrintServiceAttributeSet.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,18 +38,45 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * <code>PrintServiceAttributeSet</code> specifies an attribute set which only + * allows printing attributes of type + * {@link javax.print.attribute.PrintServiceAttribute}. + * <p> + * The methods {@link #add(Attribute)} and {@link #addAll(AttributeSet)} are + * respecified in this interface to indicate that only + * <code>PrintServiceAttribute</code> instances are allowed in this set. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface PrintServiceAttributeSet extends AttributeSet { /** - * Adds the specified attribute value to this attribute set + * Adds the specified attribute value to this attribute set * if it is not already present. + * + * This operation removes any existing attribute of the same category + * before adding the given attribute. + * + * @param attribute the attribute to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if attribute is not of type + * <code>PrintServiceAttribute</code>. + * @throws NullPointerException if the attribute is <code>null</code>. + * @throws UnmodifiableSetException if the set does not support modification. */ boolean add (Attribute attribute); /** - * Adds all of the elements in the specified set to this attribute. + * Adds all of the elements in the specified set to this attribute set. + * + * @param attributes the set of attributes to add. + * @return <code>true</code> if the set is changed, false otherwise. + * @throws ClassCastException if one of the attributes is not of type + * <code>PrintServiceAttribute</code>. + * @throws UnmodifiableSetException if the set does not support modification. + * + * @see #add(Attribute) */ boolean addAll (AttributeSet attributes); } diff --git a/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java b/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java index d73b867d5f6..3990b66c50a 100644 --- a/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java +++ b/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java @@ -38,7 +38,11 @@ exception statement from your version. */ package javax.print.attribute; import java.io.Serializable; -import java.util.Vector; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; /** * @author Michael Koch @@ -50,33 +54,41 @@ public abstract class SetOfIntegerSyntax private int[][] members; - private static int[][] normalize(Vector vecMembers) + private static int[][] normalize(int[][] values, int size) { - // XXX: Perhaps we should merge ranges that overlap. - - int current = 0; - int[][] members = new int[vecMembers.size()][]; + // Sort into increasing order. First the first index is + // compared, then the second. + Arrays.sort(values, 0, size, new Comparator() + { + public int compare(Object o1, Object o2) + { + int[] v1 = (int[]) o1; + int[] v2 = (int[]) o2; + if (v1[0] == v2[0]) + return v1[1] - v2[1]; + return v1[0] - v2[0]; + } + }); - while (vecMembers.size() > 0) + // Now coalesce overlapping ranges. + int outIndex = 0; + for (int i = 0; i < size; ++i) { - // Search the lowest range. - int[] range = (int[]) vecMembers.elementAt(0); - - for (int index = 1; index < vecMembers.size(); index++) + // Note that we compare with values[i][1]+1, since + // we can coalesce {0,1} with {2,x}. + int save = i; + while (i + 1 < size && values[i + 1][0] <= values[i][1] + 1) { - int[] tmp = (int[]) vecMembers.elementAt(index); - - if (range[0] > tmp[0] - || (range[0] == tmp[0] - && range[0] > tmp[0])) - range = tmp; + values[i][1] = Math.max(values[i][1], values[i + 1][1]); + ++i; } - - members[current] = range; - current++; + values[outIndex++] = values[save]; } - return members; + int[][] result = new int[outIndex][]; + System.arraycopy(values, 0, result, 0, outIndex); + + return result; } /** @@ -104,10 +116,13 @@ public abstract class SetOfIntegerSyntax */ protected SetOfIntegerSyntax(int[][] members) { - Vector vecMembers = new Vector(); - - if (members != null) + int[][] newMembers; + int outIndex = 0; + if (members == null) + newMembers = new int[0][]; + else { + newMembers = new int[members.length][]; for (int index = 0; index < members.length; index++) { int lower; @@ -126,6 +141,7 @@ public abstract class SetOfIntegerSyntax else throw new IllegalArgumentException("invalid member element"); + // We only want to reject non-null ranges where lower<0. if (lower <= upper && lower < 0) throw new IllegalArgumentException("invalid member element"); @@ -134,12 +150,81 @@ public abstract class SetOfIntegerSyntax int[] range = new int[2]; range[0] = lower; range[1] = upper; - vecMembers.add(range); + newMembers[outIndex++] = range; } } } - this.members = normalize(vecMembers); + this.members = normalize(newMembers, outIndex); + } + + private boolean skipWhitespace(StringCharacterIterator i) + { + while (Character.isWhitespace(i.current())) + i.next(); + return i.current() == CharacterIterator.DONE; + } + + private boolean skipNumber(StringCharacterIterator i) + { + boolean readAny = false; + while (Character.isDigit(i.current())) + { + readAny = true; + i.next(); + } + return readAny; + } + + protected SetOfIntegerSyntax(String s) + { + ArrayList vals = new ArrayList(); + + StringCharacterIterator it = new StringCharacterIterator(s); + + while (true) + { + // Skip whitespace. + if (skipWhitespace(it)) + break; + + // Parse integer. + int index = it.getIndex(); + if (! skipNumber(it)) + throw new IllegalArgumentException(); + int[] item = new int[2]; + item[0] = Integer.parseInt(s.substring(index, it.getIndex())); + + if (! skipWhitespace(it)) + { + char c = it.current(); + if (c == ':' || c == '-') + { + it.next(); + if (skipWhitespace(it)) + throw new IllegalArgumentException(); + index = it.getIndex(); + if (! skipNumber(it)) + throw new IllegalArgumentException(); + item[1] = Integer.parseInt(s.substring(index, it.getIndex())); + } + else + item[1] = item[0]; + } + else + item[1] = item[0]; + + if (item[0] <= item[1]) + vals.add(item); + + if (skipWhitespace(it)) + break; + if (it.current() != ',') + throw new IllegalArgumentException(); + it.next(); + } + + members = normalize((int[][]) vals.toArray(new int[0][]), vals.size()); } /** @@ -153,6 +238,7 @@ public abstract class SetOfIntegerSyntax */ protected SetOfIntegerSyntax(int lowerBound, int upperBound) { + // We only want to reject non-null ranges where lower<0. if (lowerBound <= upperBound && lowerBound < 0) throw new IllegalArgumentException(); @@ -175,7 +261,7 @@ public abstract class SetOfIntegerSyntax { if (value < members[index][0]) return false; - else if (value < members[index][1]) + else if (value <= members[index][1]) return true; } @@ -205,8 +291,16 @@ public abstract class SetOfIntegerSyntax { if (! (obj instanceof SetOfIntegerSyntax)) return false; - - throw new Error("not implemented"); + SetOfIntegerSyntax other = (SetOfIntegerSyntax) obj; + if (other.members.length != members.length) + return false; + for (int i = 0; i < members.length; ++i) + { + if (members[i][0] != other.members[i][0] + || members[i][1] != other.members[i][1]) + return false; + } + return true; } /** @@ -216,7 +310,7 @@ public abstract class SetOfIntegerSyntax */ public int[][] getMembers() { - throw new Error("not implemented"); + return (int[][]) members.clone(); } /** @@ -226,11 +320,14 @@ public abstract class SetOfIntegerSyntax */ public int hashCode() { - throw new Error("not implemented"); + int result = 0; + for (int i = 0; i < members.length; ++i) + result += members[i][0] + members[i][1]; + return result; } /** - * Returns the smallest value that is greater then x. + * Returns the smallest value that is greater than x which is in this set. * * @param x an integer value * @@ -238,7 +335,16 @@ public abstract class SetOfIntegerSyntax */ public int next(int x) { - throw new Error("not implemented"); + for (int i = 0; i < members.length; ++i) + { + if (x >= members[i][1]) + continue; + if (x < members[i][0]) + return members[i][0]; + // X is in this range. + return x + 1; + } + return -1; } /** @@ -248,6 +354,18 @@ public abstract class SetOfIntegerSyntax */ public String toString() { - throw new Error("not implemented"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < members.length; ++i) + { + if (i > 0) + sb.append(','); + sb.append(members[i][0]); + if (members[i][0] != members[i][1]) + { + sb.append('-'); + sb.append(members[i][1]); + } + } + return sb.toString(); } } diff --git a/libjava/classpath/javax/print/attribute/SupportedValuesAttribute.java b/libjava/classpath/javax/print/attribute/SupportedValuesAttribute.java index d0f4b65c647..a001e7e91ce 100644 --- a/libjava/classpath/javax/print/attribute/SupportedValuesAttribute.java +++ b/libjava/classpath/javax/print/attribute/SupportedValuesAttribute.java @@ -1,5 +1,5 @@ -/* Attribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. +/* SupportedValuesAttribute.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,22 @@ exception statement from your version. */ package javax.print.attribute; /** - * @author Michael Koch + * Marker interface for all attribute classes specifying the + * supported/allowed values for another printing attribute class. + * <p> + * A {@link javax.print.PrintService} instance for example provides + * printing attribute classes implementing this interface to indicate + * that a specific attribute type is supported and if the supported values. + * </p><p> + * E.g. a {@link javax.print.attribute.standard.JobPrioritySupported} + * instance indicates that the attribute class + * {@link javax.print.attribute.standard.JobPriority} is supported and + * provides the number of the possible priority levels. + * </p> + * + * @author Michael Koch (konqueror@gmx.de) */ public interface SupportedValuesAttribute extends Attribute { + // Marker interface } diff --git a/libjava/classpath/javax/print/attribute/URISyntax.java b/libjava/classpath/javax/print/attribute/URISyntax.java index f0583f7e53c..07deb4b5741 100644 --- a/libjava/classpath/javax/print/attribute/URISyntax.java +++ b/libjava/classpath/javax/print/attribute/URISyntax.java @@ -1,5 +1,5 @@ /* URISyntax.java -- - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,7 +41,10 @@ import java.io.Serializable; import java.net.URI; /** - * @author Michael Koch + * <code>URISyntax</code> is the abstract base class of all attribute + * classes having an Uniform Resource Identifier URI as value. + * + * @author Michael Koch (konqueror@gmx.de) */ public abstract class URISyntax implements Cloneable, Serializable @@ -66,11 +69,12 @@ public abstract class URISyntax } /** - * Tests of obj is equal to this object. + * Tests if the given object is equal to this object. * * @param obj the object to test * - * @returns true if both objects are equal, false otherwise. + * @return <code>true</code> if both objects are equal, + * <code>false</code> otherwise. */ public boolean equals(Object obj) { @@ -83,7 +87,7 @@ public abstract class URISyntax /** * Returns the URI value of this syntax object. * - * @return the URI + * @return The URI. */ public URI getURI() { @@ -93,7 +97,7 @@ public abstract class URISyntax /** * Returns the hashcode for this object. * - * @return the hashcode + * @return The hashcode. */ public int hashCode() { @@ -103,7 +107,7 @@ public abstract class URISyntax /** * Returns the string representation for this object. * - * @return the string representation + * @return The string representation. */ public String toString() { diff --git a/libjava/classpath/javax/print/attribute/UnmodifiableSetException.java b/libjava/classpath/javax/print/attribute/UnmodifiableSetException.java index 678531769a5..ed1687c4cba 100644 --- a/libjava/classpath/javax/print/attribute/UnmodifiableSetException.java +++ b/libjava/classpath/javax/print/attribute/UnmodifiableSetException.java @@ -1,5 +1,5 @@ -/* Attribute.java -- - Copyright (C) 2003 Free Software Foundation, Inc. +/* UnmodifiableSetException.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -35,10 +35,14 @@ 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 javax.print.attribute; /** - * @author Michael Koch + * Exception which is thrown if an operation on an unmodifiable set + * is invoked. + * + * @author Michael Koch (konqueror@gmx.de) * * @since 1.4 */ @@ -56,7 +60,7 @@ public class UnmodifiableSetException extends RuntimeException * Creates a <code>UnmodifiableSetException</code> * with the given message. * - * @param message the message for the exception + * @param message the message of the exception */ public UnmodifiableSetException(String message) { diff --git a/libjava/classpath/javax/print/attribute/package.html b/libjava/classpath/javax/print/attribute/package.html index ba67d1a79d6..37f24d56b47 100644 --- a/libjava/classpath/javax/print/attribute/package.html +++ b/libjava/classpath/javax/print/attribute/package.html @@ -1,6 +1,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <!-- package.html - describes classes in javax.print.attribute package. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,7 +40,10 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.print.attribute</title></head> <body> -<p></p> - +<p>Provides classes and interfaces describing the roles and +syntax of attribute objects in the Java Print Service API.</p> +<p> +<b>Since:</b> 1.4 +</p> </body> </html> diff --git a/libjava/classpath/javax/print/event/PrintEvent.java b/libjava/classpath/javax/print/event/PrintEvent.java index cbf93852cc5..d44c2066818 100644 --- a/libjava/classpath/javax/print/event/PrintEvent.java +++ b/libjava/classpath/javax/print/event/PrintEvent.java @@ -1,5 +1,5 @@ /* PrintEvent.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,8 @@ import java.util.EventObject; /** + * Superclass of all events in the Java Print Service API. + * * @author Michael Koch (konqueror@gmx.de) */ public class PrintEvent extends EventObject @@ -58,7 +60,7 @@ public class PrintEvent extends EventObject /** * Returns a string representation of this object. * - * @return the string representation + * @return The string representation */ public String toString() { diff --git a/libjava/classpath/javax/print/event/PrintJobAdapter.java b/libjava/classpath/javax/print/event/PrintJobAdapter.java index 3615108f93a..9229d195fe4 100644 --- a/libjava/classpath/javax/print/event/PrintJobAdapter.java +++ b/libjava/classpath/javax/print/event/PrintJobAdapter.java @@ -1,5 +1,5 @@ /* PrintJobAdapter.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,11 @@ package javax.print.event; /** + * Adapter class for implementing {@link javax.print.event.PrintJobListener} + * classes. The methods in this class do nothing by default. Subclasses may + * only implement the methods for the {@link javax.print.event.PrintJobEvent}s + * they are interested in. + * * @author Michael Koch (konqueror@gmx.de) */ public abstract class PrintJobAdapter @@ -53,10 +58,11 @@ public abstract class PrintJobAdapter } /** - * Called to notify the client that all data has bin successfully transferred + * Called to notify the client that all data has been successfully transferred * to the print service. + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printDataTransferCompleted(PrintJobEvent event) { @@ -64,9 +70,10 @@ public abstract class PrintJobAdapter } /** - * Called to notify the client that a print job was canceled. + * Called to notify the client that a print job was canceled. + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printJobCanceled(PrintJobEvent event) { @@ -75,8 +82,9 @@ public abstract class PrintJobAdapter /** * Called to notify the client that a print job was successfully completed. + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printJobCompleted(PrintJobEvent event) { @@ -86,8 +94,9 @@ public abstract class PrintJobAdapter /** * Called to notify the client that a print job failed to complete * successfully. + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printJobFailed(PrintJobEvent event) { @@ -96,8 +105,9 @@ public abstract class PrintJobAdapter /** * Called to notify the client that no more job events will be send. + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printJobNoMoreEvents(PrintJobEvent event) { @@ -105,10 +115,12 @@ public abstract class PrintJobAdapter } /** - * Called to notify the client that a problem occured during printing - * but the user may be able to fix it. + * Called to notify the client that a problem occured during printing. + * This event signals problems a user might be able to fix + * (e.g. out of paper or paper jam). + * <p>The default implementation does nothing.</p> * - * <p>The default implementation does nothing</p> + * @param event the event. */ public void printJobRequiresAttention(PrintJobEvent event) { diff --git a/libjava/classpath/javax/print/event/PrintJobAttributeEvent.java b/libjava/classpath/javax/print/event/PrintJobAttributeEvent.java index 0914aea9f4b..d401ab15152 100644 --- a/libjava/classpath/javax/print/event/PrintJobAttributeEvent.java +++ b/libjava/classpath/javax/print/event/PrintJobAttributeEvent.java @@ -1,5 +1,5 @@ /* PrintJobAttributeEvent.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,6 +42,10 @@ import javax.print.attribute.PrintJobAttributeSet; /** + * <code>PrintJobAttributeEvent</code>s are generated by a + * <code>PrintService</code> to inform registered listeners that attributes + * associated with a {@link javax.print.DocPrintJob} instance have changed. + * * @author Michael Koch (konqueror@gmx.de) */ public class PrintJobAttributeEvent extends PrintEvent @@ -66,7 +70,7 @@ public class PrintJobAttributeEvent extends PrintEvent /** * Returns the print job generating this event. * - * @return the print job + * @return The print job. */ public DocPrintJob getPrintJob() { @@ -76,7 +80,7 @@ public class PrintJobAttributeEvent extends PrintEvent /** * Returns the attributes that changed and their new values. * - * @return the changes attributes + * @return The changed attributes. */ public PrintJobAttributeSet getAttributes() { diff --git a/libjava/classpath/javax/print/event/PrintJobAttributeListener.java b/libjava/classpath/javax/print/event/PrintJobAttributeListener.java index ee816d22a1c..9f96d267e9c 100644 --- a/libjava/classpath/javax/print/event/PrintJobAttributeListener.java +++ b/libjava/classpath/javax/print/event/PrintJobAttributeListener.java @@ -1,5 +1,5 @@ /* PrintJobAttributeListener.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.event; /** + * Listener interface to receive attribute changes from a print job. + * Implementations of this interface can be registered with a + * {@link javax.print.DocPrintJob} instance. + * * @author Michael Koch (konqueror@gmx.de) */ public interface PrintJobAttributeListener diff --git a/libjava/classpath/javax/print/event/PrintJobEvent.java b/libjava/classpath/javax/print/event/PrintJobEvent.java index c4b7cd6f942..cc15f97535b 100644 --- a/libjava/classpath/javax/print/event/PrintJobEvent.java +++ b/libjava/classpath/javax/print/event/PrintJobEvent.java @@ -1,5 +1,5 @@ /* PrintEvent.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,19 +41,38 @@ import javax.print.DocPrintJob; /** + * <code>PrintJobEvent</code>s are generated by a print job during + * print job processing to inform registered listeners about the state + * of processing. + * * @author Michael Koch (konqueror@gmx.de) */ public class PrintJobEvent extends PrintEvent { private static final long serialVersionUID = -1711656903622072997L; - + + /** Indicates that the data transfer to the print service has completed. */ public static final int DATA_TRANSFER_COMPLETE = 106; + + /** Indicates that the print job was canceled. */ public static final int JOB_CANCELED = 101; + + /** Indicates that the print job was completed (=printed). */ public static final int JOB_COMPLETE = 102; + + /** Indicates that the print job failed to complete. */ public static final int JOB_FAILED = 103; + + /** Indicates that no more job events will be send.*/ public static final int NO_MORE_EVENTS = 105; + + /** + * Indicates a situation where human intervention might be needed. + * E.g. the printer run out of paper or a paper jam occured. + */ public static final int REQUIRES_ATTENTION = 104; + /** The reason (one of the defined constants). */ private int reason; /** @@ -71,7 +90,7 @@ public class PrintJobEvent extends PrintEvent /** * Returns the reason for this event. * - * @return the reason + * @return The reason. */ public int getPrintEventType() { @@ -81,7 +100,7 @@ public class PrintJobEvent extends PrintEvent /** * Returns the print job that generated this event. * - * @return the print job + * @return The print job. */ public DocPrintJob getPrintJob() { diff --git a/libjava/classpath/javax/print/event/PrintJobListener.java b/libjava/classpath/javax/print/event/PrintJobListener.java index d1dcf42be71..96c6d411d54 100644 --- a/libjava/classpath/javax/print/event/PrintJobListener.java +++ b/libjava/classpath/javax/print/event/PrintJobListener.java @@ -1,5 +1,5 @@ /* PrintJobListener.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,7 +39,14 @@ package javax.print.event; /** - * @author Michael Koch (konqueror@gmx.de) */ + * Listener interface to receive processing events from a print job. + * Implementations of this interface can be registered with a + * {@link javax.print.DocPrintJob} instance. + * + * @see javax.print.event.PrintJobAdapter + * + * @author Michael Koch (konqueror@gmx.de) + */ public interface PrintJobListener { /** @@ -79,7 +86,9 @@ public interface PrintJobListener void printJobNoMoreEvents(PrintJobEvent event); /** - * Notifies the listener that an error occured and the user might be able to fix it. + * Notifies the listener that a problem occured during printing. + * This event signals problems a user might be able to fix + * (e.g. out of paper or paper jam). * * @param event the event */ diff --git a/libjava/classpath/javax/print/event/PrintServiceAttributeEvent.java b/libjava/classpath/javax/print/event/PrintServiceAttributeEvent.java index d3981747fa8..a41e213ff7b 100644 --- a/libjava/classpath/javax/print/event/PrintServiceAttributeEvent.java +++ b/libjava/classpath/javax/print/event/PrintServiceAttributeEvent.java @@ -1,5 +1,5 @@ /* PrintServiceAttributeEvent.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,6 +42,10 @@ import javax.print.attribute.PrintServiceAttributeSet; /** + * <code>PrintServiceAttributeEvent</code>s are generated by a + * <code>PrintService</code> to inform registered listeners that + * its associated attributes have changed. + * * @author Michael Koch (konqueror@gmx.de) */ public class PrintServiceAttributeEvent extends PrintEvent @@ -64,7 +68,7 @@ public class PrintServiceAttributeEvent extends PrintEvent /** * Returns the print service that generated this event. * - * @return the print service + * @return The print service. */ public PrintService getPrintService() { @@ -74,7 +78,7 @@ public class PrintServiceAttributeEvent extends PrintEvent /** * Returns the changed attributes this event reports. * - * @return the changed attributes + * @return The changed attributes. */ public PrintServiceAttributeSet getAttributes() { diff --git a/libjava/classpath/javax/print/event/PrintServiceAttributeListener.java b/libjava/classpath/javax/print/event/PrintServiceAttributeListener.java index e43d9ad65ee..b46bf3b69d7 100644 --- a/libjava/classpath/javax/print/event/PrintServiceAttributeListener.java +++ b/libjava/classpath/javax/print/event/PrintServiceAttributeListener.java @@ -1,5 +1,5 @@ /* PrintServiceAttributeListener.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,10 @@ package javax.print.event; /** + * Listener interface to receive attribute changes from a print service. + * Implementations of this interface can be registered with a + * {@link javax.print.PrintService} instance. + * * @author Michael Koch (konqueror@gmx.de) */ public interface PrintServiceAttributeListener diff --git a/libjava/classpath/javax/print/event/package.html b/libjava/classpath/javax/print/event/package.html index 52a298a68da..f811013d10d 100644 --- a/libjava/classpath/javax/print/event/package.html +++ b/libjava/classpath/javax/print/event/package.html @@ -1,6 +1,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <!-- package.html - describes classes in javax.print.event package. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,7 +40,9 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.print.event</title></head> <body> -<p></p> - +<p>Provides events and listeners to be used with the Java Print Service API.</p> +<p> +<b>Since:</b> 1.4 +</p> </body> </html> diff --git a/libjava/classpath/javax/rmi/BAD_OPERATION.java b/libjava/classpath/javax/rmi/BAD_OPERATION.java deleted file mode 100644 index 36081a47c57..00000000000 --- a/libjava/classpath/javax/rmi/BAD_OPERATION.java +++ /dev/null @@ -1,4 +0,0 @@ -package javax.rmi; - -/** XXX - Stub till we have org.omg.CORBA */ -public class BAD_OPERATION extends Exception { } diff --git a/libjava/classpath/javax/rmi/CORBA/ClassDesc.java b/libjava/classpath/javax/rmi/CORBA/ClassDesc.java index 01befa49339..c8b38ead095 100644 --- a/libjava/classpath/javax/rmi/CORBA/ClassDesc.java +++ b/libjava/classpath/javax/rmi/CORBA/ClassDesc.java @@ -1,5 +1,5 @@ -/* ClassDesc.java -- - Copyright (C) 2002 Free Software Foundation, Inc. +/* ClassDesc.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,16 +40,27 @@ package javax.rmi.CORBA; import java.io.Serializable; -public class ClassDesc - implements Serializable +/** + * This class is used to marshal java.lang.Class objects over IIOP. + * When used as a parameter type, return type, or data member, the Java Class + * is mapped to the OMG IDL type ::javax::rmi::CORBA::ClassDesc. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class ClassDesc implements Serializable { - /* - * The following is serialized form required by Java API Doc + /** + * Use serialVersionUID (V1.4) for interoperability. */ - private String repid; - private String codebase; + private static final long serialVersionUID = -3477057297839810709L; + + /** + * The class repository Id. + */ + String repid; - public ClassDesc() - { - } + /** + * Space separeted list of URL's from where the code can be downloaded. + */ + String codebase; } diff --git a/libjava/classpath/javax/rmi/CORBA/ObjectImpl.java b/libjava/classpath/javax/rmi/CORBA/ObjectImpl.java deleted file mode 100644 index d76d673cede..00000000000 --- a/libjava/classpath/javax/rmi/CORBA/ObjectImpl.java +++ /dev/null @@ -1,9 +0,0 @@ -package javax.rmi.CORBA; - -/** XXX - Stub till we have org.omg.CORBA */ -public class ObjectImpl -{ - public ObjectImpl _orb() { return null; } - public String object_to_string(ObjectImpl o) - throws javax.rmi.BAD_OPERATION { return null; } -} diff --git a/libjava/classpath/javax/rmi/CORBA/PortableRemoteObjectDelegate.java b/libjava/classpath/javax/rmi/CORBA/PortableRemoteObjectDelegate.java index 9ae45a33d04..2849707632b 100644 --- a/libjava/classpath/javax/rmi/CORBA/PortableRemoteObjectDelegate.java +++ b/libjava/classpath/javax/rmi/CORBA/PortableRemoteObjectDelegate.java @@ -1,5 +1,5 @@ /* PortableRemoteObjectDelegate.java -- Interface supporting PortableRemoteObject - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,23 +43,95 @@ import java.rmi.Remote; import java.rmi.RemoteException; /** - * A delegate is a singleton class that support delegation for method - * implementation in PortableRemoteObject. + * A delegate, implementing the functionality, provided by the + * {@link PortableRemoteObject}. + * + * The default delegate can be altered by setting the system property + * "javax.rmi.CORBA.PortableRemoteObjectClass" to the name of the alternative + * class that must implement {@link PortableRemoteObjectDelegate}. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) */ public interface PortableRemoteObjectDelegate { + /** + * <p> + * Makes the remote object <code>target</code> ready for remote + * communication using the same communications runtime as for the passed + * <code>source</code> parameter. Connection normally happens implicitly + * when the object is sent or received as an argument on a remote method call. + * </p> + * <p> + * The target object is connected to the same ORB as source by calling the + * {@link Stub#connect} if it is a stub or by associating its tie with an ORB + * if it is an implementation object. + * </p> + * + * @param target the target object that may be either an RMI/IDL stub or an + * exported RMI/IDL implementation object + * @param source the source object may also be either an RMI/IDL stub or an + * exported RMI/IDL implementation object. + * + * @throws RemoteException if the target is already connected to another ORB. + */ void connect(Remote target, Remote source) throws RemoteException; - + + /** + * Register the passed object with the ORB runtimes, making it remotely + * accessible. When called on jre with no objects exported, creates a + * non-daemon thread that prevents jre from terminating until all objects are + * unexported. Also, such object cannot be collected by garbage collector. + * This is usually impemented via {@link Util#unexportObject} + * + * @param object the object to export. + * + * @throws RemoteException + */ void exportObject(Remote obj) throws RemoteException; + /** + * Narrows the passed object to conform to the given interface or IDL type. + * This method may return different instance and cannot be replaced by the + * direct cast. + * + * @param narrowFrom an object to narrow. + * @param narrowTo a type to that the object must be narrowed. + * + * @return On success, an object of type narrowTo or null, if narrowFrom = + * null. + * + * @throws ClassCastException if no narrowing is possible. + */ Object narrow(Object narrowFrom, Class narrowTo) throws ClassCastException; - + + /** + * Takes a server implementation object and returns a stub object that can be + * used to access that server object (target). If the target is connected, the + * returned stub is also connected to the same ORB. If the target is + * unconnected, the returned stub is unconnected. + * + * @param target a server side object. + * @return a stub object that can be used to access that server object. + * + * @throws NoSuchObjectException if a stub cannot be located for the given + * target. + */ Remote toStub(Remote obj) throws NoSuchObjectException; - + + /** + * Deregister a currently exported server object from the ORB runtimes. The + * object to becomes available for garbage collection. This is usually + * impemented via {@link Util#unexportObject} + * + * @param object the object to unexport. + * + * @throws NoSuchObjectException if the passed object is not currently + * exported. + */ void unexportObject(Remote obj) throws NoSuchObjectException; } diff --git a/libjava/classpath/javax/rmi/CORBA/Stub.java b/libjava/classpath/javax/rmi/CORBA/Stub.java index a35a08fa9d3..190b10dad57 100644 --- a/libjava/classpath/javax/rmi/CORBA/Stub.java +++ b/libjava/classpath/javax/rmi/CORBA/Stub.java @@ -1,5 +1,5 @@ -/* Stub.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* Stub.java -- + Copyright (C) 2004, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,7 +39,7 @@ exception statement from your version. */ package javax.rmi.CORBA; import gnu.javax.rmi.CORBA.DelegateFactory; -import gnu.javax.rmi.CORBA.GetDelegateInstanceException; +import gnu.javax.rmi.CORBA.StubDelegateImpl; import java.io.IOException; import java.io.ObjectInputStream; @@ -47,7 +47,27 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.rmi.RemoteException; -public abstract class Stub extends ObjectImpl +import javax.rmi.PortableRemoteObject; + +import org.omg.CORBA.ORB; +import org.omg.CORBA_2_3.portable.ObjectImpl; + +/** + * A Stub descendants provide access to the object on the client side. This base + * class implements methods, required for remote or local invocation using CORBA + * mechanisms. The most of the functionality is forwarded to the stub delegate. + * This delegate can be altered by setting the system property + * "javax.rmi.CORBA.StubClass" to the name of the alternative class that must + * implement {@link StubDelegate}. Hence Stub contains two delegates, one for + * Stub-related operations and another inherited from the ObjectImpl. + * + * @specnote GNU Classpath uses separate delegate per each Stub. The delegate + * holds information about the ORB and other data, specific for the each Stub. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public abstract class Stub + extends ObjectImpl implements Serializable { /** @@ -55,69 +75,129 @@ public abstract class Stub extends ObjectImpl */ private static final long serialVersionUID = 1087775603798577179L; - private transient StubDelegate delegate; - - protected Stub() - { - try - { - delegate = (StubDelegate)DelegateFactory.getInstance("Stub"); - } - catch(GetDelegateInstanceException e) - { - delegate = null; - } - } + /** + * The hashcode, computed once (expensive operation). + */ + transient int m_hash = Integer.MIN_VALUE; + + /** + * The stringified reference, computed once (expensive operation). + */ + transient String m_ior; + + /** + * The ORB, where the stub is connected on the client side. + */ + transient ORB m_orb; + /** + * The associated delegate, responsible for the major of the functionality of + * this stub. + */ + static StubDelegate delegate = (StubDelegate) DelegateFactory.getInstance(DelegateFactory.STUB); + + /** + * Returns the same hashcode for all stubs that point to the same remote + * object. + */ public int hashCode() { - if(delegate != null) - return delegate.hashCode(this); - else - return 0; + if (m_hash == Integer.MIN_VALUE) + m_hash = delegate.hashCode(this); + // This should finally result to the IOR comparison. + return m_hash; } - public boolean equals(Object obj) + /** + * The stubs are equal if they point to the same remote object. + */ + public boolean equals(java.lang.Object obj) { - if(delegate != null) - return delegate.equals(this, obj); - else - return false; + return delegate.equals(this, obj); } + /** + * Get the string representation of this Stub. + * + * @return the CORBA IOR reference. + */ public String toString() { - String s = null; - if(delegate != null) - s = delegate.toString(this); - if(s == null) - s = super.toString(); - return s; + if (m_ior == null) + m_ior = delegate.toString(this); + return m_ior; } - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - public void connect(javax.rmi.ORB orb) + /** + * <p> + * Finds the suitable {@link Tie} for this Stub and connects it to the given + * ORB. The tie is found by the name pattern. If the found tie is derived from + * {@link org.omg.CORBA.PortableServer.Servant}, it is connected to the root + * POA, also activating it (if not already active). + * </p> + * <p> + * This method does not allow to specify, to which POA the found Tie must be + * connected and requires to use the deprecated method {@link ORB#connect}. + * Many useful POA features remain unaccessible. A better alternative it might + * be to generate a {@link org.omg.CORBA.PortableServer.Servant} - derived Tie + * (-poa key in rmic) and connect it to POA in one of the many ways, listed in + * the description of the {@link orb.omg.PortableServer} package). The + * obtained CORBA object can be narrowed into stub using + * {@link PortableRemoteObject#narrow}. + * </p> + * <p> + * It is frequently easier to call {@link PortableRemoteObject#connect} rather + * than this method. + * </p> + * + * @param orb the ORB where the Stub must be connected. + * + * @throws RemoteException if the stub is already connected to some other ORB. + * If the stub is already connected to the ORB that was passed as parameter, + * the method returns without action. + * + * @throws BAD_PARAM if the name of this stub does not match the stub name + * pattern, "_*_Stub" or if the Tie class, "_*Impl_Tie", does not exists or an + * instance of this class cannot be instantiated. + */ + public void connect(ORB orb) throws RemoteException { - if(delegate != null) - delegate.connect(this, orb); + if (m_orb != null && orb != null) + { + if (m_orb.equals(orb)) + throw new RemoteException("Stub " + this + + " is connected to another ORB, " + orb); + else + return; + } + m_orb = orb; + delegate.connect(this, orb); } /** - * The following two routines are required by serialized form of Java API doc. + * Required by serialized form of Java API doc. */ - private void readObject(ObjectInputStream stream) + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { - if(delegate != null) - delegate.readObject(this, stream); + if (delegate instanceof StubDelegateImpl) + ((StubDelegateImpl) delegate).readObject(this, input, m_orb); + else + delegate.readObject(this, input); } - private void writeObject(ObjectOutputStream stream) + /** + * Required by serialized form of Java API doc. + */ + private void writeObject(ObjectOutputStream output) throws IOException { - if(delegate != null) - delegate.writeObject(this, stream); - } + // The m_orb in this case may be either known or not. + if (delegate instanceof StubDelegateImpl) + ((StubDelegateImpl) delegate).writeObject(this, output, m_orb); + else -} + delegate.writeObject(this, output); + } +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/StubDelegate.java b/libjava/classpath/javax/rmi/CORBA/StubDelegate.java index 5c00a038f0c..cdf76e841c3 100644 --- a/libjava/classpath/javax/rmi/CORBA/StubDelegate.java +++ b/libjava/classpath/javax/rmi/CORBA/StubDelegate.java @@ -1,5 +1,5 @@ -/* StubDelegate.java -- - Copyright (C) 2002 Free Software Foundation, Inc. +/* StubDelegate.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,28 +38,66 @@ exception statement from your version. */ package javax.rmi.CORBA; +import org.omg.CORBA.ORB; + import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; + import java.rmi.RemoteException; -//import org.omg.CORBA.ORB; +/** + * A delegate, implementing the functionality, provided by the {@link Stub}. + * The default delegate can be altered by setting the system property + * "javax.rmi.CORBA.StubClass" to the name of the alternative class that must + * implement StubDelegate. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ public interface StubDelegate { - - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - void connect(Stub self, javax.rmi.ORB orb) + /** + * <p> + * Makes the stub ready for remote communication using the given ORB. + * </p> + * <p> + * It is frequently easier to call {@link PortableRemoteObject#connect} rather + * than this method. + * </p> + * + * @param orb the ORB where the Stub must be connected. + * + * @throws RemoteException if the stub is already connected to some other ORB. + * If the stub is already connected to the ORB that was passed as parameter, + * the method returns without action. + */ + void connect(Stub self, ORB orb) throws RemoteException; + /** + * The objects stubs are equal if they refer the same remote object. + */ boolean equals(Stub self, Object obj); + /** + * Get the hashcode fo this delegate. + */ int hashCode(Stub self); + /** + * Read this stub from the object input stream. + */ void readObject(Stub self, ObjectInputStream s) throws IOException, ClassNotFoundException; - String toString(Stub self); - + /** + * Write this stub to the object output stream. + */ void writeObject(Stub self, ObjectOutputStream s) throws IOException; -} + + /** + * Get the string representation of this stub. + */ + String toString(Stub self); +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/SystemException.java b/libjava/classpath/javax/rmi/CORBA/SystemException.java deleted file mode 100644 index f8afdc35e35..00000000000 --- a/libjava/classpath/javax/rmi/CORBA/SystemException.java +++ /dev/null @@ -1,4 +0,0 @@ -package javax.rmi.CORBA; - -/** XXX - Stub till we have org.omg.CORBA */ -public class SystemException extends Exception { } diff --git a/libjava/classpath/javax/rmi/CORBA/Tie.java b/libjava/classpath/javax/rmi/CORBA/Tie.java index 4b1984c58e9..b8611838822 100644 --- a/libjava/classpath/javax/rmi/CORBA/Tie.java +++ b/libjava/classpath/javax/rmi/CORBA/Tie.java @@ -1,5 +1,5 @@ -/* Tie.java -- - Copyright (C) 2002 Free Software Foundation, Inc. +/* Tie.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -35,28 +35,91 @@ 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 javax.rmi.CORBA; +import java.rmi.NoSuchObjectException; import java.rmi.Remote; -//import org.omg.CORBA.ORB; -//import org.omg.CORBA.portable.InvokeHandler; +import org.omg.CORBA.ORB; +import org.omg.CORBA.portable.InvokeHandler; -public interface Tie // XXX extends InvokeHandler +/** + * <p> + * A Tie serves as a CORBA Servant or implementation base. It is connected to + * the ORB on a server side, providing the implementation of the required + * functionality. ORB access this implementation using {@link InvokeHandler} + * ._invoke(..). All such calls are finally delegated to the object, returned by + * {@link #getTarget()}. + * </p> + * <p> + * Ties are generated from implementations (name pattern *Impl) and have the + * name pattern _*Impl_Tie, for instance:<br> + * <code>rmic -keep -iiop -poa -always gnu.testlet.java.rmi.Remote.CalculatorImpl</code> + * </p> + * <p> + * Ties should normally be derived from the + * {@link org.omg.PortableServer.Servant}. Such ties are generated by + * <code>rmic</code> compiler using <code>-poa</code> key. Ties can be also + * derived from {@link org.omg.CORBA_2_3.portable.ObjectImpl}. + * </p> + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public interface Tie + extends InvokeHandler { - - void deactivate(); - + /** + * Get the invocation target, where all method calls should be delegated. + * + * @return the object, implementing methods, defined in the interface being + * served by this Tie. + */ Remote getTarget(); - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - javax.rmi.ORB orb(); - - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - void orb(javax.rmi.ORB orb); - + /** + * Set the invocation target, where all method calls should be delegated. + * + * @param target the object, implementing methods, defined in the interface + * being served by this Tie. The code, produced by a typical rmic compiler + * usually requires the target to be an instance of the implementation from + * that the Tie was generated. + * + * @throws ClassCastException if the passed parameter is not an instance of + * the implementation from that the Tie was generated. + */ void setTarget(Remote target); - - // XXX Object -> org.omg.CORBA.Object - Object thisObject(); -} + + /** + * Get the ORB to that this Tie is connected. + * + * @see org.omg.PortableServer.Servant#_orb + */ + ORB orb(); + + /** + * Connect this Tie to the given ORB. + */ + void orb(ORB orb); + + /** + * Get the object that delegates calls to this tie. + * + * @see org.omg.PortableServer.Servant#_this_object + */ + org.omg.CORBA.Object thisObject(); + + /** + * Deactivate this Tie. The tie is normally deactivated using POA mechanisms. + * Depending on the POA policies, it may be possible to activate the Tie + * again. The ties that are not derived from + * {@link org.omg.PortableServer.Servant} deactivate themselves by + * {@link ORB.disconnect}. + * + * @throws NoSuchObjectException if there are no objects served by this Tie, + * or if the these objects are already deactivated. + * + * @see org.omg.PortableServer.POA#deactivate_object + */ + void deactivate() + throws NoSuchObjectException; + +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/Util.java b/libjava/classpath/javax/rmi/CORBA/Util.java index 0370cce83a2..cfd3d7bd686 100644 --- a/libjava/classpath/javax/rmi/CORBA/Util.java +++ b/libjava/classpath/javax/rmi/CORBA/Util.java @@ -1,5 +1,5 @@ -/* Util.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* Util.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,148 +38,379 @@ exception statement from your version. */ package javax.rmi.CORBA; +import org.omg.CORBA.Any; +import org.omg.CORBA.BAD_PARAM; +import org.omg.CORBA.COMM_FAILURE; +import org.omg.CORBA.CompletionStatus; +import org.omg.CORBA.INVALID_TRANSACTION; +import org.omg.CORBA.INV_OBJREF; +import org.omg.CORBA.MARSHAL; +import org.omg.CORBA.NO_PERMISSION; +import org.omg.CORBA.OBJECT_NOT_EXIST; +import org.omg.CORBA.OMGVMCID; +import org.omg.CORBA.ORB; +import org.omg.CORBA.SystemException; +import org.omg.CORBA.TRANSACTION_REQUIRED; +import org.omg.CORBA.TRANSACTION_ROLLEDBACK; +import org.omg.CORBA.TypeCode; +import org.omg.CORBA.portable.InputStream; +import org.omg.CORBA.portable.OutputStream; + import gnu.javax.rmi.CORBA.DelegateFactory; -import gnu.javax.rmi.CORBA.GetDelegateInstanceException; -import java.io.InputStream; -import java.io.OutputStream; +import java.rmi.AccessException; +import java.rmi.MarshalException; +import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; +import java.rmi.ServerError; +import java.rmi.ServerException; +import java.rmi.UnexpectedException; +import java.rmi.server.RMIClassLoader; + +import javax.transaction.InvalidTransactionException; +import javax.transaction.TransactionRequiredException; +import javax.transaction.TransactionRolledbackException; +/** + * Provides utility methods used by stubs and ties to perform common operations. + * The functionality is forwarded to the enclosed UtilDelegate. This delegate + * can be altered by setting the system property "javax.rmi.CORBA.UtilClass" to + * the name of the alternative class that must implement {@link UtilDelegate}. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ public class Util { + /** + * The delegate, responsible for all functionality. + */ + static UtilDelegate delegate = (UtilDelegate) DelegateFactory.getInstance(DelegateFactory.UTIL); - private static UtilDelegate delegate; - static - { - try - { - delegate = (UtilDelegate)DelegateFactory.getInstance("Util"); - } - catch(GetDelegateInstanceException e) - { - delegate = null; - } - } - + /** + * Prevents this class from being instantiated. + */ private Util() { } - // XXX - javax.rmi.ORB -> org.omg.CORBA.ORB - public static Object copyObject(Object obj, javax.rmi.ORB orb) + /** + * Used by local stubs to create a copy of the object. The object must be + * Serializable for this operation to succeed. Strings are not copied and + * 1D-3D string arrays are only cloned. + */ + public static java.lang.Object copyObject(java.lang.Object object, ORB orb) throws RemoteException { - if(delegate != null) - return delegate.copyObject(obj, orb); - else - return null; + return delegate.copyObject(object, orb); } - // XXX - javax.rmi.ORB -> org.omg.CORBA.ORB - public static Object[] copyObjects(Object obj[], javax.rmi.ORB orb) + /** + * Used by local stubs to create a multiple copies of the object, preserving + * sharing accross the parameters if necessary. + */ + public static java.lang.Object[] copyObjects(java.lang.Object[] object, + ORB orb) throws RemoteException { - if(delegate != null) - return delegate.copyObjects(obj, orb); - else - return null; + return delegate.copyObjects(object, orb); } - + + /** + * Get the value handler that Serializes Java objects to and from CDR (GIOP) + * streams. + * + * When using the default Util implementation, the class of the returned + * handler can be altered by setting by setting the system property + * "javax.rmi.CORBA.ValueHandlerClass" to the name of the alternative class + * that must implement {@link ValueHandler}. + */ public static ValueHandler createValueHandler() { - if(delegate != null) - return delegate.createValueHandler(); - else - return null; + return delegate.createValueHandler(); } - + + /** + * This call is finally delegated to {@link RMIClassLoader#getClassAnnotation}; + */ public static String getCodebase(Class clz) { - if(delegate != null) - return delegate.getCodebase(clz); - else - return null; + return delegate.getCodebase(clz); } - + + /** + * Get the Tie that handles invocations on the given target. If the target/Tie + * pair has not been previously registered using {@link #registerTarget}, + * this method tries to locate a tie class by the name pattern. If this + * succeeds, the tie-target pair is also registered. + * + * @return the Tie. + */ public static Tie getTie(Remote target) { - if(delegate != null) - return delegate.getTie(target); - else - return null; + return delegate.getTie(target); } + /** + * Checks if the given stub is local. The implementation it delegates call to + * {@link ObjectImpl#_is_local(). + * + * @param stub a stub to check. + * @return true if the stub is local, false otherwise. + * + * @throws RemoteException if the {@link ObjectImpl#_is_local()} throws a + * {@link org.omg.CORBA.SystemException}. + */ public static boolean isLocal(Stub stub) throws RemoteException { - if(delegate != null) - return delegate.isLocal(stub); - else - return false; + return delegate.isLocal(stub); } - public static Class loadClass(String className, String remoteCodebase, ClassLoader loader) + /** + * Load the class. The method uses class loaders from the call stact first. If + * this fails, the further behaviour depends on the System Property + * "java.rmi.server.useCodebaseOnly" with default value "false". + * + * <ul> + * <li>If remoteCodebase is non-null and useCodebaseOnly is "false" then call + * java.rmi.server.RMIClassLoader.loadClass (remoteCodebase, className)</li> + * <li> If remoteCodebase is null or useCodebaseOnly is true then call + * java.rmi.server.RMIClassLoader.loadClass(className)</li> + * <li>If a class is still not successfully loaded and the loader != null + * then try Class.forName(className, false, loader). </li> + * </ul> + * + * @param className the name of the class. + * @param remoteCodebase the codebase. + * @param loader the class loader. + * @return the loaded class. + * + * @throws ClassNotFoundException of the class cannot be loaded. + */ + public static Class loadClass(String className, String remoteCodebase, + ClassLoader loader) throws ClassNotFoundException { - if(delegate != null) - return delegate.loadClass(className, remoteCodebase, loader); - else - throw new ClassNotFoundException(className + ": delegate == null"); - } - - public static RemoteException mapSystemException(SystemException ex) - { - if(delegate != null) - return delegate.mapSystemException(ex); - else - return null; + return delegate.loadClass(className, remoteCodebase, loader); } - public static Object readAny(InputStream in) + /** + * Converts CORBA {@link SystemException} into RMI {@link RemoteException}. + * The exception is converted as defined in the following table: + * <p> + * <table border = "1"> + * <tr> + * <th>CORBA Exception</th> + * <th>RMI Exception</th> + * </tr> + * <tr> + * <td>{@link COMM_FAILURE}</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link INV_OBJREF}</td> + * <td>{@link NoSuchObjectException}</td> + * </tr> + * <tr> + * <td>{@link NO_PERMISSION}</td> + * <td>{@link AccessException}</td> + * </tr> + * <tr> + * <td>{@link MARSHAL}</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link BAD_PARAM} (all other cases)</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link OBJECT_NOT_EXIST}</td> + * <td>{@link NoSuchObjectException}</td> + * </tr> + * <tr> + * <td>{@link TRANSACTION_REQUIRED}</td> + * <td>{@link TransactionRequiredException}</td> + * </tr> + * <tr> + * <td>{@link TRANSACTION_ROLLEDBACK}</td> + * <td>{@link TransactionRolledbackException}</td> + * </tr> + * <tr> + * <td>{@link INVALID_TRANSACTION}</td> + * <td>{@link InvalidTransactionException}</td> + * </tr> + * <tr> + * <td bgcolor="lightgray">Any other {@link SystemException}</td> + * <td bgcolor="lightgray">{@link RemoteException}</td> + * </tr> + * </table> + * </p> + * <p> + * The exception detailed message always consists of + * <ol> + * <li>the string "CORBA "</li> + * <li>the CORBA name of the system exception</li> + * <li>single space</li> + * <li>the hexadecimal value of the system exception's minor code, preceeded + * by 0x (higher bits contain {@link OMGVMCID}).</li> + * <li>single space</li> + * <li>the {@link CompletionStatus} of the exception: "Yes", "No" or "Maybe".</li> + * </ol> + * The subsequent content is not part of the official RMI-IIOP standart and is + * added for compatibility with Sun's implementation: + * <ol> + * <li>the phrase "<code>; nested exception is: <i>(line feed)(tab)</i></code>"</li> + * <li>the full name of the mapped SystemException, as returned by + * Class.getName().</li> + * <li>the ": ". + * <li>the value, returned by .getMessage() of the passed parameter.</li> + * </ol> + * <p> + * For instance, if the Internet connection was refused: + * </p><p> + * <code>CORBA COMM_FAILURE 0x535500C9 No</code> + * </p><p> + * The original CORBA exception is set as the cause of the RemoteException + * being created. + * </p> + */ + public static RemoteException mapSystemException(SystemException ex) { - if(delegate != null) - return delegate.readAny(in); - else - return null; + return delegate.mapSystemException(ex); } + /** + * Register the Tie-target pair. As the Tie is a Servant, it can potentially + * be connected to several objects and hence may be registered with several + * targets. + */ public static void registerTarget(Tie tie, Remote target) { - if(delegate != null) - delegate.registerTarget(tie, target); + delegate.registerTarget(tie, target); } - + + /** + * Deactivate the associated Tie, if it is found and is not connected to other + * registered targets. Independing from the POA policies, the transparent + * reactivation will not be possible. + */ public static void unexportObject(Remote target) + throws NoSuchObjectException { - if(delegate != null) - delegate.unexportObject(target); + delegate.unexportObject(target); } - - public static RemoteException wrapException(Throwable orig) + + /** + * Converts the exception that was thrown by the implementation method on a + * server side into RemoteException that can be transferred and re-thrown on a + * client side. The method converts exceptions as defined in the following + * table: <table border = "1"> + * <tr> + * <th>Exception to map (or subclass)</th> + * <th>Maps into</th> + * </tr> + * <tr> + * <td>{@link Error}</td> + * <td>{@link ServerError}</td> + * </tr> + * <tr> + * <td>{@link RemoteException}</td> + * <td>{@link ServerException}</td> + * </tr> + * <tr> + * <td>{@link SystemException}</td> + * <td>wrapException({@link #mapSystemException})</td> + * </tr> + * <tr> + * <td>{@link RuntimeException}</td> + * <td><b>rethrows</b></td> + * </tr> + * <tr> + * <td>Any other exception</td> + * <td>{@link UnexpectedException}</td> + * </tr> + * </table> + * + * @param ex an exception that was thrown on a server side implementation. + * + * @return the corresponding RemoteException unless it is a RuntimeException. + * + * @throws RuntimeException the passed exception if it is an instance of + * RuntimeException. + * + * @specnote It is the same behavior, as in Suns implementations 1.4.0-1.5.0. + */ + public static RemoteException wrapException(Throwable exception) { - if(delegate != null) - return delegate.wrapException(orig); - else - return null; + return delegate.wrapException(exception); } - - public static void writeAbstractObject(OutputStream out, Object obj) + + /** + * Write abstract interface to the CORBA output stream. The write format is + * matching CORBA abstract interface. Remotes and CORBA objects are written as + * objects, other classes are supposed to be value types and are written as + * such. {@link Remote}s are processed as defined in + * {@link #writeRemoteObject}. The written data contains discriminator, + * defining, that was written. Another method that writes the same content is + * {@link org.omg.CORBA_2_3.portable.OutputStream#write_abstract_interface(java.lang.Object)}. + * + * @param output a stream to write to, must be + * {@link org.omg.CORBA_2_3.portable.OutputStream}. + * + * @param object an object to write, must be CORBA object, Remote + */ + public static void writeAbstractObject(OutputStream output, + java.lang.Object object) { - if(delegate != null) - delegate.writeAbstractObject(out, obj); + delegate.writeAbstractObject(output, object); } - - public static void writeAny(OutputStream out, Object obj) + + /** + * Write the passed java object to the output stream in the form of the CORBA + * {@link Any}. This includes creating an writing the object {@link TypeCode} + * first. Such Any can be later read by a non-RMI-IIOP CORBA implementation + * and manipulated, for instance, by means, provided in + * {@link org.omg.DynamicAny.DynAny}. Depending from the passed value, this + * method writes CORBA object, value type or value box. For value types Null + * is written with the abstract interface, its typecode having repository id + * "IDL:omg.org/CORBA/AbstractBase:1.0" and the empty string name. + * + * @param output the object to write. + * @param object the java object that must be written in the form of the CORBA + * {@link Any}. + */ + public static void writeAny(OutputStream output, java.lang.Object object) { - if(delegate != null) - delegate.writeAny(out, obj); + delegate.writeAny(output, object); } - - public static void writeRemoteObject(OutputStream out, Object obj) + + /** + * Read Any from the input stream. + */ + public static java.lang.Object readAny(InputStream input) { - if(delegate != null) - delegate.writeRemoteObject(out, obj); + return delegate.readAny(input); } -} + /** + * Write the passed parameter to the output stream as CORBA object. If the + * parameter is an instance of Remote and not an instance of Stub, the method + * instantiates a suitable Tie, connects the parameter to this Tie and then + * connects that Tie to the ORB that is requested from the output stream. Then + * the object reference is written to the stream, making remote invocations + * possible (the ORB is started and activated, if required). This method is + * used in write_value(..) method group in + * {@link org.omg.CORBA_2_3.portable.OutputStream} and also may be called + * directly from generated Stubs and Ties. + * + * @param output a stream to write to, must be + * org.omg.CORBA_2_3.portable.OutputStream + * @param object an object to write. + */ + public static void writeRemoteObject(OutputStream output, + java.lang.Object object) + { + delegate.writeRemoteObject(output, object); + } +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/UtilDelegate.java b/libjava/classpath/javax/rmi/CORBA/UtilDelegate.java index deeb94f68b5..cacb925810b 100644 --- a/libjava/classpath/javax/rmi/CORBA/UtilDelegate.java +++ b/libjava/classpath/javax/rmi/CORBA/UtilDelegate.java @@ -1,5 +1,5 @@ -/* UtilDelegate.java -- - Copyright (C) 2002 Free Software Foundation, Inc. +/* UtilDelegate.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,48 +38,273 @@ exception statement from your version. */ package javax.rmi.CORBA; -import java.io.InputStream; -import java.io.OutputStream; +import org.omg.CORBA.Any; +import org.omg.CORBA.BAD_PARAM; +import org.omg.CORBA.COMM_FAILURE; +import org.omg.CORBA.CompletionStatus; +import org.omg.CORBA.INVALID_TRANSACTION; +import org.omg.CORBA.INV_OBJREF; +import org.omg.CORBA.MARSHAL; +import org.omg.CORBA.NO_PERMISSION; +import org.omg.CORBA.OBJECT_NOT_EXIST; +import org.omg.CORBA.OMGVMCID; +import org.omg.CORBA.ORB; +import org.omg.CORBA.SystemException; +import org.omg.CORBA.TRANSACTION_REQUIRED; +import org.omg.CORBA.TRANSACTION_ROLLEDBACK; +import org.omg.CORBA.TypeCode; +import org.omg.CORBA.portable.InputStream; +import org.omg.CORBA.portable.OutputStream; + +import java.rmi.AccessException; +import java.rmi.MarshalException; +import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; -//import org.omg.CORBA.ORB; -//import org.omg.CORBA.SystemException; -//import org.omg.CORBA.portable.InputStream; -//import org.omg.CORBA.portable.OutputStream; +import java.rmi.ServerError; +import java.rmi.ServerException; +import java.rmi.UnexpectedException; + +import javax.transaction.InvalidTransactionException; +import javax.transaction.TransactionRequiredException; +import javax.transaction.TransactionRolledbackException; +/** + * A delegate, implementing the functionality, provided by the {@link Util}. + * + * The default delegate can be altered by setting the system property + * "javax.rmi.CORBA.UtilClass" to the name of the alternative class that must + * implement this interface. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ public interface UtilDelegate { + /** + * Used by local stubs to create a copy of the object. + */ + Object copyObject(Object obj, ORB orb) + throws RemoteException; - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - Object copyObject(Object obj, javax.rmi.ORB orb) throws RemoteException; - - // XXX javax.rmi.ORB -> org.omg.CORBA.ORB - Object[] copyObjects(Object obj[], javax.rmi.ORB orb) throws RemoteException; + /** + * Used by local stubs to create a multiple copies of the object, preserving + * sharing accross the parameters if necessary. + */ + Object[] copyObjects(Object[] obj, ORB orb) + throws RemoteException; + /** + * Get the value handler that Serializes Java objects to and from CDR (GIOP) + * streams. + */ ValueHandler createValueHandler(); - + String getCodebase(Class clz); - - Tie getTie(Remote target); - - boolean isLocal(Stub stub) throws RemoteException; - Class loadClass(String className, String remoteCodebase, - ClassLoader loader) throws ClassNotFoundException; + /** + * Checks if the given stub is local. + */ + boolean isLocal(Stub stub) + throws RemoteException; + + Class loadClass(String className, String remoteCodebase, ClassLoader loader) + throws ClassNotFoundException; + /** + * Converts CORBA {@link SystemException} into RMI {@link RemoteException}. + * The exception is converted as defined in the following table: + * <p> + * <table border = "1"> + * <tr> + * <th>CORBA Exception</th> + * <th>RMI Exception</th> + * </tr> + * <tr> + * <td>{@link COMM_FAILURE}</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link INV_OBJREF}</td> + * <td>{@link NoSuchObjectException}</td> + * </tr> + * <tr> + * <td>{@link NO_PERMISSION}</td> + * <td>{@link AccessException}</td> + * </tr> + * <tr> + * <td>{@link MARSHAL}</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link BAD_PARAM} (all other cases)</td> + * <td>{@link MarshalException}</td> + * </tr> + * <tr> + * <td>{@link OBJECT_NOT_EXIST}</td> + * <td>{@link NoSuchObjectException}</td> + * </tr> + * <tr> + * <td>{@link TRANSACTION_REQUIRED}</td> + * <td>{@link TransactionRequiredException}</td> + * </tr> + * <tr> + * <td>{@link TRANSACTION_ROLLEDBACK}</td> + * <td>{@link TransactionRolledbackException}</td> + * </tr> + * <tr> + * <td>{@link INVALID_TRANSACTION}</td> + * <td>{@link InvalidTransactionException}</td> + * </tr> + * <tr> + * <td bgcolor="lightgray">Any other {@link SystemException}</td> + * <td bgcolor="lightgray">{@link RemoteException}</td> + * </tr> + * </table> + * </p> + * <p> + * The exception detailed message always consists of + * <ol> + * <li>the string "CORBA "</li> + * <li>the CORBA name of the system exception</li> + * <li>single space</li> + * <li>the hexadecimal value of the system exception's minor code, preceeded + * by 0x (higher bits contain {@link OMGVMCID}).</li> + * <li>single space</li> + * <li>the {@link CompletionStatus} of the exception: "Yes", "No" or "Maybe".</li> + * </ol> + * The subsequent content is not part of the official RMI-IIOP standart and is + * added for compatibility with Sun's implementation: + * <ol> + * <li>the phrase "<code>; nested exception is: <i>(line feed)(tab)</i></code>"</li> + * <li>the full name of the mapped SystemException, as returned by + * Class.getName().</li> + * <li>the ": ". + * <li>the value, returned by .getMessage() of the passed parameter.</li> + * </ol> + * <p> + * For instance, if the Internet connection was refused: + * </p><p> + * <code>CORBA COMM_FAILURE 0x535500C9 No</code> + * </p><p> + * The original CORBA exception is set as the cause of the RemoteException + * being created. + * </p> + */ RemoteException mapSystemException(SystemException ex); - Object readAny(InputStream in); + /** + * Get the Tie that handles invocations on the given target. The target/Tie + * pair must be previously registered using {@link #registerTarget}. + * + * @return the Tie, or null if no such is known. + */ + Tie getTie(Remote target); + /** + * Register the Tie-target pair. + */ void registerTarget(Tie tie, Remote target); - - void unexportObject(Remote target); - + + /** + * Deactivate the associated Tie, if it is found and is not connected to other + * registered targets. + */ + void unexportObject(Remote target) + throws NoSuchObjectException; + + /** + * Converts the exception that was thrown by the implementation method on a + * server side into RemoteException that can be transferred and re-thrown on a + * client side. The method converts exceptions as defined in the following + * table: <table border = "1"> + * <tr> + * <th>Exception to map (or subclass)</th> + * <th>Maps into</th> + * </tr> + * <tr> + * <td>{@link Error}</td> + * <td>{@link ServerError}</td> + * </tr> + * <tr> + * <td>{@link RemoteException}</td> + * <td>{@link ServerException}</td> + * </tr> + * <tr> + * <td>{@link SystemException}</td> + * <td>wrapException({@link #mapSystemException})</td> + * </tr> + * <tr> + * <td>{@link RuntimeException}</td> + * <td><b>rethrows</b></td> + * </tr> + * <tr> + * <td>Any other exception</td> + * <td>{@link UnexpectedException}</td> + * </tr> + * </table> + * + * @param ex an exception that was thrown on a server side implementation. + * + * @return the corresponding RemoteException unless it is a RuntimeException. + * + * @throws RuntimeException the passed exception if it is an instance of + * RuntimeException. + * + * @specnote It is the same behavior, as in Suns implementations 1.4.0-1.5.0. + */ RemoteException wrapException(Throwable orig); - - void writeAbstractObject(OutputStream out, Object obj); - - void writeAny(OutputStream out, Object obj); - void writeRemoteObject(OutputStream out, Object obj); -} + /** + * Write the passed parameter to the output stream as CORBA object. If the + * parameter is an instance of Remote and not an instance of Stub, the method + * instantiates a suitable Tie, connects the parameter to this Tie and then + * connects that Tie to the ORB that is requested from the output stream. Then + * the object reference is written to the stream, making remote invocations + * possible. This method is used in write_value(..) method group in + * {@link org.omg.CORBA_2_3.portable.OutputStream} and also may be called + * directly from generated Stubs and Ties. + * + * @param output a stream to write to, must be + * org.omg.CORBA_2_3.portable.OutputStream + * @param object an object to write. + */ + void writeRemoteObject(OutputStream output, Object obj); + + /** + * Write abstract interface to the CORBA output stream. The write format is + * matching CORBA abstract interface. Remotes and CORBA objects are written as + * objects, other classes are supposed to be value types and are written as + * such. {@link Remote}s are processed as defined in + * {@link #writeRemoteObject}. The written data contains discriminator, + * defining, that was written. Another method that writes the same content is + * {@link org.omg.CORBA_2_3.portable.OutputStream#write_abstract_interface(java.lang.Object)}. + * + * @param output a stream to write to, must be + * {@link org.omg.CORBA_2_3.portable.OutputStream}. + * + * @param object an object to write, must be CORBA object, Remote + */ + void writeAbstractObject(OutputStream output, Object object); + + /** + * Write the passed java object to the output stream in the form of the CORBA + * {@link Any}. This includes creating an writing the object {@link TypeCode} + * first. Such Any can be later read by a non-RMI-IIOP CORBA implementation + * and manipulated, for instance, by means, provided in + * {@link org.omg.DynamicAny.DynAny}. Depending from the passed value, this + * method writes CORBA object, value type or value box. For value types Null + * is written with the abstract interface, its typecode having repository id + * "IDL:omg.org/CORBA/AbstractBase:1.0" and the empty string name. + * + * @param output the object to write. + * @param object the java object that must be written in the form of the CORBA + * {@link Any}. + */ + void writeAny(OutputStream output, Object object); + + /** + * Read Any from the input stream. + */ + Object readAny(InputStream input); + +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/ValueHandler.java b/libjava/classpath/javax/rmi/CORBA/ValueHandler.java index f26e75b2a86..c4213efdcfb 100644 --- a/libjava/classpath/javax/rmi/CORBA/ValueHandler.java +++ b/libjava/classpath/javax/rmi/CORBA/ValueHandler.java @@ -1,5 +1,5 @@ -/* ValueHandler.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* ValueHandler.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,28 +38,93 @@ exception statement from your version. */ package javax.rmi.CORBA; -import java.io.InputStream; -import java.io.OutputStream; import java.io.Serializable; -//import org.omg.CORBA.portable.InputStream; -//import org.omg.CORBA.portable.OutputStream; -//import org.omg.SendingContext.RunTime; +import org.omg.CORBA.CustomMarshal; +import org.omg.CORBA.portable.InputStream; +import org.omg.CORBA.portable.OutputStream; +import org.omg.CORBA.portable.Streamable; +import org.omg.SendingContext.RunTime; + +/** + * Serializes Java objects to and from CDR (GIOP) streams. The working instance + * of the value handler is returned by {@link Util#createValueHandler} and can + * be altered by setting the system property "javax.rmi.CORBA.ValueHandlerClass" + * to the name of the alternative class that must implement ValueHandler. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ public interface ValueHandler { - + /** + * Get CORBA repository Id for the given java class. + * + * The syntax of the repository ID is the initial ?RMI:?, followed by the Java + * class name, followed by name, followed by a hash code string, followed + * optionally by a serialization version UID string. + * + * For Java identifiers that contain illegal OMG IDL identifier characters + * such as ?$?, any such illegal characters are replaced by ?\U? followed by + * the 4 hexadecimal characters (in upper case) representing the Unicode + * value. + * + * @param clz a class for that the repository Id is required. + * + * @return the class repository id. + */ String getRMIRepositoryID(Class clz); - - // XXX Runtime -> RunTime - Runtime getRunTimeCodeBase(); - + + /** + * Returns the CodeBase for this ValueHandler. + * + * @return the codebase. + */ + RunTime getRunTimeCodeBase(); + + /** + * Indicates that the given class is responsible itself for writing its + * content to the stream. Such classes implement either {@link Streamable} + * (default marshalling, generated by IDL-to-java compiler) or + * {@link CustomMarshal} (the user-programmed marshalling). + * + * @param clz the class being checked. + * @return true if the class supports custom or default marshalling, false + * otherwise. + */ boolean isCustomMarshaled(Class clz); - - // XXX Runtime -> RunTime + + /** + * Read value from the CORBA input stream in the case when the value is not + * Streamable or CustomMarshall'ed. The fields of the class being written will + * be accessed using reflection. + * + * @param in a CORBA stream to read. + * @param offset the current position in the input stream. + * @param clz the type of value being read. + * @param repositoryID the repository Id of the value being read. + * @param sender the sending context that should provide data about the + * message originator. + * + * @return the object, extracted from the stream. + */ Serializable readValue(InputStream in, int offset, Class clz, - String repositoryID, Runtime sender); - + String repositoryID, RunTime sender); + + /** + * When the value provides the writeReplace method, the result of this method + * is written. Otherwise, the value itself is written. + * + * @param the value that should be written to the stream. + * + * @return the value that will be actually written to the stream. + */ Serializable writeReplace(Serializable value); - + + /** + * Write value to CORBA output stream using java senmatics. + * + * @param out a stream to write into. + * @param value a java object to write. + */ void writeValue(OutputStream out, Serializable value); -} +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/CORBA/ValueHandlerMultiFormat.java b/libjava/classpath/javax/rmi/CORBA/ValueHandlerMultiFormat.java new file mode 100644 index 00000000000..4db65c1dbfd --- /dev/null +++ b/libjava/classpath/javax/rmi/CORBA/ValueHandlerMultiFormat.java @@ -0,0 +1,94 @@ +/* ValueHandlerMultiFormat.java -- + Copyright (C) 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 javax.rmi.CORBA; + +import org.omg.CORBA.portable.OutputStream; + +import java.io.Serializable; + +/** + * This interface extends the previous ValueHandler, supporting various stream + * format versions. The {@link ValueHandler} can be casted into this interface + * to access additional features. + * + * @since 1.5 + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public interface ValueHandlerMultiFormat + extends ValueHandler +{ + /** + * Get the maximal supported version for the value types, supported by + * this value handler. The versions are integer numbers, the currently valid + * values being 1 and 2. + * + * These two versions differ in how the additional data, stored by the + * writeObject method, are encoded. + * <ul> + * <li> For version 1 (GNU Classpath default), that data (if present) are + * written "as is". </li> + * <li>For version 2, this data fragment is enclosed within a CDR custom + * valuetype with no codebase and repository Id "RMI:org.omg.custom.<class>" + * where <class> is the fully-qualified name of the class whose writeObject + * method is being invoked. If the object does not write any data via + * writeObject method, the null valuetype (0x0) must be written.</li> + * </ul> + * As the version number is part of the value type record, there is no need + * to the format control during the reading. + * + * @return the maximal supported version. + */ + byte getMaximumStreamFormatVersion(); + + /** + * Write the value type to the output stream using the given format version. + * The older method {@link ValueHandler#writeValue} always uses the version 1. + * + * @param output the stream, where the value should be written, must implement + * {@link ValueOutputStream}. + * @param value the value that should be written. + * @param version the version of the format that must be used to write the + * value. + * + * @throws BAD_PARAM if the version number is less than 1 or greater than the + * maximal supported version. + */ + void writeValue(OutputStream output, Serializable value, byte version); +} diff --git a/libjava/classpath/javax/rmi/CORBA/package.html b/libjava/classpath/javax/rmi/CORBA/package.html index f5a5ac758f9..6014c8c8533 100644 --- a/libjava/classpath/javax/rmi/CORBA/package.html +++ b/libjava/classpath/javax/rmi/CORBA/package.html @@ -1,5 +1,5 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> -<!-- package.html - describes classes in javax.rmi.CORBA package. +<!-- package.html - describes classes in javax.rmi package. Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,7 +40,37 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.rmi.CORBA</title></head> <body> -<p></p> - +<p> +Java RMI over IIOP combines RMI technology with CORBA technology. Like plain RMI, +RMI over IIOP allows to work completely in the Java programming language +(no IDL). When CORBA needs a separate helper class for each structure being +passed, RMI over IIOP only needs stubs and ties for the objects that are remotely +accessible. As a result, development with RMI-IIOP is easier. However the +specialised pure CORBA helpers needs no reflection to transfer they structures +and hence may be faster than methods, used by both RMI-IIOP and plain RMI. +</p><p> +Like RMI, RMI over IIOP provides flexibility by allowing to pass any serializable +Java object (Objects By Value) between application components. A certain +"imaginary IDL" is automatically supposed; this IDL can be explicitly generated +and later used to interoperate with non-java application. +</p><p> +Like CORBA, RMI over IIOP is based on open standards defined with the +participation of hundredsof vendors and users in the OMG. It uses IIOP +communication protocol that provides much better interoperability with other +programming languages. +</p><p> +With RMI/IIOP you can use advanced CORBA features: multiple objects per servant +and servants per object, servant activators and locators, servant, client and +ior interceptors, CORBA naming service, various ORB policies, stringified object +references and so on. This functionality is based on CORBA value type standard. +RMI/IIOP supports (and GNU Classpath implements) transferring of the arbitrary +connected object graphs (graph flattenning). +</p><p> +GNU Classpath RMI-IIOP functionality is implemented as described in +OMG formal/03-09-04 (IDL to Java mapping v1.3). Value types are written as +described in formal/04-03-12 (CORBA 3.0.3). +</p> +@author Wu Gansha (gansha.wu@intel.com), headers. +@author Audrius Meskauskas (AudriusA@Bioinformatics.org), implementation. </body> </html> diff --git a/libjava/classpath/javax/rmi/ORB.java b/libjava/classpath/javax/rmi/ORB.java deleted file mode 100644 index be7a894e65a..00000000000 --- a/libjava/classpath/javax/rmi/ORB.java +++ /dev/null @@ -1,4 +0,0 @@ -package javax.rmi; - -/** XXX - Stub till we have org.omg.CORBA */ -public class ORB { } diff --git a/libjava/classpath/javax/rmi/PortableRemoteObject.java b/libjava/classpath/javax/rmi/PortableRemoteObject.java index 5e5d1fd29d7..5bb6b112613 100644 --- a/libjava/classpath/javax/rmi/PortableRemoteObject.java +++ b/libjava/classpath/javax/rmi/PortableRemoteObject.java @@ -1,5 +1,5 @@ -/* PortableRemoteObject.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* PortableRemoteObject.java -- + Copyright (C) 2004, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,77 +39,190 @@ exception statement from your version. */ package javax.rmi; import gnu.javax.rmi.CORBA.DelegateFactory; -import gnu.javax.rmi.CORBA.GetDelegateInstanceException; + +import org.omg.CORBA.BAD_PARAM; +import org.omg.CORBA.ORB; +import org.omg.CORBA.portable.ObjectImpl; +import org.omg.PortableServer.POA; +import org.omg.PortableServer.Servant; import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import javax.rmi.CORBA.PortableRemoteObjectDelegate; - +import javax.rmi.CORBA.Stub; +import javax.rmi.CORBA.Tie; +import javax.rmi.CORBA.Util; + +/** + * <p> + * An utility class for RMI/IDL server side object implementations. Server side + * implementation objects may inherit from this class, but this is not + * mandatory, as the needed methds are static. Server side implementations may + * choose to inherit from {@link ObjectImpl} or {@link Servant} instead. + * </p> + * <p> + * The functionality of methods in this class is forwarded to the enclosed + * PortableRemoteObjectDelegate. This delegate can be altered by setting the + * system property "javax.rmi.CORBA.PortableRemoteObjectClass" to the name of + * the alternative class that must implement + * {@link PortableRemoteObjectDelegate}. + * </p> + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ public class PortableRemoteObject - implements Remote /* why doc doesn't say should implement Remote */ { - - private static PortableRemoteObjectDelegate delegate; - static - { - try - { - delegate = (PortableRemoteObjectDelegate)DelegateFactory.getInstance - ("PortableRemoteObject"); - } - catch(GetDelegateInstanceException e) - { - e.printStackTrace(); - delegate = null; - } - } - + /** + * A delegate where the functionality is forwarded. + */ + static PortableRemoteObjectDelegate delegate = (PortableRemoteObjectDelegate) DelegateFactory.getInstance(DelegateFactory.PORTABLE_REMOTE_OBJECT); + + /** + * The protected constructor calls {@link exportObject} (this). + * + * @throws RemoteException if the exportObject(this) throws one. + */ protected PortableRemoteObject() throws RemoteException { - if(delegate != null) - exportObject((Remote)this); + exportObject((Remote) this); } + /** + * <p> + * Makes the remote object <code>a_target</code> ready for remote + * communication using the same communications runtime as for the passed + * <code>a_source</code> parameter. The a_target is connected to the same + * ORB (and, if applicable, to the same {@link POA}) as the a_source. + * + * @param a_target the target to connect to ORB, must be an instance of either + * {@link ObjectImpl} (Stubs and old-style ties) or {@link Tie}. + * + * @param a_source the object, providing the connection information, must be + * an instance of either {@link ObjectImpl} (Stubs and old-style ties) or + * {@link Servant} (the next-generation Ties supporting {@link POA}). + * + * @throws RemoteException if the target is already connected to another ORB. + */ public static void connect(Remote target, Remote source) throws RemoteException { - if(delegate != null) - delegate.connect(target, source); + delegate.connect(target, source); } - - public static void exportObject(Remote obj) + + /** + * <p> + * Makes a server object ready for remote calls. The subclasses of + * PortableRemoteObject do not need to call this method, as it is called by + * the constructor. + * </p> + * <p> + * This method only creates a tie object and caches it for future usage. The + * created tie does not have a delegate or an ORB associated. + * </p> + * + * @param object the object to export. + * + * @throws RemoteException if export fails due any reason. + */ + public static void exportObject(Remote object) throws RemoteException { - if(delegate != null) - delegate.exportObject(obj); + delegate.exportObject(object); } - public static Object narrow(Object narrowFrom, Class narrowTo) + /** + * Narrows the passed object to conform to the given interface or IDL type. In + * RMI-IIOP, this method replaces the narrow(org.omg.CORBA.Object) method that + * was present in the CORBA Helpers. This method frequently returns different + * instance and cannot be replaced by the direct cast. The typical narrowing + * cases (all supported by GNU Classpath) are: + * <ul> + * <li>A CORBA object (for instance, returned by the + * {@link ORB#string_to_object} or from the naming service) can be narrowed + * into interface, derived from Remote. The method will try to locate an + * appropriate {@link Stub} by the name pattern (_*_Stub). If the object being + * narrowed is connected to an ORB, the returned instance will inherit that + * connection, representing the same remote (or local) object, but now with + * the possibility to invoke remote methods. </li> + * <li>A CORBA object may be directly narrowed into the appropriate + * {@link Stub} class, if it is and passed as a second parameter. This allows + * to use non-standard stubs without parameterless constructors.</li> + * <li>Any two classes, derived from the {@link ObjectImpl} (may be Stub's) + * can be narrowed one into another (a delegate is transferred). </li> + * <li>An implementation of Remote can be narrowed into {@link Tie} that can + * later connected to an ORB, making the methods accessible remotely. The + * Remote being narrowed normally provides a local implementation, but you can + * also narrow remote Stub, creating "forwarding Tie".</li> + * <li>null is narrowed into null regardless of the second parameter.</li> + * <li>A {@link Tie} can be narrowed into Remote, representing the + * implementation for this Tie (if one is set).</li> + * </ul> + * + * @param object the object like CORBA Object, Stub or Remote that must be + * narrowed to the given interface. + * + * @param narrowToInstaceOf the class of the interface to that the object must + * be narrowed. + * + * @return On success, an object of type narrowTo or null, if narrowFrom = + * null. + * + * @throws ClassCastException if no narrowing is possible. + */ + public static Object narrow(Object object, Class narrowToInstaceOf) throws ClassCastException { - if(delegate != null) - return delegate.narrow(narrowFrom, narrowTo); - else - return null; + return delegate.narrow(object, narrowToInstaceOf); } - public static Remote toStub(Remote obj) + /** + * <p> + * Takes a server implementation object (name pattern *imp) and returns a stub + * object that can be used to access that server object (target), name + * (pattern _*_Stub). + * + * The returned stub is not connected to any ORB and must be explicitly + * connected using {@link #connect}. + * </p> + * <p> + * The method signature prevents it from returning stubs that does not + * implement Remote (ClassCastException will be thrown). + * </p> + * + * @param target a server side object implementation. + * @return a stub object that can be used to access that server object. + * + * @throws NoSuchObjectException if a stub class cannot be located by supposed + * name pattern, or an instance of stub fails to be instantiated. + * + * @throws ClassCastException if the stub class can be located, but it does + * not inherit from Remote. + * + * @throws BAD_PARAM if the name of the passed class does not match the + * implementation name pattern (does not end by 'Impl'). + */ + public static Remote toStub(Remote targetImpl) throws NoSuchObjectException { - if(delegate != null) - return delegate.toStub(obj); - else - return null; + return delegate.toStub(targetImpl); } - public static void unexportObject(Remote obj) + /** + * Deregister a currently exported server object from the ORB runtimes. The + * object to becomes available for garbage collection. This is usually + * impemented via {@link Util#unexportObject} + * + * @param object the object to unexport. + * + * @throws NoSuchObjectException if the passed object is not currently + * exported. + */ + public static void unexportObject(Remote object) throws NoSuchObjectException { - if(delegate != null) - delegate.unexportObject(obj); + delegate.unexportObject(object); } - -} +}
\ No newline at end of file diff --git a/libjava/classpath/javax/rmi/package.html b/libjava/classpath/javax/rmi/package.html index febf59bd61c..6014c8c8533 100644 --- a/libjava/classpath/javax/rmi/package.html +++ b/libjava/classpath/javax/rmi/package.html @@ -37,10 +37,40 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. --> <html> -<head><title>GNU Classpath - javax.rmi</title></head> +<head><title>GNU Classpath - javax.rmi.CORBA</title></head> <body> -<p></p> - +<p> +Java RMI over IIOP combines RMI technology with CORBA technology. Like plain RMI, +RMI over IIOP allows to work completely in the Java programming language +(no IDL). When CORBA needs a separate helper class for each structure being +passed, RMI over IIOP only needs stubs and ties for the objects that are remotely +accessible. As a result, development with RMI-IIOP is easier. However the +specialised pure CORBA helpers needs no reflection to transfer they structures +and hence may be faster than methods, used by both RMI-IIOP and plain RMI. +</p><p> +Like RMI, RMI over IIOP provides flexibility by allowing to pass any serializable +Java object (Objects By Value) between application components. A certain +"imaginary IDL" is automatically supposed; this IDL can be explicitly generated +and later used to interoperate with non-java application. +</p><p> +Like CORBA, RMI over IIOP is based on open standards defined with the +participation of hundredsof vendors and users in the OMG. It uses IIOP +communication protocol that provides much better interoperability with other +programming languages. +</p><p> +With RMI/IIOP you can use advanced CORBA features: multiple objects per servant +and servants per object, servant activators and locators, servant, client and +ior interceptors, CORBA naming service, various ORB policies, stringified object +references and so on. This functionality is based on CORBA value type standard. +RMI/IIOP supports (and GNU Classpath implements) transferring of the arbitrary +connected object graphs (graph flattenning). +</p><p> +GNU Classpath RMI-IIOP functionality is implemented as described in +OMG formal/03-09-04 (IDL to Java mapping v1.3). Value types are written as +described in formal/04-03-12 (CORBA 3.0.3). +</p> +@author Wu Gansha (gansha.wu@intel.com), headers. +@author Audrius Meskauskas (AudriusA@Bioinformatics.org), implementation. </body> </html> diff --git a/libjava/classpath/javax/security/auth/Subject.java b/libjava/classpath/javax/security/auth/Subject.java index 4e35a645ddb..1659c6425fe 100644 --- a/libjava/classpath/javax/security/auth/Subject.java +++ b/libjava/classpath/javax/security/auth/Subject.java @@ -1,5 +1,5 @@ /* Subject.java -- a single entity in the system. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -385,19 +385,19 @@ public final class Subject implements Serializable // Constructors. // ----------------------------------------------------------------------- - SecureSet (final Subject subject, final int type, final Collection elements) + SecureSet (final Subject subject, final int type, final Collection inElements) { this (subject, type); - for (Iterator it = elements.iterator(); it.hasNext(); ) + for (Iterator it = inElements.iterator(); it.hasNext(); ) { Object o = it.next(); if (type == PRINCIPALS && !(o instanceof Principal)) { throw new IllegalArgumentException(o+" is not a Principal"); } - if (!elements.contains (o)) + if (!this.elements.contains (o)) { - elements.add (o); + this.elements.add (o); } } } @@ -511,7 +511,7 @@ public final class Subject implements Serializable public synchronized boolean contains (final Object element) { - return elements.remove (element); + return elements.contains (element); } public boolean removeAll (final Collection c) diff --git a/libjava/classpath/javax/security/auth/SubjectDomainCombiner.java b/libjava/classpath/javax/security/auth/SubjectDomainCombiner.java index 94a7160eb18..927e7479df6 100644 --- a/libjava/classpath/javax/security/auth/SubjectDomainCombiner.java +++ b/libjava/classpath/javax/security/auth/SubjectDomainCombiner.java @@ -1,5 +1,5 @@ /* SubjectDomainCombiner.java -- domain combiner for Subjects. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -67,8 +67,9 @@ public class SubjectDomainCombiner implements DomainCombiner final ProtectionDomain[] assigned) { LinkedList domains = new LinkedList(); - Principal[] principals = - (Principal[]) subject.getPrincipals().toArray (new Principal[0]); + Principal[] principals = null; + if (subject != null) + principals = (Principal[]) subject.getPrincipals().toArray (new Principal[0]); if (current != null) { for (int i = 0; i < current.length; i++) diff --git a/libjava/classpath/javax/security/auth/login/Configuration.java b/libjava/classpath/javax/security/auth/login/Configuration.java index 1e0d272f19e..eb5e4a81979 100644 --- a/libjava/classpath/javax/security/auth/login/Configuration.java +++ b/libjava/classpath/javax/security/auth/login/Configuration.java @@ -67,29 +67,7 @@ public abstract class Configuration SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission (new AuthPermission ("getLoginConfiguration")); - if (config == null) - { - String conf = (String) AccessController.doPrivileged - (new PrivilegedAction() - { - public Object run() - { - return Security.getProperty ("login.configuration.provider"); - } - }); - try - { - if (conf != null) - config = (Configuration) Class.forName (conf).newInstance(); - else - config = new NullConfiguration(); - } - catch (Exception x) - { - config = new NullConfiguration(); - } - } - return config; + return getConfig(); } public static synchronized void setConfiguration (Configuration config) @@ -115,6 +93,28 @@ public abstract class Configuration */ static Configuration getConfig() { + if (config == null) + { + String conf = (String) AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + return Security.getProperty ("login.configuration.provider"); + } + }); + try + { + if (conf != null) + config = (Configuration) Class.forName (conf).newInstance(); + else + config = new NullConfiguration(); + } + catch (Exception x) + { + config = new NullConfiguration(); + } + } return config; } } diff --git a/libjava/classpath/javax/security/auth/login/LoginContext.java b/libjava/classpath/javax/security/auth/login/LoginContext.java index 8fc2ca75983..a3cbf8f0651 100644 --- a/libjava/classpath/javax/security/auth/login/LoginContext.java +++ b/libjava/classpath/javax/security/auth/login/LoginContext.java @@ -220,7 +220,9 @@ public class LoginContext Exception cause = null; try { - module = (LoginModule) Class.forName (entry.getLoginModuleName()).newInstance(); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class c = Class.forName(entry.getLoginModuleName(), true, cl); + module = (LoginModule) c.newInstance(); } catch (ClassNotFoundException cnfe) { diff --git a/libjava/classpath/javax/security/auth/x500/X500Principal.java b/libjava/classpath/javax/security/auth/x500/X500Principal.java index 78c35ade157..fcbb4950a39 100644 --- a/libjava/classpath/javax/security/auth/x500/X500Principal.java +++ b/libjava/classpath/javax/security/auth/x500/X500Principal.java @@ -140,6 +140,22 @@ public final class X500Principal implements Principal, Serializable // Instance methods. // ------------------------------------------------------------------------ + public int hashCode() + { + int result = size(); + for (int i = 0; i < size(); ++i) + { + Map m = (Map) components.get(i); + for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); ) + { + Map.Entry e = (Map.Entry) it2.next(); + // We don't bother looking at the value of the entry. + result = result * 31 + ((OID) e.getKey()).hashCode(); + } + } + return result; + } + public boolean equals(Object o) { if (!(o instanceof X500Principal)) diff --git a/libjava/classpath/javax/security/sasl/AuthenticationException.java b/libjava/classpath/javax/security/sasl/AuthenticationException.java index 12a8fe0529b..0f674645dc0 100644 --- a/libjava/classpath/javax/security/sasl/AuthenticationException.java +++ b/libjava/classpath/javax/security/sasl/AuthenticationException.java @@ -1,5 +1,5 @@ /* AuthenticationException.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -49,6 +49,8 @@ package javax.security.sasl; * instead of <code>AuthenticationException</code> if it is unable to determine * the nature of the failure, or if does not want to disclose the nature of the * failure, for example, due to security reasons.</p> + * + * @since 1.5 */ public class AuthenticationException extends SaslException { diff --git a/libjava/classpath/javax/security/sasl/AuthorizeCallback.java b/libjava/classpath/javax/security/sasl/AuthorizeCallback.java index bf1b8470d31..fa3b29a3d92 100644 --- a/libjava/classpath/javax/security/sasl/AuthorizeCallback.java +++ b/libjava/classpath/javax/security/sasl/AuthorizeCallback.java @@ -1,5 +1,5 @@ /* AuthorizeCallback.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,19 +38,23 @@ exception statement from your version. */ package javax.security.sasl; +import java.io.Serializable; import javax.security.auth.callback.Callback; /** * This callback is used by {@link SaslServer} to determine whether one entity * (identified by an authenticated authentication ID) can act on behalf of * another entity (identified by an authorization ID). + * + * @since 1.5 */ -public class AuthorizeCallback implements Callback +public class AuthorizeCallback implements Callback, Serializable { - // Constants and variables // ------------------------------------------------------------------------- + private static final long serialVersionUID = -2353344186490470805L; + /** @serial The (authenticated) authentication id to check. */ private String authenticationID = null; diff --git a/libjava/classpath/javax/security/sasl/RealmCallback.java b/libjava/classpath/javax/security/sasl/RealmCallback.java index 12d834054a4..7cb36433fe6 100644 --- a/libjava/classpath/javax/security/sasl/RealmCallback.java +++ b/libjava/classpath/javax/security/sasl/RealmCallback.java @@ -1,5 +1,5 @@ /* RealmCallback.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,6 +43,8 @@ import javax.security.auth.callback.TextInputCallback; /** * This callback is used by {@link SaslClient} and {@link SaslServer} to * retrieve realm information. + * + * @since 1.5 */ public class RealmCallback extends TextInputCallback { diff --git a/libjava/classpath/javax/security/sasl/RealmChoiceCallback.java b/libjava/classpath/javax/security/sasl/RealmChoiceCallback.java index d16e61f6afd..7068a504bce 100644 --- a/libjava/classpath/javax/security/sasl/RealmChoiceCallback.java +++ b/libjava/classpath/javax/security/sasl/RealmChoiceCallback.java @@ -1,5 +1,5 @@ /* RealmChoiceCallback.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,6 +43,8 @@ import javax.security.auth.callback.ChoiceCallback; /** * This callback is used by {@link SaslClient} and {@link SaslServer} to obtain * a realm given a list of realm choices. + * + * @since 1.5 */ public class RealmChoiceCallback extends ChoiceCallback { diff --git a/libjava/classpath/javax/security/sasl/Sasl.java b/libjava/classpath/javax/security/sasl/Sasl.java index 058e4f6e1b6..dbe4cc8c2ae 100644 --- a/libjava/classpath/javax/security/sasl/Sasl.java +++ b/libjava/classpath/javax/security/sasl/Sasl.java @@ -74,6 +74,8 @@ import javax.security.auth.callback.CallbackHandler; * Sasl.createSaslServer(mechanism, protocol, serverName, props, * callbackHandler); * </pre> + * + * @since 1.5 */ public class Sasl { diff --git a/libjava/classpath/javax/security/sasl/SaslClient.java b/libjava/classpath/javax/security/sasl/SaslClient.java index 544ab17d6a8..58eb5e298b6 100644 --- a/libjava/classpath/javax/security/sasl/SaslClient.java +++ b/libjava/classpath/javax/security/sasl/SaslClient.java @@ -1,5 +1,5 @@ /* SaslClient.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -102,6 +102,8 @@ package javax.security.sasl; * * @see Sasl * @see SaslClientFactory + * + * @since 1.5 */ public interface SaslClient { @@ -216,7 +218,7 @@ public interface SaslClient * @throws IllegalStateException if this authentication exchange has not * completed. */ - Object getNegotiatedProperty(String propName) throws SaslException; + Object getNegotiatedProperty(String propName); /** * Disposes of any system resources or security-sensitive information the diff --git a/libjava/classpath/javax/security/sasl/SaslClientFactory.java b/libjava/classpath/javax/security/sasl/SaslClientFactory.java index d6e8cd5ffd1..ae36171c56e 100644 --- a/libjava/classpath/javax/security/sasl/SaslClientFactory.java +++ b/libjava/classpath/javax/security/sasl/SaslClientFactory.java @@ -1,5 +1,5 @@ /* SaslClientFactory.java - Copyright (C) 2003, Free Software Foundation, Inc. + Copyright (C) 2003, 2005, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -55,6 +55,8 @@ import javax.security.auth.callback.CallbackHandler; * * @see SaslClient * @see Sasl + * + * @since 1.5 */ public interface SaslClientFactory { diff --git a/libjava/classpath/javax/security/sasl/SaslException.java b/libjava/classpath/javax/security/sasl/SaslException.java index 13113e6bcda..89764bb18f0 100644 --- a/libjava/classpath/javax/security/sasl/SaslException.java +++ b/libjava/classpath/javax/security/sasl/SaslException.java @@ -1,5 +1,5 @@ /* SaslException.java - Copyright (C) 2003, Free Software Foundation, Inc. + Copyright (C) 2003, 2005, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -45,6 +45,8 @@ import java.io.Serializable; /** * This class represents an error that has occurred when using SASL. + * + * @since 1.5 */ public class SaslException extends IOException implements Serializable { @@ -52,6 +54,8 @@ public class SaslException extends IOException implements Serializable // Constants and variables // ------------------------------------------------------------------------- + private static final long serialVersionUID = 4579784287983423626L; + /** * @serial The possibly null root cause exception. */ diff --git a/libjava/classpath/javax/security/sasl/SaslServer.java b/libjava/classpath/javax/security/sasl/SaslServer.java index f5a04975d53..d30b8f6ba14 100644 --- a/libjava/classpath/javax/security/sasl/SaslServer.java +++ b/libjava/classpath/javax/security/sasl/SaslServer.java @@ -1,5 +1,5 @@ -/* SasServer.java - Copyright (C) 2003, Free Software Foundation, Inc. +/* SaslServer.java + Copyright (C) 2003, 2005, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -92,6 +92,8 @@ package javax.security.sasl; * * @see Sasl * @see SaslServerFactory + * + * @since 1.5 */ public interface SaslServer { @@ -211,7 +213,7 @@ public interface SaslServer * @throws IllegalStateException if this authentication exchange has not * completed. */ - Object getNegotiatedProperty(String propName) throws SaslException; + Object getNegotiatedProperty(String propName); /** * Disposes of any system resources or security-sensitive information the diff --git a/libjava/classpath/javax/security/sasl/SaslServerFactory.java b/libjava/classpath/javax/security/sasl/SaslServerFactory.java index 0fff32fa43b..fc43fb636e9 100644 --- a/libjava/classpath/javax/security/sasl/SaslServerFactory.java +++ b/libjava/classpath/javax/security/sasl/SaslServerFactory.java @@ -1,5 +1,5 @@ /* SaslServerFactory.java - Copyright (C) 2003, Free Software Foundation, Inc. + Copyright (C) 2003, 2005, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -55,6 +55,8 @@ import javax.security.auth.callback.CallbackHandler; * * @see SaslServer * @see Sasl + * + * @since 1.5 */ public interface SaslServerFactory { diff --git a/libjava/classpath/javax/sound/midi/ControllerEventListener.java b/libjava/classpath/javax/sound/midi/ControllerEventListener.java new file mode 100644 index 00000000000..eb075b32f8b --- /dev/null +++ b/libjava/classpath/javax/sound/midi/ControllerEventListener.java @@ -0,0 +1,58 @@ +/* ControllerEventListener.java -- Listener for MIDI controller events + Copyright (C) 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 javax.sound.midi; + +import java.util.EventListener; + +/** + * The interface defines the methods to be implemented by classes wanting + * to be notified on MIDI controller events from a Sequencer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface ControllerEventListener extends EventListener +{ + /** + * Called on MIDI controller events. + * @param event the controller change event message + */ + public void controlChange(ShortMessage event); +} diff --git a/libjava/classpath/javax/sound/midi/Instrument.java b/libjava/classpath/javax/sound/midi/Instrument.java new file mode 100644 index 00000000000..3402e8289c9 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Instrument.java @@ -0,0 +1,77 @@ +/* Instrument.java -- A MIDI Instrument + Copyright (C) 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 javax.sound.midi; + +/** + * The abstract base class for all MIDI instruments. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class Instrument extends SoundbankResource +{ + // The instrument patch. + private Patch patch; + + /** + * Create a new Instrument. + * + * @param soundbank the Soundbank containing the instrument. + * @param patch the patch for this instrument + * @param name the name of this instrument + * @param dataClass the class used to represent sample data for this instrument + */ + protected Instrument(Soundbank soundbank, Patch patch, + String name, Class dataClass) + { + super(soundbank, name, dataClass); + this.patch = patch; + } + + /** + * Get the patch for this instrument. + * + * @return the patch for this instrument + */ + public Patch getPatch() + { + return patch; + } +} diff --git a/libjava/classpath/javax/sound/midi/InvalidMidiDataException.java b/libjava/classpath/javax/sound/midi/InvalidMidiDataException.java new file mode 100644 index 00000000000..d7f770757a4 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/InvalidMidiDataException.java @@ -0,0 +1,90 @@ +/* InvalidMidiDataException.java -- Thrown for invalid MIDI data. + Copyright (C) 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 javax.sound.midi; + +/** + * This exception is thrown when we encounter bad MIDI data. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class InvalidMidiDataException extends Exception +{ + private static final long serialVersionUID = 2780771756789932067L; + + /** + * Create an InvalidMidiDataException object. + */ + public InvalidMidiDataException() + { + super(); + } + + /** + * Create an InvalidMidiDataException object. + * + * @param s the exception message string + */ + public InvalidMidiDataException(String s) + { + super(s); + } + + /** + * Create an InvalidMidiDataException object. + * + * @param s the exception message string + * @param cause the root cause of the exception + */ + public InvalidMidiDataException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an InvalidMidiDataException object. + * + * @param cause the root cause of the exception + */ + public InvalidMidiDataException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/javax/sound/midi/MetaEventListener.java b/libjava/classpath/javax/sound/midi/MetaEventListener.java new file mode 100644 index 00000000000..dd7b8a24be8 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MetaEventListener.java @@ -0,0 +1,58 @@ +/* MetaEventListener.java -- Listener for MIDI meta events + Copyright (C) 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 javax.sound.midi; + +import java.util.EventListener; + +/** + * The interface defines the methods to be implemented by classes wanting + * to be notified on MIDI meta events from a Sequencer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface MetaEventListener extends EventListener +{ + /** + * Called on MIDI meta events. + * @param meta the meta event message + */ + public void meta(MetaMessage meta); +} diff --git a/libjava/classpath/javax/sound/midi/MetaMessage.java b/libjava/classpath/javax/sound/midi/MetaMessage.java new file mode 100644 index 00000000000..2ca93accd77 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MetaMessage.java @@ -0,0 +1,176 @@ +/* MetaMessage.java -- A meta message for MIDI files. + Copyright (C) 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 javax.sound.midi; + +/** + * A system exclusive MIDI message. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class MetaMessage extends MidiMessage +{ + /** + * The META status code. Only valid for MIDI files, not the wire protocol. + */ + public static final int META = 0xFF; + + // The length of the variable length data length encoding. + private int lengthLength = 0; + + /** + * Create a default valid meta message. + * + * The official specs don't specify what message is to be + * created. For now, we create a zero length meta message + * with a type code of 0. + */ + public MetaMessage() + { + super(new byte[4]); + data[0] = (byte) META; + data[1] = (byte) 0; // Type + data[2] = (byte) 1; // Length length + data[3] = (byte) 0; // Length + lengthLength = 1; + } + + /** + * Create a MetaMessage object. + * @param data a complete system exclusive message + */ + public MetaMessage(byte[] data) + { + super(data); + int index = 2; + lengthLength = 1; + while ((data[index++] & 0x80) > 0) + lengthLength++; + } + + /** + * Set the meta message. + * + * @param type the meta type byte (< 128) + * @param data the message data + * @param length the length of the message data + * @throws InvalidMidiDataException if this message is invalid + */ + public void setMessage(int type, byte[] data, int length) + throws InvalidMidiDataException + { + if (type > 127) + throw new InvalidMidiDataException("Meta type 0x" + + Integer.toHexString(type) + + " must be less than 128"); + + // For a nice description of how variable length values are handled, + // see http://www.borg.com/~jglatt/tech/midifile.htm + + // First compute the length of the length value + lengthLength = 0; + int lengthValue = length; + do { + lengthValue = lengthValue >> 7; + lengthLength++; + } while (lengthValue > 0); + + // Now allocate our data array + this.length = 2 + lengthLength + length; + this.data = new byte[this.length]; + this.data[0] = (byte) META; + this.data[1] = (byte) type; + + // Now compute the length representation + long buffer = length & 0x7F; + while ((length >>= 7) > 0) + { + buffer <<= 8; + buffer |= ((length & 0x7F) | 0x80); + } + + // Now store the variable length length value + int index = 2; + do + { + this.data[index++] = (byte) (buffer & 0xFF); + if ((buffer & 0x80) == 0) + break; + buffer >>= 8; + } while (true); + + // Now copy the real data. + System.arraycopy(data, 0, this.data, index, length); + } + + /** + * Get the meta message type. + * + * @return the meta message type + */ + public int getType() + { + return data[1]; + } + + /** + * Get the data for this message, not including the status, + * type, or length information. + * + * @return the message data, not including status, type or lenght info + */ + public byte[] getData() + { + int dataLength = length - 2 - lengthLength; + byte[] result = new byte[dataLength]; + System.arraycopy(data, 2 + lengthLength, result, 0, dataLength); + return result; + } + + /* Create a deep-copy clone of this object. + * @see java.lang.Object#clone() + */ + public Object clone() + { + byte message[] = new byte[length]; + System.arraycopy(data, 0, message, 0, length); + return new MetaMessage(message); + } +} diff --git a/libjava/classpath/javax/sound/midi/MidiChannel.java b/libjava/classpath/javax/sound/midi/MidiChannel.java new file mode 100644 index 00000000000..fe3b5111ab7 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiChannel.java @@ -0,0 +1,236 @@ +/* MidiChannel.java -- A MIDI channel + Copyright (C) 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 javax.sound.midi; + +/** + * A MIDI channel. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface MidiChannel +{ + + /** + * Start playing a note. + * + * @param noteNumber the MIDI note number + * @param velocity the velocity at which the key was pressed + */ + public void noteOn(int noteNumber, int velocity); + + /** + * Stop playing a note. + * + * @param noteNumber the MIDI note number + * @param velocity the volcity at which the ket was released + */ + public void noteOff(int noteNumber, int velocity); + + /** + * Stop playing a note. + * + * @param noteNumber the MIDI note number + */ + public void noteOff(int noteNumber); + + /** + * Change in a key pressure for a note. + * + * @param noteNumber the MIDI note number + * @param pressure the key pressure + */ + public void setPolyPressure(int noteNumber, int pressure); + + /** + * Get the key pressure for a note. + * + * @param noteNumber the MIDI note number + * @return the key pressure + */ + public int getPolyPressure(int noteNumber); + + /** + * Set the key pressure for the channel. + * + * @param pressure the key pressure + */ + public void setChannelPressure(int pressure); + + /** + * Get the key pressure for the channel. + * + * @return the key pressure + */ + public int getChannelPressure(); + + /** + * Set a change in a controller's value. + * + * @param controller the MIDI controller number (0 to 127) + * @param value the new value (0 to 127) + */ + public void controlChange(int controller, int value); + + /** + * Get a controller's value. + * + * @param controller the MIDI controller number (0 to 127) + * @return the controller's value (0 to 127) + */ + public int getController(int controller); + + /** + * Change the patch for this channel. + * + * @param program the patch number to switch to (0 to 127) + */ + public void programChange(int program); + + /** + * Change the bank and patch for this channel. + * + * @param bank the bank to switch to (0 to 16383) + * @param program the patch to switch to (0 to 127) + */ + public void programChange(int bank, int program); + + /** + * Get the current patch for this channel. + * + * @return current patch (0 to 127) + */ + public int getProgram(); + + /** + * Change the pitch bend for this channel using a positive 14-bit value. + * + * @param bend the new pitch bend value + */ + public void setPitchBend(int bend); + + /** + * Get the pitch bend for this channel as a positive 14-bit value. + * + * @return the current patch bend value + */ + public int getPitchBend(); + + /** + * Reset all MIDI controllers to their default values. + */ + public void resetAllControllers(); + + /** + * Stop playing all notes. Sound may not stop. + */ + public void allNotesOff(); + + /** + * Stop all sound. + */ + public void allSoundOff(); + + /** + * Set whether or not local controls are on or off. They are on by + * default. + * + * @param on true to enable local controls, false to disable + * @return the new value + */ + public boolean localControl(boolean on); + + /** + * Turns mono mode on or off. + * + * @param on true to enable mono mode, false to disable + */ + public void setMono(boolean on); + + /** + * Get the current mono mode. + * + * @return true if mono is enabled, false otherwise + */ + public boolean getMono(); + + /** + * Turns omni mode on or off. + * + * @param on true to enable omni mode, false to disable + */ + public void setOmni(boolean on); + + /** + * Get the current omni mode. + * + * @return true if omni is enabled, false otherwise + */ + public boolean getOmni(); + + /** + * Turns mute mode on or off. + * + * @param mute true to enable mute mode, false to disable + */ + public void setMute(boolean mute); + + /** + * Get the current mute mode. + * + * @return true if mute is enabled, false otherwise + */ + public boolean getMute(); + + /** + * Turns solo mode on or off. If any channels are soloed, then only those + * channels make sounds, otherwise all channels will make sound. + * + * @param solo true to enable solo mode, false to disable + */ + public void setSolo(boolean solo); + + /** + * Get the current solo mode. + * + * @return true is solo is enabled, false otherwise. + */ + public boolean getSolo(); +} diff --git a/libjava/classpath/javax/sound/midi/MidiDevice.java b/libjava/classpath/javax/sound/midi/MidiDevice.java new file mode 100644 index 00000000000..6f43c25481d --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiDevice.java @@ -0,0 +1,220 @@ +/* MidiDevice.java -- Interface for MIDI devices + Copyright (C) 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 javax.sound.midi; + +/** + * Interface for all MIDI devices. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface MidiDevice +{ + /** + * Get the Info object describing this device. + * @return the Info object describing this device + */ + public Info getDeviceInfo(); + + /** + * Open this MIDI device and allocate any system resource we need. + * + * @throws MidiUnavailableException if we're not able to open for some reason + */ + public void open() throws MidiUnavailableException; + + /** + * Close this MIDI device, and release any system resources we're using. + */ + public void close(); + + /** + * Returns true if this MIDI device is open and false otherwise. + * + * @return true if this is open, false otherwise + */ + public boolean isOpen(); + + /** + * If this device supports time-stamps, then it will return the number + * of microseconds since this device has been open, and -1 otherwise. + * + * @return -1 or the number of microseconds since this was opened + */ + public long getMicrosecondPosition(); + + /** + * The maximum number of MIDI IN connections we can get as Receivers, + * or -1 if there is no maximum. + * + * @return -1 or the maximum number of Receivers we can get + */ + public int getMaxReceivers(); + + /** + * The maximum number of MIDI OUT connections we can get as Transmitters, + * or -1 if there is no maximum. + * + * @return -1 or the maximum number of Transmitters we can get + */ + public int getMaxTransmitters(); + + /** + * Get a MIDI IN Receiver for this device. + * + * @return a MIDI IN Receiver for this device + * @throws MidiUnavailableException if we can't get a Receiver + */ + public Receiver getReceiver() throws MidiUnavailableException; + + /** + * Get a MIDI OUT Transmitter for this device. + * + * @return a MIDI OUT Transmitter for this device + * @throws MidiUnavailableException if we can't get a Transmitter + */ + public Transmitter getTransmitter() throws MidiUnavailableException; + + /** + * A MIDI device descriptor object. + * + * @author green@redhat.com + * + */ + public static class Info + { + // Private data describing this device + private String name; + private String vendor; + private String description; + private String version; + + /** + * Create an Info object for a MIDI device + * + * @param name the device name + * @param vendor the vendor name + * @param description the device description + * @param version the device version string + */ + public Info(String name, String vendor, String description, String version) + { + this.name = name; + this.vendor = vendor; + this.description = description; + this.version = version; + } + + /** + * This equals method only returns true if this object + * is the same as obj. + * + * @param obj the object we're comparing to + * @return true if this is the same object + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + return super.equals(obj); + } + + /** + * A hash code for this object. + * + * @return the hash code for this object + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Get the device name. + * + * @return the device name + */ + public String getName() + { + return name; + } + + /** + * Get the device vendor. + * + * @return the device vendor + */ + public String getVendor() + { + return vendor; + } + + /** + * Get the device description + * + * @return the device description + */ + public String getDescription() + { + return description; + } + + /** + * get the device version + * + * @return the device version + */ + public String getVersion() + { + return version; + } + + /** + * Simple return the name of the device. + * + * @return the device name + * @see java.lang.Object#toString() + */ + public String toString() + { + return name; + } + } +} diff --git a/libjava/classpath/javax/sound/midi/MidiEvent.java b/libjava/classpath/javax/sound/midi/MidiEvent.java new file mode 100644 index 00000000000..3ca5c21ea86 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiEvent.java @@ -0,0 +1,95 @@ +/* MidiEvent.java -- A MIDI Event + Copyright (C) 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 javax.sound.midi; + +/** + * A MIDI event is the combination of a MIDI message and a timestamp specified + * in MIDI ticks. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class MidiEvent +{ + private final MidiMessage message; + private long tick; + + /** + * Create a MIDI event object from the given MIDI message and timestamp. + * + * @param message the MidiMessage for this event + * @param tick the timestamp for this event + */ + public MidiEvent(MidiMessage message, long tick) + { + this.message = message; + this.tick = tick; + } + + /** + * Get the MIDI message for this event. + * + * @return the MidiMessage for this event + */ + public MidiMessage getMessage() + { + return message; + } + + /** + * Set the timestemp for this event in MIDI ticks. + * + * @param tick the timestamp + */ + public void setTick(long tick) + { + this.tick = tick; + } + + /** + * Get the timestamp for this event in MIDI ticks. + * + * @return the timestamp for this even in MIDI ticks + */ + public long getTick() + { + return tick; + } +} diff --git a/libjava/classpath/javax/sound/midi/MidiFileFormat.java b/libjava/classpath/javax/sound/midi/MidiFileFormat.java new file mode 100644 index 00000000000..79fa9fe2964 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiFileFormat.java @@ -0,0 +1,158 @@ +/* MidiFileFormat.java -- Information about a MIDI file + Copyright (C) 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 javax.sound.midi; + +/** + * Describe a MIDI file, including specifics about its type, length and timing. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class MidiFileFormat +{ + /** + * The MIDI file type. This is either 0, 1 or 2. + * + * Type 0 files contain a single track and represents a single song + * performance. + * Type 1 may contain multiple tracks for a single song performance. + * Type 2 may contain multiple tracks, each representing a + * separate song performance. + * + * See http://en.wikipedia.org/wiki/MIDI#MIDI_file_formats for more + * information. + */ + protected int type; + + /** + * The division type of the MIDI file. + */ + protected float divisionType; + + /** + * The timing resolution of the MIDI file. + */ + protected int resolution; + + /** + * The size of the MIDI file in bytes. + */ + protected int byteLength = UNKNOWN_LENGTH; + + /** + * The length of the MIDI file in microseconds. + */ + protected long microsecondLength = UNKNOWN_LENGTH; + + /** + * A special value indicating an unknown quantity. + */ + public static final int UNKNOWN_LENGTH = -1; // FIXME is this really -1? + + /** + * Create a MidiFileFormat object from the given parameters. + * + * @param type the MIDI file type (0, 1, or 2) + * @param divisionType the MIDI file division type + * @param resolution the MIDI file timing resolution + * @param bytes the MIDI file size in bytes + * @param microseconds the MIDI file length in microseconds + */ + public MidiFileFormat(int type, float divisionType, + int resolution, int bytes, long microseconds) + { + this.type = type; + this.divisionType = divisionType; + this.resolution = resolution; + this.byteLength = bytes; + this.microsecondLength = microseconds; + } + + /** + * Get the MIDI file type (0, 1, or 2). + * + * @return the MIDI file type (0, 1, or 2) + */ + public int getType() + { + return type; + } + + /** + * Get the file division type. + * + * @return the file divison type + */ + public float getDivisionType() + { + return divisionType; + } + + /** + * Get the file timing resolution. If the division type is PPQ, then this + * is value represents ticks per beat, otherwise it's ticks per frame (SMPTE). + * + * @return the timing resolution in ticks per beat or ticks per frame + */ + public int getResolution() + { + return resolution; + } + + /** + * Get the file length in bytes. + * + * @return the file length in bytes or UNKNOWN_LENGTH + */ + public int getByteLength() + { + return byteLength; + } + + /** + * Get the file length in microseconds. + * + * @return the file length in microseconds or UNKNOWN_LENGTH + */ + public long getMicrosecondLength() + { + return microsecondLength; + } +} diff --git a/libjava/classpath/javax/sound/midi/MidiMessage.java b/libjava/classpath/javax/sound/midi/MidiMessage.java new file mode 100644 index 00000000000..e265b5eadcc --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiMessage.java @@ -0,0 +1,126 @@ +/* MidiMessage.java -- base class for MIDI messages. + Copyright (C) 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 javax.sound.midi; + +/** + * The base class for all MIDI messages. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class MidiMessage implements Cloneable +{ + /** + * MIDI message data. + */ + protected byte data[]; + + /** + * The total length of the MIDI message. + */ + protected int length; + + /** + * MidiMessage contructor. + * + * @param data a valid MIDI message + */ + protected MidiMessage(byte[] data) + { + this.data = data; + this.length = data.length; + } + + /** + * Set the complete MIDI message. + * + * @param data The complete MIDI message. + * @param length The length of the MIDI message. + * @throws InvalidMidiDataException Thrown when the MIDI message is invalid. + */ + protected void setMessage(byte[] data, int length) + throws InvalidMidiDataException + { + this.data = new byte[length]; + System.arraycopy(data, 0, this.data, 0, length); + this.length = length; + } + + /** + * Get the MIDI message data. + * + * @return an array containing the MIDI message data + */ + public byte[] getMessage() + { + byte copy[] = new byte[length]; + System.arraycopy(data, 0, copy, 0, length); + return copy; + } + + /** + * Get the status byte of the MIDI message (as an int) + * + * @return the status byte of the MIDI message (as an int), or zero if the message length is zero. + */ + public int getStatus() + { + if (length > 0) + return (data[0] & 0xff); + else + return 0; + } + + /** + * Get the length of the MIDI message. + * + * @return the length of the MIDI messsage + */ + public int getLength() + { + return length; + } + + /* Create a clone of this object. + * + * @see java.lang.Object#clone() + */ + public abstract Object clone(); +} diff --git a/libjava/classpath/javax/sound/midi/MidiSystem.java b/libjava/classpath/javax/sound/midi/MidiSystem.java new file mode 100644 index 00000000000..8ea12eb7002 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiSystem.java @@ -0,0 +1,566 @@ +/* MidiSystem.java -- Access system MIDI resources + Copyright (C) 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 javax.sound.midi; + +import gnu.classpath.ServiceFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +import javax.sound.midi.spi.MidiDeviceProvider; +import javax.sound.midi.spi.MidiFileReader; +import javax.sound.midi.spi.MidiFileWriter; +import javax.sound.midi.spi.SoundbankReader; + +/** + * MidiSystem provides access to the computer system's MIDI resources, + * as well as utility routines for reading MIDI files and more. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class MidiSystem +{ + /** + * Get an array of all available MIDI devices. + * + * @return a possibly empty array of all available MIDI devices + */ + public static MidiDevice.Info[] getMidiDeviceInfo() + { + Iterator deviceProviders = + ServiceFactory.lookupProviders(MidiDeviceProvider.class); + List infoList = new ArrayList(); + + while (deviceProviders.hasNext()) + { + MidiDeviceProvider provider = (MidiDeviceProvider) deviceProviders.next(); + MidiDevice.Info[] infos = provider.getDeviceInfo(); + for (int i = infos.length; i > 0; ) + infoList.add(infos[--i]); + } + + return (MidiDevice.Info[]) + infoList.toArray(new MidiDevice.Info[infoList.size()]); + } + + /** + * Get the specified MIDI device. + * + * @param info a description of the device we're looking for + * @return the requested MIDI device + * @throws MidiUnavailableException if no MIDI devices are configured or found + * @throws IllegalArgumentException if the device described by info is not found + */ + public static MidiDevice getMidiDevice(MidiDevice.Info info) + throws MidiUnavailableException + { + Iterator deviceProviders = + ServiceFactory.lookupProviders(MidiDeviceProvider.class); + + if (! deviceProviders.hasNext()) + throw new MidiUnavailableException("No MIDI device providers available."); + + do + { + MidiDeviceProvider provider = + (MidiDeviceProvider) deviceProviders.next(); + if (provider.isDeviceSupported(info)) + return provider.getDevice(info); + } while (deviceProviders.hasNext()); + + throw new IllegalArgumentException("MIDI device " + + info + " not available."); + } + + /** + * Get the default Receiver instance. This just picks the first one + * it finds for now. + * + * @return the default Receiver instance + * @throws MidiUnavailableException if no Receiver is found + */ + public static Receiver getReceiver() throws MidiUnavailableException + { + // TODO: The 1.5 spec has a fancy mechanism to specify the default + // receiver device. For now, well just return the first one we find. + MidiDevice.Info[] infos = getMidiDeviceInfo(); + for (int i = 0; i < infos.length; i++) + { + MidiDevice device = getMidiDevice(infos[i]); + if (device instanceof Receiver) + return (Receiver) device; + } + throw new MidiUnavailableException("No Receiver device available"); + } + + /** + * Get the default Transmitter instance. This just picks the first one + * it finds for now. + * + * @return the default Transmitter instance + * @throws MidiUnavailableException if no Transmitter is found + */ + public static Transmitter getTransmitter() throws MidiUnavailableException + { + // TODO: The 1.5 spec has a fancy mechanism to specify the default + // Transmitter device. For now, well just return the first one we find. + MidiDevice.Info[] infos = getMidiDeviceInfo(); + for (int i = 0; i < infos.length; i++) + { + MidiDevice device = getMidiDevice(infos[i]); + if (device instanceof Transmitter) + return (Transmitter) device; + } + throw new MidiUnavailableException("No Transmitter device available"); + } + + /** + * Get the default Synthesizer instance. This just picks the first one + * it finds for now. + * + * @return the default Synthesizer instance + * @throws MidiUnavailableException if no Synthesizer is found + */ + public static Synthesizer getSynthesizer() throws MidiUnavailableException + { + // TODO: The 1.5 spec has a fancy mechanism to specify the default + // Synthesizer device. For now, well just return the first one we find. + MidiDevice.Info[] infos = getMidiDeviceInfo(); + for (int i = 0; i < infos.length; i++) + { + MidiDevice device = getMidiDevice(infos[i]); + if (device instanceof Synthesizer) + return (Synthesizer) device; + } + throw new MidiUnavailableException("No Synthesizer device available"); + } + + /** + * Get the default Sequencer instance. This just picks the first one + * it finds for now. + * + * @return the default Sequencer instance + * @throws MidiUnavailableException if no Sequencer is found + */ + public static Sequencer getSequencer() throws MidiUnavailableException + { + // TODO: The 1.5 spec has a fancy mechanism to specify the default + // Sequencer device. For now, well just return the first one we find. + MidiDevice.Info[] infos = getMidiDeviceInfo(); + for (int i = 0; i < infos.length; i++) + { + MidiDevice device = getMidiDevice(infos[i]); + if (device instanceof Sequencer) + return (Sequencer) device; + } + throw new MidiUnavailableException("No Sequencer device available"); + } + + /** + * Read a Soundbank object from the given stream. + * + * @param stream the stream from which to read the Soundbank + * @return the Soundbank object + * @throws InvalidMidiDataException if we were unable to read the soundbank + * @throws IOException if an I/O error happened while reading + */ + public static Soundbank getSoundbank(InputStream stream) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(SoundbankReader.class); + while (readers.hasNext()) + { + SoundbankReader sr = (SoundbankReader) readers.next(); + Soundbank sb = sr.getSoundbank(stream); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Cannot read soundbank from stream"); + } + + /** + * Read a Soundbank object from the given url. + * + * @param url the url from which to read the Soundbank + * @return the Soundbank object + * @throws InvalidMidiDataException if we were unable to read the soundbank + * @throws IOException if an I/O error happened while reading + */ + public static Soundbank getSoundbank(URL url) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(SoundbankReader.class); + while (readers.hasNext()) + { + SoundbankReader sr = (SoundbankReader) readers.next(); + Soundbank sb = sr.getSoundbank(url); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Cannot read from url " + url); + } + + /** + * Read a Soundbank object from the given file. + * + * @param file the file from which to read the Soundbank + * @return the Soundbank object + * @throws InvalidMidiDataException if we were unable to read the soundbank + * @throws IOException if an I/O error happened while reading + */ + public static Soundbank getSoundbank(File file) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(SoundbankReader.class); + while (readers.hasNext()) + { + SoundbankReader sr = (SoundbankReader) readers.next(); + Soundbank sb = sr.getSoundbank(file); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Cannot read soundbank from file " + + file); + } + + /** + * Read a MidiFileFormat object from the given stream. + * + * @param stream the stream from which to read the MidiFileFormat + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat + * @throws IOException if an I/O error happened while reading + */ + public static MidiFileFormat getMidiFileFormat(InputStream stream) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + MidiFileFormat sb = sr.getMidiFileFormat(stream); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Can't read MidiFileFormat from stream"); + } + + /** + * Read a MidiFileFormat object from the given url. + * + * @param url the url from which to read the MidiFileFormat + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat + * @throws IOException if an I/O error happened while reading + */ + public static MidiFileFormat getMidiFileFormat(URL url) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + MidiFileFormat sb = sr.getMidiFileFormat(url); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Cannot read from url " + url); + } + + /** + * Read a MidiFileFormat object from the given file. + * + * @param file the file from which to read the MidiFileFormat + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat + * @throws IOException if an I/O error happened while reading + */ + public static MidiFileFormat getMidiFileFormat(File file) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + MidiFileFormat sb = sr.getMidiFileFormat(file); + if (sb != null) + return sb; + } + throw new InvalidMidiDataException("Can't read MidiFileFormat from file " + + file); + } + + /** + * Read a Sequence object from the given stream. + * + * @param stream the stream from which to read the Sequence + * @return the Sequence object + * @throws InvalidMidiDataException if we were unable to read the Sequence + * @throws IOException if an I/O error happened while reading + */ + public static Sequence getSequence(InputStream stream) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + Sequence sq = sr.getSequence(stream); + if (sq != null) + return sq; + } + throw new InvalidMidiDataException("Can't read Sequence from stream"); + } + + /** + * Read a Sequence object from the given url. + * + * @param url the url from which to read the Sequence + * @return the Sequence object + * @throws InvalidMidiDataException if we were unable to read the Sequence + * @throws IOException if an I/O error happened while reading + */ + public static Sequence getSequence(URL url) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + Sequence sq = sr.getSequence(url); + if (sq != null) + return sq; + } + throw new InvalidMidiDataException("Cannot read from url " + url); + } + + /** + * Read a Sequence object from the given file. + * + * @param file the file from which to read the Sequence + * @return the Sequence object + * @throws InvalidMidiDataException if we were unable to read the Sequence + * @throws IOException if an I/O error happened while reading + */ + public static Sequence getSequence(File file) + throws InvalidMidiDataException, IOException + { + Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class); + while (readers.hasNext()) + { + MidiFileReader sr = (MidiFileReader) readers.next(); + Sequence sq = sr.getSequence(file); + if (sq != null) + return sq; + } + throw new InvalidMidiDataException("Can't read Sequence from file " + + file); + } + + /** + * Return an array of supported MIDI file types on this system. + * + * @return the array of supported MIDI file types + */ + public static int[] getMidiFileTypes() + { + // We only support a max of 3 MIDI file types. + boolean supported[] = new boolean[3]; + // The number of supported formats. + int count = 0; + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + int types[] = fw.getMidiFileTypes(); + for (int i = types.length; i > 0;) + { + int type = types[--i]; + if (supported[type] == false) + { + count++; + supported[type] = true; + } + } + } + int result[] = new int[count]; + for (int i = supported.length; i > 0;) + { + if (supported[--i]) + result[--count] = i; + } + return result; + } + + /** + * Return true if the system supports writing files of type fileType. + * + * @param fileType the MIDI file type we want to write + * @return true if we can write fileType files, false otherwise + */ + public static boolean isFileTypeSupported(int fileType) + { + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + + if (fw.isFileTypeSupported(fileType)) + return true; + } + return false; + } + + /** + * Return an array of supported MIDI file types on this system + * for the given sequnce. + * + * @param sequence the sequnce to write + * @return the array of supported MIDI file types + */ + public static int[] getMidiFileTypes(Sequence sequence) + { + // We only support a max of 3 MIDI file types. + boolean supported[] = new boolean[3]; + // The number of supported formats. + int count = 0; + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + int types[] = fw.getMidiFileTypes(sequence); + for (int i = types.length; i > 0;) + { + int type = types[--i]; + if (supported[type] == false) + { + count++; + supported[type] = true; + } + } + } + int result[] = new int[count]; + for (int i = supported.length; i > 0;) + { + if (supported[--i]) + result[--count] = i; + } + return result; + } + + /** + * Return true if the system supports writing files of type fileType + * for the given sequence. + * + * @param fileType the MIDI file type we want to write + * @param sequence the Sequence we want to write + * @return true if we can write fileType files for sequence, false otherwise + */ + public static boolean isFileTypeSupported(int fileType, Sequence sequence) + { + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + + if (fw.isFileTypeSupported(fileType, sequence)) + return true; + } + return false; + } + + /** + * Write a sequence to an output stream using a specific MIDI file format. + * + * @param in the sequence to write + * @param fileType the MIDI file format to use + * @param out the output stream to write to + * @return the number of bytes written + * @throws IOException if an I/O exception happens + * @throws IllegalArgumentException if fileType is not supported for in + */ + public static int write(Sequence in, int fileType, OutputStream out) + throws IOException + { + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + + if (fw.isFileTypeSupported(fileType, in)) + return fw.write(in, fileType, out); + } + throw new IllegalArgumentException("File type " + + fileType + " is not supported"); + } + + /** + * Write a sequence to a file using a specific MIDI file format. + * + * @param in the sequence to write + * @param fileType the MIDI file format to use + * @param out the file to write to + * @return the number of bytes written + * @throws IOException if an I/O exception happens + * @throws IllegalArgumentException if fileType is not supported for in + */ + public static int write(Sequence in, int fileType, File out) + throws IOException + { + Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class); + while (writers.hasNext()) + { + MidiFileWriter fw = (MidiFileWriter) writers.next(); + + if (fw.isFileTypeSupported(fileType, in)) + return fw.write(in, fileType, out); + } + throw new IllegalArgumentException("File type " + + fileType + " is not supported"); + } +} + diff --git a/libjava/classpath/javax/sound/midi/MidiUnavailableException.java b/libjava/classpath/javax/sound/midi/MidiUnavailableException.java new file mode 100644 index 00000000000..d4b85e810aa --- /dev/null +++ b/libjava/classpath/javax/sound/midi/MidiUnavailableException.java @@ -0,0 +1,90 @@ +/* MidiUnavailableException.java -- MIDI resources are not available + Copyright (C) 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 javax.sound.midi; + +/** + * This exception is thrown when MIDI resources are not available. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class MidiUnavailableException extends Exception +{ + private static final long serialVersionUID = 6093809578628944323L; + + /** + * Create a MidiUnavailableException. + */ + public MidiUnavailableException() + { + super(); + } + + /** + * Create an MidiUnavailableException object. + * + * @param s the exception message string + */ + public MidiUnavailableException(String s) + { + super(s); + } + + /** + * Create an MidiUnavailableException object. + * + * @param s the exception message string + * @param cause the root cause of the exception + */ + public MidiUnavailableException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an MidiUnavailableException object. + * + * @param cause the root cause of the exception + */ + public MidiUnavailableException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/javax/sound/midi/Patch.java b/libjava/classpath/javax/sound/midi/Patch.java new file mode 100644 index 00000000000..eb9d8bc3b82 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Patch.java @@ -0,0 +1,86 @@ +/* Patch.java -- A MIDI patch. + Copyright (C) 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 javax.sound.midi; + +/** + * A Patch describes where an Instrument is loaded on a Synthesizer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class Patch +{ + // Private data describing the patch + private int bank = 0; + private int program = 0; + + /** + * Create a Patch object, specifying the bank and program in which this Patch + * is located. + * + * @param bank the bank in which this Patch is located + * @param program the program in which this Patch is located + */ + public Patch(int bank, int program) + { + this.bank = bank; + this.program = program; + } + + /** + * Get the bank in which this Patch is located. + * + * @return the bank in which this Patch is located + */ + public int getBank() + { + return bank; + } + + /** + * Get the program in which this Patch is located. + * + * @return the program in which this Patch is located + */ + public int getProgram() + { + return program; + } +} diff --git a/libjava/classpath/javax/sound/midi/Receiver.java b/libjava/classpath/javax/sound/midi/Receiver.java new file mode 100644 index 00000000000..0e70b2809e9 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Receiver.java @@ -0,0 +1,66 @@ +/* Receiver.java -- An interface for objects receiving MIDI data + Copyright (C) 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 javax.sound.midi; + +/** + * This interface describes the methods required by objects receiving MIDI + * messages. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface Receiver +{ + /** + * Send a MIDI message and timestamp. Some receivers don't support + * timestamps, in which case timeStamp should be -1. + * + * @param message the MIDI message to send + * @param timeStamp time timestamp for this message in microseconds (or -1) + * @throws IllegalStateException if the receiver is closed + */ + public void send(MidiMessage message, long timeStamp) + throws IllegalStateException; + + /** + * Close this receiver, possibly freeing system resources. + */ + public void close(); +} diff --git a/libjava/classpath/javax/sound/midi/Sequence.java b/libjava/classpath/javax/sound/midi/Sequence.java new file mode 100644 index 00000000000..1a43d207ce6 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Sequence.java @@ -0,0 +1,248 @@ +/* Sequence.java -- A sequence of MIDI events + Copyright (C) 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 javax.sound.midi; + +import java.util.Iterator; +import java.util.Vector; + +/** + * Objects of this type represent sequences of MIDI messages that can be + * played back by a Sequencer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class Sequence +{ + /** + * The timing division type for this sequence (PPQ or SMPTE*) + */ + protected float divisionType; + + /** + * The timing resolution in ticks/beat or ticks/frame, depending on the + * division type. + */ + protected int resolution; + + /** + * The MIDI tracks used by this sequence. + */ + protected Vector tracks; + + /** + * Tempo-based timing. Resolution is specified in ticks per beat. + */ + public static final float PPQ = 0.0f; + + /** + * 24 frames/second timing. Resolution is specific in ticks per frame. + */ + public static final float SMPTE_24 = 24.0f; + + /** + * 25 frames/second timing. Resolution is specific in ticks per frame. + */ + public static final float SMPTE_25 = 25.0f; + + /** + * 30 frames/second timing. Resolution is specific in ticks per frame. + */ + public static final float SMPTE_30 = 30.0f; + + /** + * 29.97 frames/second timing. Resolution is specific in ticks per frame. + */ + public static final float SMPTE_30DROP = 29.97f; + + // Private helper class + private void init(float divisionType, int resolution, int numTracks) + throws InvalidMidiDataException + { + if (divisionType != PPQ + && divisionType != SMPTE_24 + && divisionType != SMPTE_25 + && divisionType != SMPTE_30 + && divisionType != SMPTE_30DROP) + throw new InvalidMidiDataException("Invalid division type (" + + divisionType + ")"); + + this.divisionType = divisionType; + this.resolution = resolution; + + tracks = new Vector(numTracks); + while (numTracks > 0) + tracks.set(--numTracks, new Track()); + } + + /** + * Create a MIDI sequence object with no initial tracks. + * + * @param divisionType the division type (must be one of PPQ or SMPTE_*) + * @param resolution the timing resolution + * @throws InvalidMidiDataException if the division type is invalid + */ + public Sequence(float divisionType, int resolution) + throws InvalidMidiDataException + { + init(divisionType, resolution, 0); + } + + /** + * Create a MIDI seqence object. + * + * @param divisionType the division type (must be one of PPQ or SMPTE_*) + * @param resolution the timing resolution + * @param numTracks the number of initial tracks + * @throws InvalidMidiDataException if the division type is invalid + */ + public Sequence(float divisionType, int resolution, int numTracks) + throws InvalidMidiDataException + { + init(divisionType, resolution, 0); + } + + /** + * The division type of this sequence. + * + * @return division type of this sequence + */ + public float getDivisionType() + { + return divisionType; + } + + /** + * The timing resolution for this sequence, relative to the division type. + * + * @return the timing resolution for this sequence + */ + public int getResolution() + { + return resolution; + } + + /** + * Create a new empty MIDI track and add it to this sequence. + * + * @return the newly create MIDI track + */ + public Track createTrack() + { + Track track = new Track(); + tracks.add(track); + return track; + } + + /** + * Remove the specified MIDI track from this sequence. + * + * @param track the track to remove + * @return true if track was removed and false othewise + */ + public boolean deleteTrack(Track track) + { + return tracks.remove(track); + } + + /** + * Get an array of MIDI tracks used in this sequence. + * + * @return a possibly empty array of tracks + */ + public Track[] getTracks() + { + return (Track[]) tracks.toArray(new Track[tracks.size()]); + } + + /** + * The length of this sequence in microseconds. + * + * @return the length of this sequence in microseconds + */ + public long getMicrosecondLength() + { + long tickLength = getTickLength(); + + if (divisionType == PPQ) + { + // FIXME + // How can this possible be computed? PPQ is pulses per quarter-note, + // which is dependent on the tempo of the Sequencer. + throw new + UnsupportedOperationException("Can't compute PPQ based lengths yet"); + } + else + { + // This is a fixed tick per frame computation + return (long) ((tickLength * 1000000) / (divisionType * resolution)); + } + } + + /** + * The length of this sequence in MIDI ticks. + * + * @return the length of this sequence in MIDI ticks + */ + public long getTickLength() + { + long length = 0; + Iterator itr = tracks.iterator(); + while (itr.hasNext()) + { + Track track = (Track) itr.next(); + long trackTicks = track.ticks(); + if (trackTicks > length) + length = trackTicks; + } + return length; + } + + /** + * Get an array of patches used in this sequence. + * + * @return an array of patches used in this sequence + */ + public Patch[] getPatchList() + { + // FIXE: not quite sure how to do this yet. + throw new UnsupportedOperationException("Can't get patch list yet"); + } +} diff --git a/libjava/classpath/javax/sound/midi/Sequencer.java b/libjava/classpath/javax/sound/midi/Sequencer.java new file mode 100644 index 00000000000..894d876e91f --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Sequencer.java @@ -0,0 +1,402 @@ +/* Sequencer.java -- A MIDI sequencer object + Copyright (C) 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 javax.sound.midi; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A Sequencer object plays MIDI sequences described as Sequence objects. + * This class provides methods for loading and unloading sequences, as well + * as basic transport controls. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface Sequencer extends MidiDevice +{ + /** + * Set the Sequence object for this sequencer. + * + * @param seq the Sequence to process + * @throws InvalidMidiDataException if the sequence is invalid for any reason + */ + public void setSequence(Sequence seq) throws InvalidMidiDataException; + + /** + * Set the sequence for this sequencer. istream reads on a valid MIDI file. + * + * @param istream an input stream for a valid MIDI file + * @throws IOException if an I/O exception happens + * @throws InvalidMidiDataException if the MIDI file contains bad data + */ + public void setSequence(InputStream istream) + throws IOException, InvalidMidiDataException; + + /** + * Get the current sequence object for this sequencer. + * + * @return the current sequence object. May be null. + */ + public Sequence getSequence(); + + /** + * Start playback of the current sequence. + */ + public void start(); + + /** + * Stop playback of the current sequence. + */ + public void stop(); + + /** + * Returns true if the sequence is playing. + * + * @return true if the sequence is playing and false otherwise + */ + public boolean isRunning(); + + /** + * Start playback and record of MIDI events. + * Any tracks enabled for recording will have their events replaced. + * Any newly recorded events, and all events from non-recording tracks + * will be sent to the sequencer's transmitter. + */ + public void startRecording(); + + /** + * Stop recording, although continue playing. + */ + public void stopRecording(); + + /** + * Returns true if sequence is recording. + * + * @return true if the sequence is recording and false otherwise + */ + public boolean isRecording(); + + /** + * Enable recording for a specific track using data from a specific channel. + * + * @param track the track to enable for recording + * @param channel the channel from which to record + */ + public void recordEnable(Track track, int channel); + + /** + * Disable recording for a specific track. + * + * @param track the track to disable recording for + */ + public void recordDisable(Track track); + + /** + * Get the current tempo in beats per minute. + * + * @return the current tempo in beats per minute + */ + public float getTempoInBPM(); + + /** + * Sets the current tempo in beats per minute. + * + * @param bpm the new tempo in bears per minutes + */ + public void setTempoInBPM(float bpm); + + /** + * Get the current tempo in microseconds per quarter note. + * + * @return the current tempo in microseconds per quarter note. + */ + public float getTempoInMPQ(); + + /** + * Sets the current tempo in microseconds per quarter note. + * + * @param mpq the new tempo in microseconds per quarter note. + */ + public void setTempoInMPQ(float mpq); + + /** + * Set a scaling factor for the playback tempo, which is 1.0 by default. + * + * @param factor the new tempo scaling factor + */ + public void setTempoFactor(float factor); + + /** + * Get the current scaling factor for the playback tempo. + * + * @return the current tempo scaling factor + */ + public float getTempoFactor(); + + /** + * Get the length of the current sequence in MIDI ticks. + * + * @return the length of the current sequence in MIDI ticks + */ + public long getTickLength(); + + /** + * Get the current playback position of the sequencer in MIDI ticks. + * + * @return the current playback position of the sequencer in MIDI ticks + */ + public long getTickPosition(); + + /** + * Set the current playback position of the sequencer in MIDI ticks. + * + * @param tick the new playback position of the sequencer in MIDI ticks + */ + public void setTickPosition(long tick); + + /** + * Get the length of the current sequence in microseconds. + * + * @return the length of the current sequence in microseconds + */ + public long getMicrosecondLength(); + + /** + * Get the current playback position of the sequencer in microseconds. + * + * @return the current playback position of the sequencer in microseconds + */ + public long getMicrosecondPosition(); + + /** + * Set the current playback position of the sequencer in microseconds. + * + * @param microsecond the new playback position of the sequencer in microseconds + */ + public void setMicrosecondPosition(long microsecond); + + /** + * Set the source of timing information. sync must be found in the array + * returned by getMasterSyncModes(). + * FIXME: What happens if it isn't? + * + * @param sync the new source of timing information + */ + public void setMasterSyncMode(SyncMode sync); + + /** + * Get the source of timing information. + * + * @return the current source of timing information + */ + public SyncMode getMasterSyncMode(); + + /** + * Get an array of timing sources supported by this sequencer. + * + * @return an array of timing sources supported by this sequencer + */ + public SyncMode[] getMasterSyncModes(); + + /** + * Set the slave synchronization mode for this sequencer. sync must be + * found in the array returned by getSlaveSyncModes(). + * FIXME: What happens if it isn't? + * + * @param sync the new slave sync mode for this sequencer + */ + public void setSlaveSyncMode(SyncMode sync); + + /** + * Get the current slave synchronization mode. + * + * @return the current slave synchronization mode + */ + public SyncMode getSlaveSyncMode(); + + /** + * Get an array of slave sync modes supported by this sequencer. + * + * @return an array of slave sync modes supported by this sequencer + */ + public SyncMode[] getSlaveSyncModes(); + + /** + * Sets the mute state for a specific track. + * + * @param track the track to modify + * @param mute the new mute state + */ + public void setTrackMute(int track, boolean mute); + + /** + * Get the mute state of a specific track. + * + * @param track the track to query + * @return the mute state for track + */ + public boolean getTrackMute(int track); + + /** + * Sets the solo state for a specific track. + * + * @param track the track to modify + * @param solo the new solo state + */ + public void setTrackSolo(int track, boolean solo); + + /** + * Get the solo state for a specific track. + * + * @param track the track to query + * @return the solo state for track + */ + public boolean getTrackSolo(int track); + + /** + * Add a meta event listening object to this sequencer. It will receive + * notification whenever the sequencer processes a meta event. + * A listener may fail to get added if this sequencer doesn't support + * meta events. + * + * @param listener the listener to add + * @return true if listener was added, false othewise + */ + public boolean addMetaEventListener(MetaEventListener listener); + + /** + * Remove a meta event listener from this sequencer. + * + * @param listener the listener to remove + */ + public void removeMetaEventListener(MetaEventListener listener); + + /** + * Add a controller event listening object to this sequencer. It will + * receive notification whenever the sequencer processes a controller + * event for a specified controller number.. + * + * @param listener the listener to add + * @param controllers the conroller numbers to listen to + * @return the controller numbers being listened to + */ + public int[] addControllerEventListener(ControllerEventListener listener, + int controllers[]); + + /** + * Remove a controller listener from this sequencer for the specified + * controller numbers. + * + * @param listener the listener to remove + * @param controllers the controllers to unlisten + * @return the controller numbers being unlistened + */ + public int[] removeControllerEventListener(ControllerEventListener listener, + int controllers[]); + + /** + * A SyncMode object represents the mechanism by which a MIDI sequencer + * synchronizes time with a master or slave device. + * + * @author green@redhat.com + * + */ + public static class SyncMode + { + /** + * A master sync mode indicating the use of an internal sequencer clock. + */ + public static final SyncMode INTERNAL_CLOCK = new SyncMode("Internal Clock"); + + /** + * A master or slave sync mode indicating the use of MIDI clock messages. + */ + public static final SyncMode MIDI_SYNC = new SyncMode("MIDI Sync"); + + /** + * A master or slave sync mode indicating the use of MIDI Time Code + * messages. + */ + public static final SyncMode MIDI_TIME_CODE = new SyncMode("MIDI Time Code"); + + /** + * A slave sync mode indicating that no timing info will be transmitted. + */ + public static final SyncMode NO_SYNC = new SyncMode("No Timing"); + + // The name + private String name; + + /** + * Create a new SyncMode object + * @param name the SyncMode name + */ + protected SyncMode(String name) + { + this.name = name; + } + + /** + * SyncMode objects are only equal when identical. + */ + public final boolean equals(Object o) + { + return super.equals(o); + } + + /** + * SyncMode objects use the Object hashCode. + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Use the SyncMode name as the string representation. + * @see java.lang.Object#toString() + */ + public final String toString() + { + return name; + } + } +} diff --git a/libjava/classpath/javax/sound/midi/ShortMessage.java b/libjava/classpath/javax/sound/midi/ShortMessage.java new file mode 100644 index 00000000000..43c0e25fe72 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/ShortMessage.java @@ -0,0 +1,344 @@ +/* ShortMessage.java -- A MIDI message no longer than 3 bytes + Copyright (C) 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 javax.sound.midi; + +/** + * A short MIDI message that is no longer than 3 bytes long. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class ShortMessage extends MidiMessage +{ + /** + * Status byte for System Exclusive message. + */ + public static final int SYSTEM_EXCLUSIVE = 0xF0; + + /** + * Status byte for Time Code message. + */ + public static final int MIDI_TIME_CODE = 0xF1; + + /** + * Status byte for Song Position Pointer message. + */ + public static final int SONG_POSITION_POINTER = 0xF2; + + /** + * Status byte for Song Select message. + */ + public static final int SONG_SELECT = 0xF3; + + /** + * Status byte for Tune Request message. + */ + public static final int TUNE_REQUEST = 0xF6; + + /** + * Status byte for End Of Exclusive message. + */ + public static final int END_OF_EXCLUSIVE = 0xF7; + + /** + * Status byte for Timing Clock message. + */ + public static final int TIMING_CLOCK = 0xF8; + + /** + * Status byte for Start message. + */ + public static final int START = 0xFA; + + /** + * Status byte for Continue message. + */ + public static final int CONTINUE = 0xFB; + + /** + * Status byte for Stop message. + */ + public static final int STOP = 0xFC; + + /** + * Status byte for Active Sensing message. + */ + public static final int ACTIVE_SENSING = 0xFE; + + /** + * Status byte for System Reset message. + */ + public static final int SYSTEM_RESET = 0xFF; + + /** + * Status nibble for Note Off message. + */ + public static final int NOTE_OFF = 0x80; + + /** + * Status nibble for Note On message. + */ + public static final int NOTE_ON = 0x90; + + /** + * Status nibble for Poly Pressure message. + */ + public static final int POLY_PRESSURE = 0xA0; + + /** + * Status nibble for Control Change message. + */ + public static final int CONTROL_CHANGE = 0xB0; + + /** + * Status nibble for Program Change message. + */ + public static final int PROGRAM_CHANGE = 0xC0; + + /** + * Statue nibble for Channel Pressure message. + */ + public static final int CHANNEL_PRESSURE = 0xD0; + + /** + * Status nibble for Pitch Bend message. + */ + public static final int PITCH_BEND = 0xE0; + + // Create and initialize a default, arbitrary message. + private static byte[] defaultMessage; + static + { + defaultMessage = new byte[1]; + defaultMessage[0] = (byte) STOP; + } + + /** + * Create a short MIDI message. + * + * The spec requires that this represent a valid MIDI message, but doesn't + * specify what it should be. We've chosen the STOP message for our + * implementation. + */ + public ShortMessage() + { + this(defaultMessage); + } + + /** + * Create a short MIDI message. + * + * The data argument should be a valid MIDI message. Unfortunately the spec + * does not allow us to throw an InvalidMidiDataException if data is invalid. + * + * @param data the message data + */ + public ShortMessage(byte[] data) + { + super(data); + } + + /** + * Set the MIDI message. + * + * @param status the status byte for this message + * @param data1 the first data byte for this message + * @param data2 the second data byte for this message + * @throws InvalidMidiDataException if status is bad, or data is out of range + */ + public void setMessage(int status, int data1, int data2) + throws InvalidMidiDataException + { + length = getDataLength(status); + length++; + if (data == null || data.length < length) + data = new byte[length]; + data[0] = (byte) status; + if (length > 1) + { + if (data1 < 0 || data1 > 127) + throw new InvalidMidiDataException("data1 (" + data1 + + ") must be between 0 and 127."); + data[1] = (byte) data1; + if (length > 2) + { + if (data2 < 0 || data2 > 127) + throw new InvalidMidiDataException("data2 (" + data2 + + ") must be between 0 and 127."); + data[2] = (byte) data2; + } + } + } + + public void setMessage(int command, int channel, int data1, int data2) + throws InvalidMidiDataException + { + // TODO: This could probably stand some error checking. + // It currently assumes command and channel are valid values. + setMessage(command + channel, data1, data2); + } + + /** + * Set the MIDI message to one that requires no data bytes. + * + * @param status the status byte for this message + * @throws InvalidMidiDataException if status is bad, or requires data + */ + public void setMessage(int status) throws InvalidMidiDataException + { + int length = getDataLength(status); + if (length != 0) + throw new InvalidMidiDataException("Status byte 0x" + + Integer.toHexString(status) + + " requires " + + length + " bytes of data."); + setMessage(status, 0, 0); + } + + + /** + * Return the number of data bytes needed for a given MIDI status byte. + * + * @param status the status byte for a short MIDI message + * @return the number of data bytes needed for this status byte + * @throws InvalidMidiDataException if status is an invalid status byte + */ + protected final int getDataLength(int status) throws InvalidMidiDataException + { + int originalStatus = status; + + if ((status & 0xF0) != 0xF0) + status &= 0xF0; + + switch (status) + { + case NOTE_OFF: + case NOTE_ON: + case POLY_PRESSURE: + case CONTROL_CHANGE: + case PITCH_BEND: + case SONG_POSITION_POINTER: + return 2; + + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + case SONG_SELECT: + case 0xF5: // FIXME: unofficial bus select. Not in spec?? + return 1; + + case SYSTEM_EXCLUSIVE: + return 0; // FIXME: is this correct? + + case TUNE_REQUEST: + case END_OF_EXCLUSIVE: + case TIMING_CLOCK: + case START: + case CONTINUE: + case STOP: + case ACTIVE_SENSING: + case SYSTEM_RESET: + return 0; + + default: + throw new InvalidMidiDataException("Invalid status: 0x" + + Integer.toHexString(originalStatus)); + } + } + + /** + * Get the channel information from this MIDI message, assuming it is a + * MIDI channel message. + * + * @return the MIDI channel for this message + */ + public int getChannel() + { + return data[0] & 0x0F; + } + + /** + * Get the command nibble from this MIDI message, assuming it is a MIDI + * channel message. + * + * @return the MIDI command for this message + */ + public int getCommand() + { + return data[0] & 0xF0; + } + + /** + * Get the first data byte from this message, assuming it exists, and + * zero otherwise. + * + * @return the first data byte or zero if none exists. + */ + public int getData1() + { + if (length > 1) + return data[1]; + else + return 0; + } + + /** + * Get the second data byte from this message, assuming it exists, and + * zero otherwise. + * + * @return the second date byte or zero if none exists. + */ + public int getData2() + { + if (length > 2) + return data[2]; + else + return 0; + } + + /* Create a deep-copy clone of this object. + * @see java.lang.Object#clone() + */ + public Object clone() + { + byte message[] = new byte[length]; + System.arraycopy(data, 0, message, 0, length); + return new ShortMessage(message); + } +} diff --git a/libjava/classpath/javax/sound/midi/Soundbank.java b/libjava/classpath/javax/sound/midi/Soundbank.java new file mode 100644 index 00000000000..16d9d79eaa6 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Soundbank.java @@ -0,0 +1,101 @@ +/* Soundbank.java -- Container of Instruments to be loaded into a Synthesizer + Copyright (C) 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 javax.sound.midi; + +/** + * A Soundbank is a container for instruments which may be loaded into + * a Synthesizer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface Soundbank +{ + /** + * Get the sound bank name. + * + * @return the sound bank name + */ + String getName(); + + /** + * Get the sound bank version. + * + * @return the sound bank version + */ + String getVersion(); + + /** + * Get the sound bank vendor. + * + * @return the sound bank vendor + */ + String getVendor(); + + + /** + * Get the sound bank description. + * + * @return the sound bank description + */ + String getDescription(); + + /** + * Get an array of non-Instrument resources in this sound bank. + * + * @return an array of non-instrument resources in this sound bank + */ + SoundbankResource[] getResources(); + + /** + * Get an array of Instruments in this sound bank. + * + * @return an array of instruments in this sound bank + */ + Instrument[] getInstruments(); + + /** + * Get the Instrument for the given Patch. + * + * @param patch the Patch to search for + * @return the Instrument corresponding to patch + */ + Instrument getInstrument(Patch patch); +} diff --git a/libjava/classpath/javax/sound/midi/SoundbankResource.java b/libjava/classpath/javax/sound/midi/SoundbankResource.java new file mode 100644 index 00000000000..435017e4cf2 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/SoundbankResource.java @@ -0,0 +1,104 @@ +/* SoundbankResource.java -- An audio resource from a sound bank + Copyright (C) 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 javax.sound.midi; + +/** + * SoundbankResource objects represent audio data stored in a sound bank. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class SoundbankResource +{ + private final Soundbank soundbank; + private final String name; + private final Class dataClass; + + /** + * Create a SoundbankResource object. + * + * @param soundbank the soundbank object containing this resource + * @param name the name of the resource + * @param dataClass the class used to represent the audio data + */ + protected SoundbankResource(Soundbank soundbank, String name, Class dataClass) + { + this.soundbank = soundbank; + this.name = name; + this.dataClass = dataClass; + } + + /** + * Get the sound bank containing this resource. + * + * @return the sound bank in which this resource resides + */ + public Soundbank getSoundbank() + { + return soundbank; + } + + /** + * Get the name of this resource. + * + * @return the name of this resource + */ + public String getName() + { + return name; + } + + /** + * Get the class used to represent the audio data for this resource. + * + * @return the class used to represent the audio data for this resource + */ + public Class getDataClass() + { + return dataClass; + } + + /** + * Get the audio data for this resource. + * + * @return the audio data object for this resource + */ + public abstract Object getData(); +} diff --git a/libjava/classpath/javax/sound/midi/Synthesizer.java b/libjava/classpath/javax/sound/midi/Synthesizer.java new file mode 100644 index 00000000000..22e5e9256f4 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Synthesizer.java @@ -0,0 +1,173 @@ +/* Synthesizer.java -- A MIDI audio synthesizer interface + Copyright (C) 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 javax.sound.midi; + +/** + * Interface for MIDI audio synthesizer devices. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface Synthesizer extends MidiDevice +{ + /** + * Get the maximum number of notes that the synth can play at once. + * + * @return the maximum number of notes that the synth can play at once + */ + public int getMaxPolyphony(); + + /** + * The processing latency for this synth in microseconds. + * + * @return the processing latency for this synth in microseconds + */ + public long getLatency(); + + /** + * Get the set of MIDI channels controlled by this synth. + * + * @return an array of MIDI channels controlled by this synth + */ + public MidiChannel[] getChannels(); + + /** + * Get the current status for the voices produced by this synth. + * + * @return an array of VoiceStatus objects, getMaxPolyphony() in length + */ + public VoiceStatus[] getVoiceStatus(); + + /** + * Returns true is this synth is capable of loading soundbank. + * + * @param soundbank the Soundbank to examine + * @return true if soundbank can be loaded, false otherwise + */ + public boolean isSoundbankSupported(Soundbank soundbank); + + /** + * Load an instrument into this synth. The instrument must be part of a + * supported soundbank. + * + * @param instrument the Instrument to load + * @return true if the instrument was loaded and false otherwise + * @throws IllegalArgumentException if this synth doesn't support instrument + */ + public boolean loadInstrument(Instrument instrument); + + /** + * Unload an instrument from this synth. + * + * @param instrument the Instrument to unload + * @throws IllegalArgumentException if this synth doesn't support instrument + */ + public void unloadInstrument(Instrument instrument); + + /** + * Move an intrument from one place to another. The instrument at the + * target location is unloaded. + * + * @param from the instrument source + * @param to the instrument target + * @return if from was remapped + * @throws IllegalArgumentException + */ + public boolean remapInstrument(Instrument from, Instrument to); + + /** + * Get the default Soundbank for this synth. Return null if there is no + * default. + * + * @return the default Soundbank for this synth, possibly null. + */ + public Soundbank getDefaultSoundbank(); + + /** + * Get an array containing all instruments in this synthesizer. + * + * @return an array containing all instruments in this synthesizer + */ + public Instrument[] getAvailableInstruments(); + + /** + * Get an array containing all instruments loaded in this synthesizer. + * + * @return an array containing all instruments loaded in this synthesizer + */ + public Instrument[] getLoadedInstruments(); + + /** + * Load all soundbank instruments into this synthesizer. + * + * @param soundbank the Soundbank from which to load instruments + * @return true if all instruments were loaded, false othewise + * @throws IllegalArgumentException if the soundbank isn't supported by this + */ + public boolean loadAllInstruments(Soundbank soundbank); + + /** + * Unload all soundbank instruments from this synthesizer. + * + * @param soundbank the Soundbank containing the instruments to unload + * @throws IllegalArgumentException if the soundbank isn't supported by this + */ + public void unloadAllInstruments(Soundbank soundbank); + + /** + * Load a subset of soundbank instruments into this synthesizer. The + * subset is defined by an array of Patch objects. + * + * @param soundbank the Soundbank from which to load instruments + * @param patchList the array of patches identifying instruments to load + * @return true if instruments were loaded, false otherwise + * @throws IllegalArgumentException if the soundbank isn't supported by this + */ + public boolean loadInstruments(Soundbank soundbank, Patch[] patchList); + + /** + * Unload a subset of soundbank instruments from this synthesizer. + * + * @param soundbank the Soundbank containing the instruments to unload + * @param patchList the array of patches identifying instruments to unload + * @throws IllegalArgumentException if the soundbank isn't supported by this + */ + public void unloadInstruments(Soundbank soundbank, Patch[] patchList); +} diff --git a/libjava/classpath/javax/sound/midi/SysexMessage.java b/libjava/classpath/javax/sound/midi/SysexMessage.java new file mode 100644 index 00000000000..7ab60f4b6af --- /dev/null +++ b/libjava/classpath/javax/sound/midi/SysexMessage.java @@ -0,0 +1,139 @@ +/* SysexMessage.java -- System Exclusive MIDI message. + Copyright (C) 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 javax.sound.midi; + +/** + * A system exclusive MIDI message. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class SysexMessage extends MidiMessage +{ + public static final int SYSTEM_EXCLUSIVE = 0xF0; + + public static final int SPECIAL_SYSTEM_EXCLUSIVE = 0xF7; + + /** + * Create a default valid system exclusive message. + * + * The official specs don't specify what message is to be + * created. Our implementation creates an empty + * system exclusive message. + */ + public SysexMessage() + { + super(new byte[2]); + data[0] = (byte) SYSTEM_EXCLUSIVE; + data[1] = (byte) ShortMessage.END_OF_EXCLUSIVE; + } + + /** + * Create a SysexMessage object. + * @param data a complete system exclusive message + */ + public SysexMessage(byte[] data) + { + super(data); + } + + /** + * Set the sysex message. The first data byte (status) must be + * 0xF0 or 0xF7. + * + * @param data the message data + * @param length the length of the message data + * @throws InvalidMidiDataException if the status byte is not 0xF0 or 0xF7 + */ + public void setMessage(byte[] data, int length) + throws InvalidMidiDataException + { + if (data[0] != SYSTEM_EXCLUSIVE + || data[0] != SPECIAL_SYSTEM_EXCLUSIVE) + throw new InvalidMidiDataException("Sysex message starts with 0x" + + Integer.toHexString(data[0]) + + " instead of 0xF0 or 0xF7"); + super.setMessage(data, length); + } + + /** + * Set the sysex message. status must be either 0xF0 or 0xF7. + * + * @param status the sysex statys byte (0xF0 or 0xF7) + * @param data the message data + * @param length the length of the message data + * @throws InvalidMidiDataException if status is not 0xF0 or 0xF7 + */ + public void setMessage(int status, byte[] data, int length) + throws InvalidMidiDataException + { + if (status != SYSTEM_EXCLUSIVE + || status != SPECIAL_SYSTEM_EXCLUSIVE) + throw new InvalidMidiDataException("Sysex message starts with 0x" + + Integer.toHexString(status) + + " instead of 0xF0 or 0xF7"); + this.data = new byte[length+1]; + this.data[0] = (byte) status; + System.arraycopy(data, 0, this.data, 1, length); + this.length = length+1; + } + + /** + * Get the data for this message, not including the status byte. + * @return the message data, not including the status byte + */ + public byte[] getData() + { + byte[] result = new byte[length - 1]; + System.arraycopy(data, 1, result, 0, length - 1); + return result; + } + + /* Create a deep-copy clone of this object. + * @see java.lang.Object#clone() + */ + public Object clone() + { + byte message[] = new byte[length]; + System.arraycopy(data, 0, message, 0, length); + return new SysexMessage(message); + } +} + diff --git a/libjava/classpath/javax/sound/midi/Track.java b/libjava/classpath/javax/sound/midi/Track.java new file mode 100644 index 00000000000..da7ef2ef3b9 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Track.java @@ -0,0 +1,160 @@ +/* Track.java -- A track of MIDI events + Copyright (C) 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 javax.sound.midi; + +import java.util.HashSet; +import java.util.Vector; + +/** + * A Track contains a list of timecoded MIDI events for processing + * by a Sequencer. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class Track +{ + /** + * The list of MidiEvents for this track. + */ + protected Vector events; + + // A HashSet to speed processing + private HashSet eventSet; + + /** + * Add a new event to this track. Specific events may only be added once. + * The event will be inserted into the appropriate spot in the event list + * based on its timecode. + * + * @param event the event to add + * @return true if the event was added, false otherwise + */ + public boolean add(MidiEvent event) + { + synchronized (events) + { + if (eventSet.contains(event)) + return false; + + eventSet.add(event); + + long targetTick = event.getTick(); + int i = events.size() - 1; + while (i >= 0 && (((MidiEvent)events.get(i)).getTick() > targetTick)) + i--; + events.add(i+1, event); + return true; + } + } + + /** + * Remove an event from this track. + * + * @param event the event to remove + * @return true if the event was removed, false otherwise + */ + public boolean remove(MidiEvent event) + { + synchronized (events) + { + if (! eventSet.remove(event)) + return false; + + int i = events.indexOf(event); + if (i >= 0) + { + events.remove(i); + return true; + } + + throw new InternalError("event in set but not list"); + } + } + + /** + * Get an event idetified by its order index + * + * @param index the location of the event to get + * @return the event at index + * @throws ArrayIndexOutOfBoundsException if index is out of bounds + */ + public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException + { + synchronized (events) + { + try + { + return (MidiEvent) events.get(index); + } + catch (IndexOutOfBoundsException e) + { + throw (ArrayIndexOutOfBoundsException) + new ArrayIndexOutOfBoundsException().initCause(e); + } + } + } + + + /** + * Get the number events in this track. + * + * @return the number of events in this track + */ + public int size() + { + return events.size(); + } + + /** + * Get the length of the track in MIDI ticks. + * + * @return the length of the track in MIDI ticks + */ + public long ticks() + { + synchronized (events) + { + int size = events.size(); + return ((MidiEvent) events.get(size - 1)).getTick(); + } + } + } + diff --git a/libjava/classpath/javax/sound/midi/Transmitter.java b/libjava/classpath/javax/sound/midi/Transmitter.java new file mode 100644 index 00000000000..d788725e065 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/Transmitter.java @@ -0,0 +1,70 @@ +/* Transmitter.java -- A interface for objects sending MIDI events + Copyright (C) 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 javax.sound.midi; + +/** + * This interface specifies the methods required by objects which send + * MIDI events to Receivers, including MIDI IN ports and Sequencers. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public interface Transmitter +{ + /** + * Set the Receiver to which MIDI events will be sent. + * + * @param receiver the Receiver to which MIDI events will be sent + */ + public void setReceiver(Receiver receiver); + + /** + * Get the Receiver to which MIDI events will be sent (possibly null) + * + * @return the Receiver to which MIDI events will be sent (possibly null) + */ + public Receiver getReceiver(); + + /** + * Close this Transmitter, possibly releasing system resources. + * FIXME: Does this mean the Receiver is closed? I think it must. + */ + public void close(); +} diff --git a/libjava/classpath/javax/sound/midi/VoiceStatus.java b/libjava/classpath/javax/sound/midi/VoiceStatus.java new file mode 100644 index 00000000000..a6a9c3f87c8 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/VoiceStatus.java @@ -0,0 +1,79 @@ +/* VoiceStatus.java -- the current status of a Synthesizer voice + Copyright (C) 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 javax.sound.midi; + +/** + * Objects of this type define the status of a Synthesizer voice. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public class VoiceStatus +{ + /** + * True if this voice is processing a MIDI note. + */ + public boolean active = false; + + /** + * The channel for this voice when active. + */ + public int channel = 0; + + /** + * The bank of the voice when active. + */ + public int bank = 0; + + /** + * The program for this voice when active. + */ + public int program = 0; + + /** + * The note for this voice when active. + */ + public int note = 0; + + /** + * The volume for this voice when active. + */ + public int volume = 0; +} diff --git a/libjava/classpath/javax/sound/midi/spi/MidiDeviceProvider.java b/libjava/classpath/javax/sound/midi/spi/MidiDeviceProvider.java new file mode 100644 index 00000000000..537edb2b3d0 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/spi/MidiDeviceProvider.java @@ -0,0 +1,90 @@ +/* MidiDeviceProvider.java -- Abstract parent for a MIDI device provider. + Copyright (C) 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 javax.sound.midi.spi; + +import javax.sound.midi.*; + +/** + * The abstract base class for all MidiDeviceProvider types. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class MidiDeviceProvider +{ + /** + * Returns true if this provider supports a specific MIDI device. + * + * @param info the MIDI device descriptor + * @return true if this provider supports info + */ + public boolean isDeviceSupported(MidiDevice.Info info) + { + MidiDevice.Info infos[] = getDeviceInfo(); + + int i = infos.length; + + while (i > 0) + { + if (info.equals(infos[--i])) + return true; + } + + return false; + } + + /** + * Get the list descriptors for all MIDI devices supported by + * this provider. + * + * @return an array of descriptors for all supported MIDI devices. + */ + public abstract MidiDevice.Info[] getDeviceInfo(); + + /** + * Get the MidiDevice for the MIDI device described by info + * + * @param info the descriptor for the MIDI device we want + * @return the MidiDevice we're looking for + * @throws IllegalArgumentException is this provider doesn't support info + */ + public abstract MidiDevice getDevice(MidiDevice.Info info) + throws IllegalArgumentException; +} diff --git a/libjava/classpath/javax/sound/midi/spi/MidiFileReader.java b/libjava/classpath/javax/sound/midi/spi/MidiFileReader.java new file mode 100644 index 00000000000..4342ebf5e2b --- /dev/null +++ b/libjava/classpath/javax/sound/midi/spi/MidiFileReader.java @@ -0,0 +1,125 @@ +/* MidiFilerReader.java -- MIDI file reading services + Copyright (C) 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 javax.sound.midi.spi; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MidiFileFormat; +import javax.sound.midi.Sequence; + +/** + * The MidiFileReader abstract class defines the methods to be provided + * by a MIDI file reader. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class MidiFileReader +{ + /** + * Read a MidiFileFormat from the given stream. + * + * @param stream the stream from which to read the MIDI data + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if the stream refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract MidiFileFormat getMidiFileFormat(InputStream stream) + throws InvalidMidiDataException, IOException; + + /** + * Read a MidiFileFormat from the given stream. + * + * @param url the url from which to read the MIDI data + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if the url refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract MidiFileFormat getMidiFileFormat(URL url) + throws InvalidMidiDataException, IOException; + + /** + * Read a MidiFileFormat from the given stream. + * + * @param file the file from which to read the MIDI data + * @return the MidiFileFormat object + * @throws InvalidMidiDataException if the file refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract MidiFileFormat getMidiFileFormat(File file) + throws InvalidMidiDataException, IOException; + + /** + * Read a Sequence from the given stream. + * + * @param stream the stream from which to read the MIDI data + * @return the Sequence object + * @throws InvalidMidiDataException if the stream refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract Sequence getSequence(InputStream stream) + throws InvalidMidiDataException, IOException; + + /** + * Read a Sequence from the given stream. + * + * @param url the url from which to read the MIDI data + * @return the Sequence object + * @throws InvalidMidiDataException if the url refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract Sequence getSequence(URL url) + throws InvalidMidiDataException, IOException; + + /** + * Read a Sequence from the given stream. + * + * @param file the file from which to read the MIDI data + * @return the Sequence object + * @throws InvalidMidiDataException if the file refers to invalid data + * @throws IOException if an I/O exception occurs while reading + */ + public abstract Sequence getSequence(File file) + throws InvalidMidiDataException, IOException; +} diff --git a/libjava/classpath/javax/sound/midi/spi/MidiFileWriter.java b/libjava/classpath/javax/sound/midi/spi/MidiFileWriter.java new file mode 100644 index 00000000000..e2a1f55c6e0 --- /dev/null +++ b/libjava/classpath/javax/sound/midi/spi/MidiFileWriter.java @@ -0,0 +1,143 @@ +/* MidiFileWriter.java -- MIDI file writing services + Copyright (C) 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 javax.sound.midi.spi; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import javax.sound.midi.Sequence; + +/** + * MidiFileWriter provides MIDI file writing services. + * + * There are three types of Standard MIDI File (SMF) formats, + * represented by integers 0, 1, and 2. + * + * Type 0 files contain a single track and represents a single song + * performance. + * Type 1 may contain multiple tracks for a single song performance. + * Type 2 may contain multiple tracks, each representing a + * separate song performance. + * + * See http://en.wikipedia.org/wiki/MIDI#MIDI_file_formats for more + * information. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class MidiFileWriter +{ + /** + * Return the MIDI file types supported by this writer. + * + * @return the MIDI file types, or an empty array + */ + public abstract int[] getMidiFileTypes(); + + /** + * Return the MIDI file types supported by this writer for the + * given sequence. + * + * @param sequence the sequence we'd like to write + * @return the MIDI file types, or an empty array + */ + public abstract int[] getMidiFileTypes(Sequence sequence); + + /** + * Returns true if this writer supports the given file type. + * + * @param fileType the file type we're asking about + * @return true if this writer supports fileType, false otherwise + */ + public boolean isFileTypeSupported(int fileType) + { + int types[] = getMidiFileTypes(); + for (int i = types.length; i > 0;) + { + if (types[--i] == fileType) + return true; + } + return false; + } + + /** + * Returns true if this writer supports the given file type for the + * given sequence. + * + * @param fileType the file type we're asking about + * @param sequence the sequence we'd like to write + * @return true if this writer supports fileType, false otherwise + */ + public boolean isFileTypeSupported(int fileType, Sequence sequence) + { + int types[] = getMidiFileTypes(sequence); + for (int i = types.length; i > 0;) + { + if (types[--i] == fileType) + return true; + } + return false; + } + + /** + * Write a sequence to a stream using the specified MIDI file type. + * + * @param in the sequence to write + * @param fileType the MIDI file type to use + * @param out the output stream to write to + * @return the number of byte written + * @throws IOException if an I/O exception happens + */ + public abstract int write(Sequence in, int fileType, OutputStream out) + throws IOException; + + /** + * Write a sequence to a file using the specified MIDI file type. + * + * @param in the sequence to write + * @param fileType the MIDI file type to use + * @param out the file to write to + * @return the number of byte written + * @throws IOException if an I/O exception happens + */ + public abstract int write(Sequence in, int fileType, File out) + throws IOException; +} diff --git a/libjava/classpath/javax/sound/midi/spi/SoundbankReader.java b/libjava/classpath/javax/sound/midi/spi/SoundbankReader.java new file mode 100644 index 00000000000..dbf2bb0183a --- /dev/null +++ b/libjava/classpath/javax/sound/midi/spi/SoundbankReader.java @@ -0,0 +1,97 @@ +/* SoundbankReader.java -- Soundbank file reading services + Copyright (C) 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 javax.sound.midi.spi; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.Soundbank; + +/** + * The SoundbankReader abstract class defines the methods to be provided + * by a soundbank file reader. + * + * @author Anthony Green (green@redhat.com) + * @since 1.3 + * + */ +public abstract class SoundbankReader +{ + /** + * Get a Soundbank from the given URL. + * + * @param url from which to read the Soundbank + * + * @return the Soundbank object + * + * @throws InvalidMidiDataException if the data provided by url cannot be recognized + * @throws IOException if the data provided by url cannot be read + */ + public abstract Soundbank getSoundbank(URL url) + throws InvalidMidiDataException, IOException; + + /** + * Get a Soundbank from the given InputStream. + * + * @param stream from which to read the Soundbank + * + * @return the Soundbank object + * + * @throws InvalidMidiDataException if the data provided by InputStream cannot be recognized + * @throws IOException if the data provided by InputStream cannot be read + */ + public abstract Soundbank getSoundbank(InputStream stream) + throws InvalidMidiDataException, IOException; + + /** + * Get a Soundbank from the given File. + * + * @param file from which to read the Soundbank + * + * @return the Soundbank object + * + * @throws InvalidMidiDataException if the data provided by File cannot be recognized + * @throws IOException if the data provided by File cannot be read + */ + public abstract Soundbank getSoundbank(File file) + throws InvalidMidiDataException, IOException; +} diff --git a/libjava/classpath/javax/sound/sampled/AudioFileFormat.java b/libjava/classpath/javax/sound/sampled/AudioFileFormat.java new file mode 100644 index 00000000000..81bbe4ecf10 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/AudioFileFormat.java @@ -0,0 +1,242 @@ +/* Audio file format + Copyright (C) 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 javax.sound.sampled; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This describes an audio file, including information about its length, + * the format of the audio data, and other things. + * @since 1.3 + */ +public class AudioFileFormat +{ + /** + * An instance of this type describes a standard audio file format. + * @since 1.3 + */ + public static class Type + { + // This is kind of goofy since there are multiple extensions for + // some of these. + + /** The AIFC format. */ + public static final Type AIFC = new Type("AIFC", "aifc"); + + /** The AIFF format. */ + public static final Type AIFF = new Type("AIFF", "aiff"); + + /** The AU format. */ + public static final Type AU = new Type("AU", "au"); + + /** The SND format. */ + public static final Type SND = new Type("SND", "snd"); + + /** The WAVE format. */ + public static final Type WAVE = new Type ("WAVE", "wav"); + + private String name; + private String extension; + + /** + * Create a new Type given its name and file extension. + * The file extension does not include the ".". + * @param name the type's name + * @param extension the file extension + */ + public Type(String name, String extension) + { + this.name = name; + this.extension = extension; + } + + public final boolean equals(Object o) + { + if (! (o instanceof Type)) + return false; + Type other = (Type) o; + return name.equals(other.name) && extension.equals(other.extension); + } + + public final int hashCode() + { + return name.hashCode() + extension.hashCode(); + } + + /** + * Return the extension associated with this Type. + */ + public String getExtension() + { + return extension; + } + + /** + * Return the name of this Type. + */ + public final String toString() + { + return name; + } + } + + private int byteLength; + private AudioFormat format; + private Type type; + private int frameLength; + private Map properties; + + /** + * Create a new AudioFileFormat given the type, the format, and the + * frame length. The new object will have an unspecified byte length, + * and an empty properties map. + * @param type the type + * @param fmt the format + * @param frameLen the frame length + */ + public AudioFileFormat(Type type, AudioFormat fmt, int frameLen) + { + this.byteLength = AudioSystem.NOT_SPECIFIED; + this.format = fmt; + this.type = type; + this.frameLength = frameLen; + this.properties = Collections.EMPTY_MAP; + } + + /** + * Create a new AudioFileFormat given the type, the format, the + * frame length, and some properties. The new object will have an + * unspecified byte length. A copy of the properties argument will + * be made, so changes to the map passed in will not affect the + * new AudioFileFormat. + * @param type the type + * @param fmt the format + * @param frameLen the frame length + * @param properties the properties + */ + public AudioFileFormat(Type type, AudioFormat fmt, int frameLen, + Map properties) + { + this.byteLength = AudioSystem.NOT_SPECIFIED; + this.format = fmt; + this.type = type; + this.frameLength = frameLen; + this.properties = Collections.unmodifiableMap(new HashMap(properties)); + } + + /** + * Create a new AudioFileFormat given the type, the byte length, the format, + * and the frame length. The new object will have an empty properties map. + * @param type the type + * @param byteLen the byte length + * @param fmt the format + * @param frameLen the frame length + */ + protected AudioFileFormat(Type type, int byteLen, AudioFormat fmt, + int frameLen) + { + this.byteLength = byteLen; + this.format = fmt; + this.type = type; + this.frameLength = frameLen; + this.properties = Collections.EMPTY_MAP; + } + + /** + * Return the byte length of this file format. + */ + public int getByteLength() + { + return byteLength; + } + + /** + * Return the AudioFormat associated with this file format. + */ + public AudioFormat getFormat() + { + return format; + } + + /** + * Return the frame length of this file format. + */ + public int getFrameLength() + { + return frameLength; + } + + /** + * Return the value of a property defined in this format. + * @param key the property name + * @return the value of the property, or null if the property is not defined + */ + public Object getProperty(String key) + { + return properties.get(key); + } + + /** + * Return the Type associated with this file format. + */ + public Type getType() + { + return type; + } + + /** + * Return the properties associated with this format, as a Map. + * The returned Map is unmodifiable. + */ + public Map properties() + { + return properties; + } + + /** + * Return a description of this AudioFileFormat. + */ + public String toString() + { + return ("byteLength=" + byteLength + "; format=" + format + + "; type=" + type + "; frameLength=" + frameLength); + } +} diff --git a/libjava/classpath/javax/sound/sampled/AudioFormat.java b/libjava/classpath/javax/sound/sampled/AudioFormat.java new file mode 100644 index 00000000000..5199d71c3a3 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/AudioFormat.java @@ -0,0 +1,345 @@ +/* An audio format + Copyright (C) 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 javax.sound.sampled; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class describes an audio format, including its encoding, + * the number of channels, its frame rate, etc. + * @since 1.3 + */ +public class AudioFormat +{ + /** + * This describes a given audio format encoding. + * @since 1.3 + */ + public static class Encoding + { + /** The ALAW encoding. */ + public static final Encoding ALAW = new Encoding("alaw"); + + /** The signed PCM encoding. */ + public static final Encoding PCM_SIGNED = new Encoding("pcm_signed"); + + /** The unsigned PCM encoding. */ + public static final Encoding PCM_UNSIGNED = new Encoding("pcm_unsigned"); + + /** The ULAW encoding. */ + public static final Encoding ULAW = new Encoding("ulaw"); + + private String name; + + /** + * Create a new encoding descriptor, given its name. + * @param name the name + */ + public Encoding(String name) + { + this.name = name; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the name of this encoding. + */ + public final String toString() + { + return name; + } + } + + /** + * True if the audio data is stored big-endian. + */ + protected boolean bigEndian; + + /** + * The number of channels of data in this format. + */ + protected int channels; + + /** + * The encoding of this format. + */ + protected Encoding encoding; + + /** + * The frame rate of this format. This is the number of frames + * per second. + */ + protected float frameRate; + + /** + * The number of bytes per frame in this format. + */ + protected int frameSize; + + /** + * The number of samples per second. + */ + protected float sampleRate; + + /** + * The number of bits in each sample. + */ + protected int sampleSizeInBits; + + private Map properties; + + /** + * Create a new audio format, given various attributes of it. + * The properties map for this format will be empty. + * + * @param encoding the encoding for this format + * @param sampleRate the sample rate + * @param sampleSizeInBits the sample size, in bits + * @param channels the number of channels + * @param frameSize the frame size, in bytes + * @param frameRate the frame rate, in frames per second + * @param bigEndian true if the data is stored big-endian + */ + public AudioFormat(Encoding encoding, float sampleRate, int sampleSizeInBits, + int channels, int frameSize, float frameRate, + boolean bigEndian) + { + this.encoding = encoding; + this.sampleRate = sampleRate; + this.sampleSizeInBits = sampleSizeInBits; + this.channels = channels; + this.frameSize = frameSize; + this.frameRate = frameRate; + this.bigEndian = bigEndian; + this.properties = Collections.EMPTY_MAP; + } + + /** + * Create a new audio format, given various attributes of it. + * The properties map is copied by this constructor, so changes + * to the argument Map will not affect the new object. + * + * @param encoding the encoding for this format + * @param sampleRate the sample rate + * @param sampleSizeInBits the sample size, in bits + * @param channels the number of channels + * @param frameSize the frame size, in bytes + * @param frameRate the frame rate, in frames per second + * @param bigEndian true if the data is stored big-endian + * @param properties a map describing properties of this format + */ + public AudioFormat(Encoding encoding, float sampleRate, int sampleSizeInBits, + int channels, int frameSize, float frameRate, + boolean bigEndian, Map properties) + { + this.encoding = encoding; + this.sampleRate = sampleRate; + this.sampleSizeInBits = sampleSizeInBits; + this.channels = channels; + this.frameSize = frameSize; + this.frameRate = frameRate; + this.bigEndian = bigEndian; + this.properties = Collections.unmodifiableMap(new HashMap(properties)); + } + + /** + * Create a new PCM-based audio format, given various attributes of it. + * The encoding will either be Encoding#PCM_SIGNED or Encoding#PCM_UNSIGNED. + * The frame size for this format will be derived from the sample size in + * bits and the number of channels, unless one of those is + * AudioSystem#NOT_SPECIFIED. The frame rate will be the same as the sample + * rate, and the properties map will be empty. + * + * @param sampleRate the sample rate + * @param sampleSizeInBits the sample size, in bits + * @param channels the number of channels + * @param signed true if this is a signed encoding + * @param bigEndian true if the data is stored big-endian + */ + public AudioFormat(float sampleRate, int sampleSizeInBits, + int channels, boolean signed, boolean bigEndian) + { + this.encoding = signed ? Encoding.PCM_SIGNED : Encoding.PCM_UNSIGNED; + this.sampleRate = sampleRate; + this.sampleSizeInBits = sampleSizeInBits; + this.channels = channels; + // It isn't clear whether channels can be NOT_SPECIFIED. + if (sampleSizeInBits == AudioSystem.NOT_SPECIFIED + || channels == AudioSystem.NOT_SPECIFIED) + this.frameSize = AudioSystem.NOT_SPECIFIED; + else + this.frameSize = (sampleSizeInBits + 7) / 8 * channels; + this.frameRate = sampleRate; + this.bigEndian = bigEndian; + this.properties = Collections.EMPTY_MAP; + } + + /** + * Return the number of channels in this format. + */ + public int getChannels() + { + return channels; + } + + /** + * Return the encoding of this format. + */ + public Encoding getEncoding() + { + return encoding; + } + + /** + * Return the frame rate of this format. + */ + public float getFrameRate() + { + return frameRate; + } + + /** + * Return the frame size of this format. + */ + public int getFrameSize() + { + return frameSize; + } + + /** + * Given a key, return a property associated with this format; + * or null if this property is not set. + * @param key the name of the property + * @return the value of the property, or null if the property is not set + */ + public Object getProperty(String key) + { + return properties.get(key); + } + + /** + * Return the sample rate of this format. + */ + public float getSampleRate() + { + return sampleRate; + } + + /** + * Return the sample size of this format, in bits. + */ + public int getSampleSizeInBits() + { + return sampleSizeInBits; + } + + /** + * Return true if this format is big endian, false otherwise. + * This only matters for formats whose sample size is greater than + * one byte. + */ + public boolean isBigEndian() + { + return bigEndian; + } + + /** + * Return true if this audio format matches another. + * @param fmt the format to match against + * @return true if they match, false otherwise + */ + public boolean matches(AudioFormat fmt) + { + if (! encoding.equals(fmt.encoding) + || channels != fmt.channels + || sampleSizeInBits != fmt.sampleSizeInBits + || frameSize != fmt.frameSize) + return false; + if (sampleRate != AudioSystem.NOT_SPECIFIED + && fmt.sampleRate != AudioSystem.NOT_SPECIFIED + && sampleRate != fmt.sampleRate) + return false; + if (frameRate != AudioSystem.NOT_SPECIFIED + && fmt.frameRate != AudioSystem.NOT_SPECIFIED + && frameRate != fmt.frameRate) + return false; + if (sampleSizeInBits > 8) + return bigEndian == fmt.bigEndian; + return true; + } + + /** + * Return a read-only Map holding the properties associated with + * this format. + */ + public Map properties() + { + return properties; + } + + /** + * Return a description of this format. + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append(encoding); + result.append(" "); + result.append(sampleRate); + result.append(" Hz "); + result.append(sampleSizeInBits); + result.append(" bits "); + result.append(channels); + result.append(" channels"); + if (sampleSizeInBits > 8) + result.append(bigEndian ? " big endian" : " little endian"); + return result.toString(); + } +} diff --git a/libjava/classpath/javax/sound/sampled/AudioInputStream.java b/libjava/classpath/javax/sound/sampled/AudioInputStream.java new file mode 100644 index 00000000000..863578b0b6d --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/AudioInputStream.java @@ -0,0 +1,258 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This is an InputStream which is specialized for reading audio files. + * In particular it only allows operations to act on a multiple of + * the audio stream's frame size. + * @since 1.3 + */ +public class AudioInputStream extends InputStream +{ + /** The format of the audio stream. */ + protected AudioFormat format; + + /** The length of the audio stream in frames. */ + protected long frameLength; + + /** The current frame position, starting from frame zero. */ + protected long framePos; + + /** The size of a frame in bytes. */ + protected int frameSize; + + // I wonder why this class doesn't inherit from FilterInputStream. + private InputStream input; + + // The saved frame position, used for mark/reset. + private long markedFramePos; + + /** + * Create a new AudioInputStream given an underlying InputStream, + * the audio format, and the length of the data in frames. The + * frame size is taken from the format. + * @param is the underlying input stream + * @param fmt the format of the data + * @param length the length of the data in frames + */ + public AudioInputStream(InputStream is, AudioFormat fmt, long length) + { + this.format = fmt; + this.frameLength = length; + this.framePos = 0; + this.frameSize = fmt.getFrameSize(); + this.input = is; + } + + /** + * Create a new AudioInputStream given a TargetDataLine. The audio + * format and the frame size are taken from the line. + * @param line the TargetDataLine + */ + public AudioInputStream(TargetDataLine line) + { + this(new TargetInputStream(line), line.getFormat(), + AudioSystem.NOT_SPECIFIED); + } + + /** + * Return the number of bytes available to be read from the + * underlying stream. This wrapper method ensures that the result + * is always a multiple of the frame size. + */ + public int available() throws IOException + { + int result = input.available(); + // Ensure result is a multiple of the frame size. + if (frameSize != AudioSystem.NOT_SPECIFIED) + result -= result % frameSize; + return result; + } + + /** + * Close the stream. + */ + public void close() throws IOException + { + input.close(); + } + + /** + * Get the format associated with this stream. + * @return the AudioFormat + */ + public AudioFormat getFormat() + { + return format; + } + + /** + * Get the length of this stream in frames. Note that this + * may be AudioSystem#NOT_SPECIFIED. + * @return the length of the stream in frames + */ + public long getFrameLength() + { + return frameLength; + } + + public void mark(int limit) + { + input.mark(limit); + markedFramePos = framePos; + } + + /** + * Return true if the underlying stream supports mark and reset, + * false otherwise. + */ + public boolean markSupported() + { + return input.markSupported(); + } + + /** + * Read a single byte from the underlying stream. If the frame + * size is set, and is not one byte, an IOException will be thrown. + */ + public int read() throws IOException + { + if (frameSize != 1) + throw new IOException("frame size must be 1 for read()"); + int result; + if (framePos == frameLength) + result = -1; + else + result = input.read(); + if (result != -1) + ++framePos; + return result; + } + + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + public int read(byte[] buf, int offset, int length) throws IOException + { + int result; + if (framePos == frameLength) + result = -1; + else + { + int myFrameSize = (frameSize == AudioSystem.NOT_SPECIFIED + ? 1 : frameSize); + // Ensure length is a multiple of frame size. + length -= length % myFrameSize; + + result = 0; + while (result == 0 || result % myFrameSize != 0) + { + int val = input.read(buf, offset, length); + if (val < 0) + { + // This is a weird situation as we might have read a + // frame already. It isn't clear at all what to do if + // we only found a partial frame. For now we just + // return whatever we did find. + if (result == 0) + return -1; + result -= result % myFrameSize; + break; + } + result += val; + } + // assert result % myFrameSize == 0; + framePos += result / myFrameSize; + } + return result; + } + + public void reset() throws IOException + { + input.reset(); + framePos = markedFramePos; + } + + public long skip(long n) throws IOException + { + if (frameSize != AudioSystem.NOT_SPECIFIED) + n -= n % frameSize; + long actual = input.skip(n); + if (frameSize != AudioSystem.NOT_SPECIFIED) + framePos += actual / frameSize; + return actual; + } + + private static class TargetInputStream extends InputStream + { + private TargetDataLine line; + private byte[] buf; + + /** + * Create a new TargetInputStream. + * @param line the line to wrap + */ + public TargetInputStream(TargetDataLine line) + { + this.line = line; + // FIXME: do we have to call line.open()? + } + + public synchronized int read() throws IOException + { + if (buf == null) + buf = new byte[1]; + int count = read(buf, 0, 1); + if (count < 0) + return -1; + return buf[0]; + } + + public int read(byte[] buf, int offset, int length) throws IOException + { + return line.read(buf, offset, length); + } + } +} diff --git a/libjava/classpath/javax/sound/sampled/AudioPermission.java b/libjava/classpath/javax/sound/sampled/AudioPermission.java new file mode 100644 index 00000000000..6698e35ced2 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/AudioPermission.java @@ -0,0 +1,70 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +import java.security.BasicPermission; + +/** + * This represents the permission to use an audio device. + * The only predefined permission names are "play" and "record". + * @since 1.3 + */ +public class AudioPermission extends BasicPermission +{ + private static final long serialVersionUID = -5518053473477801126L; + + /** + * Construct an AudioPermission with the given name. + * @param name the name of the permission, like "play" or "record" + */ + public AudioPermission(String name) + { + super(name); + } + + /** + * Construct an AudioPermission with the given name. + * @param name the name of the permission, like "play" or "record" + * @param actions the actions; should be null + */ + public AudioPermission(String name, String actions) + { + super(name, actions); + } +} diff --git a/libjava/classpath/javax/sound/sampled/AudioSystem.java b/libjava/classpath/javax/sound/sampled/AudioSystem.java new file mode 100644 index 00000000000..0b0b754b59e --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/AudioSystem.java @@ -0,0 +1,744 @@ +/* Main interface to audio system + Copyright (C) 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 javax.sound.sampled; + +import gnu.classpath.ServiceFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.HashSet; +import java.util.Iterator; + +import javax.sound.sampled.spi.AudioFileReader; +import javax.sound.sampled.spi.AudioFileWriter; +import javax.sound.sampled.spi.FormatConversionProvider; +import javax.sound.sampled.spi.MixerProvider; + +/** + * This clas is the primary interface to the audio system. It contains + * a number of static methods which can be used to access this package's + * functionality. + * + * @since 1.3 + */ +public class AudioSystem +{ + /** + * A constant which can be passed to a number of methods in this package, + * to indicate an unspecified value. + */ + public static final int NOT_SPECIFIED = -1; + + /** + * Return the file format of a given File. + * @param f the file to check + * @return the format of the file + * @throws UnsupportedAudioFileException if the file's format is not + * recognized + * @throws IOException if there is an I/O error reading the file + */ + public static AudioFileFormat getAudioFileFormat(File f) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioFileFormat(f); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("file type not recognized"); + } + + /** + * Return the file format of a given input stream. + * @param is the input stream to check + * @return the format of the stream + * @throws UnsupportedAudioFileException if the stream's format is not + * recognized + * @throws IOException if there is an I/O error reading the stream + */ + public static AudioFileFormat getAudioFileFormat(InputStream is) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioFileFormat(is); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("input stream type not recognized"); + } + + /** + * Return the file format of a given URL. + * @param url the URL to check + * @return the format of the URL + * @throws UnsupportedAudioFileException if the URL's format is not + * recognized + * @throws IOException if there is an I/O error reading the URL + */ + public static AudioFileFormat getAudioFileFormat(URL url) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioFileFormat(url); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("URL type not recognized"); + } + + /** + * Return an array of all the supported AudioFileFormat types. + * @return an array of unique types + */ + public static AudioFileFormat.Type[] getAudioFileTypes() + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class); + while (i.hasNext()) + { + AudioFileWriter writer = (AudioFileWriter) i.next(); + AudioFileFormat.Type[] types = writer.getAudioFileTypes(); + for (int j = 0; j < types.length; ++j) + result.add(types[j]); + } + return (AudioFileFormat.Type[]) result.toArray(new AudioFileFormat.Type[result.size()]); + } + + /** + * Return an array of all the supported AudioFileFormat types which match the + * given audio input stream + * @param ais the audio input stream + * @return an array of unique types + */ + public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream ais) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class); + while (i.hasNext()) + { + AudioFileWriter writer = (AudioFileWriter) i.next(); + AudioFileFormat.Type[] types = writer.getAudioFileTypes(ais); + for (int j = 0; j < types.length; ++j) + result.add(types[j]); + } + return (AudioFileFormat.Type[]) result.toArray(new AudioFileFormat.Type[result.size()]); + } + + /** + * Given an audio input stream, this will try to create a new audio input + * stream whose encoding matches the given target encoding. If no provider + * offers this conversion, an exception is thrown. + * @param targ the target encoding + * @param ais the original audio stream + * @return a new audio stream + * @throws IllegalArgumentException if the conversion cannot be made + */ + public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targ, + AudioInputStream ais) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + if (! prov.isConversionSupported(targ, ais.getFormat())) + continue; + return prov.getAudioInputStream(targ, ais); + } + throw new IllegalArgumentException("encoding not supported for stream"); + } + + /** + * Given an audio input stream, this will try to create a new audio input + * stream whose format matches the given target format. If no provider + * offers this conversion, an exception is thrown. + * @param targ the target format + * @param ais the original audio stream + * @return a new audio stream + * @throws IllegalArgumentException if the conversion cannot be made + */ + public static AudioInputStream getAudioInputStream(AudioFormat targ, + AudioInputStream ais) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + if (! prov.isConversionSupported(targ, ais.getFormat())) + continue; + return prov.getAudioInputStream(targ, ais); + } + throw new IllegalArgumentException("format not supported for stream"); + } + + /** + * Return an audio input stream for the file. + * @param f the file to read + * @return an audio input stream for the file + * @throws UnsupportedAudioFileException if the file's audio format is not + * recognized + * @throws IOException if there is an error while reading the file + */ + public static AudioInputStream getAudioInputStream(File f) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioInputStream(f); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("file type not recognized"); + } + + /** + * Return an audio input stream given an input stream. + * @param is the input stream + * @return an audio input stream + * @throws UnsupportedAudioFileException if the input stream's audio format + * is not supported by any of the installed providers + * @throws IOException if there is an error while reading the input stream + */ + public static AudioInputStream getAudioInputStream(InputStream is) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioInputStream(is); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("input stream type not recognized"); + } + + /** + * Return an audio input stream for the given URL. + * @param url the URL + * @return an audio input stream + * @throws UnsupportedAudioFileException if the URL's audio format is not + * supported by any of the installed providers + * @throws IOException if there is an error while reading the URL + */ + public static AudioInputStream getAudioInputStream(URL url) + throws UnsupportedAudioFileException, IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class); + while (i.hasNext()) + { + AudioFileReader reader = (AudioFileReader) i.next(); + try + { + return reader.getAudioInputStream(url); + } + catch (UnsupportedAudioFileException _) + { + // Try the next provider. + } + } + throw new UnsupportedAudioFileException("URL type not recognized"); + } + + /** + * Return a new clip which can be used for playing back an audio stream. + * @throws LineUnavailableException if a clip is not available for some + * reason + * @throws SecurityException if a clip cannot be made for security reasons + * @since 1.5 + */ + public static Clip getClip() + throws LineUnavailableException + { + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + Mixer mix = getMixer(infos[i]); + Line[] lines = mix.getSourceLines(); + for (int j = 0; j < lines.length; ++j) + { + if (lines[j] instanceof Clip) + return (Clip) lines[j]; + } + } + throw new LineUnavailableException("no Clip available"); + } + + /** + * Return a new clip which can be used for playing back an audio stream. + * The clip is obtained from the indicated mixer. + * @param info the mixer to use + * @throws LineUnavailableException if a clip is not available for some + * reason + * @throws SecurityException if a clip cannot be made for security reasons + * @since 1.5 + */ + public static Clip getClip(Mixer.Info info) + throws LineUnavailableException + { + Mixer mix = getMixer(info); + Line[] lines = mix.getSourceLines(); + for (int j = 0; j < lines.length; ++j) + { + if (lines[j] instanceof Clip) + return (Clip) lines[j]; + } + throw new LineUnavailableException("no Clip available"); + } + + /** + * Return a line matching the provided description. All the providers + * on the system are searched for a matching line. + * @param info description of the line + * @return the matching line + * @throws LineUnavailableException if no provider supplies a matching line + */ + public static Line getLine(Line.Info info) throws LineUnavailableException + { + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + Mixer mix = getMixer(infos[i]); + try + { + return mix.getLine(info); + } + catch (LineUnavailableException _) + { + // Try the next provider. + } + } + throw new LineUnavailableException("no Clip available"); + } + + /** + * Return a mixer matching the provided description. All the providers + * on the system are searched for a matching mixer. + * @param info description of the mixer + * @return the matching mixer + * @throws IllegalArgumentException if no provider supplies a matching mixer + */ + public static Mixer getMixer(Mixer.Info info) + { + Iterator i = ServiceFactory.lookupProviders(MixerProvider.class); + while (i.hasNext()) + { + MixerProvider prov = (MixerProvider) i.next(); + if (prov.isMixerSupported(info)) + return prov.getMixer(info); + } + throw new IllegalArgumentException("mixer not found"); + } + + /** + * Return an array of descriptions of all the mixers provided on the system. + */ + public static Mixer.Info[] getMixerInfo() + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(MixerProvider.class); + while (i.hasNext()) + { + MixerProvider prov = (MixerProvider) i.next(); + Mixer.Info[] is = prov.getMixerInfo(); + for (int j = 0; j < is.length; ++j) + result.add(is[j]); + } + return (Mixer.Info[]) result.toArray(new Mixer.Info[result.size()]); + } + + /** + * Return a source data line matching the given audio format. + * @param fmt the audio format + * @throws LineUnavailableException if no source data line matching + * this format is available + * @since 1.5 + */ + public static SourceDataLine getSourceDataLine(AudioFormat fmt) + throws LineUnavailableException + { + DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt); + Mixer.Info[] mixers = getMixerInfo(); + for (int i = 0; i < mixers.length; ++i) + { + Mixer mix = getMixer(mixers[i]); + if (mix.isLineSupported(info)) + return (SourceDataLine) mix.getLine(info); + } + throw new LineUnavailableException("source data line not found"); + } + + /** + * Return a target data line matching the given audio format. + * @param fmt the audio format + * @throws LineUnavailableException if no target data line matching + * this format is available + * @since 1.5 + */ + public static SourceDataLine getSourceDataLine(AudioFormat fmt, + Mixer.Info mixer) + throws LineUnavailableException + { + DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt); + Mixer mix = getMixer(mixer); + if (mix.isLineSupported(info)) + return (SourceDataLine) mix.getLine(info); + throw new LineUnavailableException("source data line not found"); + } + + /** + * Return an array of descriptions of all the source lines matching + * the given line description. + * @param info description of the lines to match + */ + public static Line.Info[] getSourceLineInfo(Line.Info info) + { + HashSet result = new HashSet(); + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + Mixer mix = getMixer(infos[i]); + Line.Info[] srcs = mix.getSourceLineInfo(info); + for (int j = 0; j < srcs.length; ++j) + result.add(srcs[j]); + } + return (Line.Info[]) result.toArray(new Line.Info[result.size()]); + } + + /** + * Find and return a target data line matching the given audio format. + * @param fmt the format to match + * @throws LineUnavailableException if no matching line was found + * @since 1.5 + */ + public static TargetDataLine getTargetDataLine(AudioFormat fmt) + throws LineUnavailableException + { + DataLine.Info info = new DataLine.Info(TargetDataLine.class, fmt); + Mixer.Info[] mixers = getMixerInfo(); + for (int i = 0; i < mixers.length; ++i) + { + Mixer mix = getMixer(mixers[i]); + if (mix.isLineSupported(info)) + return (TargetDataLine) mix.getLine(info); + } + throw new LineUnavailableException("target data line not found"); + } + + /** + * Return a target data line matching the given audio format and + * mixer. + * @param fmt the audio format + * @param mixer the mixer description + * @return a target data line + * @throws LineUnavailableException if no matching target data line was + * found + * @since 1.5 + */ + public static TargetDataLine getTargetDataLine(AudioFormat fmt, + Mixer.Info mixer) + throws LineUnavailableException + { + DataLine.Info info = new DataLine.Info(TargetDataLine.class, fmt); + Mixer mix = getMixer(mixer); + if (mix.isLineSupported(info)) + return (TargetDataLine) mix.getLine(info); + throw new LineUnavailableException("target data line not found"); + } + + /** + * Given a source encoding, return an array of all target encodings to which + * data in this form can be converted. + * @param source the source encoding + */ + public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding source) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + if (! prov.isSourceEncodingSupported(source)) + continue; + AudioFormat.Encoding[] es = prov.getTargetEncodings(); + for (int j = 0; j < es.length; ++j) + result.add(es[j]); + } + return (AudioFormat.Encoding[]) result.toArray(new AudioFormat.Encoding[result.size()]); + } + + /** + * Given a source format, return an array of all the target encodings to + * which data in this format can be converted. + * @param source the source format + */ + public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat source) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + AudioFormat.Encoding[] es = prov.getTargetEncodings(source); + for (int j = 0; j < es.length; ++j) + result.add(es[j]); + } + return (AudioFormat.Encoding[]) result.toArray(new AudioFormat.Encoding[result.size()]); + } + + /** + * Given a target encoding and a source audio format, return an array of all + * matching audio formats to which data in this source format can be converted. + * @param encoding the target encoding + * @param sourceFmt the source format + */ + public static AudioFormat[] getTargetFormats(AudioFormat.Encoding encoding, + AudioFormat sourceFmt) + { + HashSet result = new HashSet(); + Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + AudioFormat[] es = prov.getTargetFormats(encoding, sourceFmt); + for (int j = 0; j < es.length; ++j) + result.add(es[j]); + } + return (AudioFormat[]) result.toArray(new AudioFormat[result.size()]); + } + + /** + * Given a line description, return an array of descriptions of all + * the matching target lines. + * @param info the line description + */ + public static Line.Info[] getTargetLineInfo(Line.Info info) + { + HashSet result = new HashSet(); + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + Mixer mix = getMixer(infos[i]); + Line.Info[] targs = mix.getTargetLineInfo(info); + for (int j = 0; j < targs.length; ++j) + result.add(targs[j]); + } + return (Line.Info[]) result.toArray(new Line.Info[result.size()]); + } + + /** + * Return true if the currently installed providers are able to + * convert data from the given source format to the given target encoding. + * @param targ the target encoding + * @param source the source format + */ + public static boolean isConversionSupported(AudioFormat.Encoding targ, + AudioFormat source) + { + Iterator i + = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + if (prov.isConversionSupported(targ, source)) + return true; + } + return false; + } + + /** + * Return true if the currently installed providers are able to convert + * the given source format to the given target format. + * @param targ the target format + * @param source the source format + */ + public static boolean isConversionSupported(AudioFormat targ, + AudioFormat source) + { + Iterator i + = ServiceFactory.lookupProviders(FormatConversionProvider.class); + while (i.hasNext()) + { + FormatConversionProvider prov = (FormatConversionProvider) i.next(); + if (prov.isConversionSupported(targ, source)) + return true; + } + return false; + } + + private static boolean isFileTypeSupported(AudioFileFormat.Type[] types, + AudioFileFormat.Type type) + { + for (int i = 0; i < types.length; ++i) + { + if (types[i].equals(type)) + return true; + } + return false; + } + + /** + * Return true if the given audio file format is supported by one of + * the providers installed on the system. + * @param type the audio file format type + */ + public static boolean isFileTypeSupported(AudioFileFormat.Type type) + { + return isFileTypeSupported(getAudioFileTypes(), type); + } + + /** + * Return true if the given audio file format is supported for the + * given audio input stream by one of the providers installed on the + * system. + * @param type the audio file format type + * @param ais the audio input stream + */ + public static boolean isFileTypeSupported(AudioFileFormat.Type type, + AudioInputStream ais) + { + return isFileTypeSupported(getAudioFileTypes(ais), type); + } + + /** + * Return true if some provider on the system supplies a line + * matching the argument. + * @param info the line to match + */ + public static boolean isLineSupported(Line.Info info) + { + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + if (getMixer(infos[i]).isLineSupported(info)) + return true; + } + return false; + } + + /** + * Write an audio input stream to the given file, using the specified + * audio file format. All the providers installed on the system will + * be searched to find one that supports this operation. + * @param ais the audio input stream to write + * @param type the desired audio file format type + * @param out the file to write to + * @return the number of bytes written + * @throws IOException if an I/O error occurs while writing + * @throws IllegalArgumentException if the file type is not supported + */ + public static int write(AudioInputStream ais, AudioFileFormat.Type type, + File out) + throws IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class); + while (i.hasNext()) + { + AudioFileWriter w = (AudioFileWriter) i.next(); + if (w.isFileTypeSupported(type, ais)) + return w.write(ais, type, out); + } + throw new IllegalArgumentException("file type not supported by system"); + } + + /** + * Write an audio input stream to the given output stream, using the + * specified audio file format. All the providers installed on the + * system will be searched to find one that supports this operation. + * @param ais the audio input stream to write + * @param type the desired audio file format type + * @param os the output stream to write to + * @return the number of bytes written + * @throws IOException if an I/O error occurs while writing + * @throws IllegalArgumentException if the file type is not supported + */ + public static int write(AudioInputStream ais, AudioFileFormat.Type type, + OutputStream os) + throws IOException + { + Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class); + while (i.hasNext()) + { + AudioFileWriter w = (AudioFileWriter) i.next(); + if (w.isFileTypeSupported(type, ais)) + return w.write(ais, type, os); + } + throw new IllegalArgumentException("file type not supported by system"); + } +} diff --git a/libjava/classpath/javax/sound/sampled/BooleanControl.java b/libjava/classpath/javax/sound/sampled/BooleanControl.java new file mode 100644 index 00000000000..aae1e23a298 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/BooleanControl.java @@ -0,0 +1,145 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +/** + * A BooleanControl is a Control which has two states. + * @since 1.3 + */ +public class BooleanControl extends Control +{ + /** + * A Type specialized to represent a boolean control. + * @since 1.3 + */ + public static class Type extends Control.Type + { + // FIXME: correct constructions? + + /** + * A control for applying reverb. + */ + public final static Type APPLY_REVERB = new Type("Apply reverb"); + + /** + * A control for muting. + */ + public final static Type MUTE = new Type("Mute"); + + /** + * Create a new Type given its name. + * @param name the name of the type + */ + protected Type(String name) + { + super(name); + } + } + + private Type type; + private boolean value; + private String trueLabel; + private String falseLabel; + + /** + * Create a new boolean control, with the indicated Type and initial + * value. The description strings will default to "true" and "false". + * @param type the type + * @param init the initial value + */ + protected BooleanControl(Type type, boolean init) + { + super(type); + this.value = init; + this.trueLabel = "true"; + this.falseLabel = "false"; + } + + /** + * Create a new boolean control, with the indicated Type, initial + * value, and labels. + * @param type the type + * @param init the initial value + * @param trueLabel the label for the true state + * @param falseLabel the label for the false state + */ + protected BooleanControl(Type type, boolean init, String trueLabel, + String falseLabel) + { + super(type); + this.value = init; + this.trueLabel = trueLabel; + this.falseLabel = falseLabel; + } + + /** + * Return the label corresponding to the indicated state. + * @param state the state + * @return the true label or the false label, as appropriate + */ + public String getStateLabel(boolean state) + { + return state ? trueLabel : falseLabel; + } + + /** + * Return the current value of thhe control. + */ + public boolean getValue() + { + return value; + } + + /** + * Set the value of the control as indicated. + * @param value the new value + */ + public void setValue(boolean value) + { + this.value = value; + } + + /** + * Return a string describing this control. + */ + public String toString() + { + return super.toString() + ": " + getStateLabel(value); + } +} diff --git a/libjava/classpath/javax/sound/sampled/Clip.java b/libjava/classpath/javax/sound/sampled/Clip.java new file mode 100644 index 00000000000..1fa769470a2 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/Clip.java @@ -0,0 +1,115 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +import java.io.IOException; + +/** + * A Clip represents some pre-loaded audio data. + * @since 1.3 + */ +public interface Clip extends DataLine +{ + /** + * This can be passed to {@link #loop(int)} to indicate that looping + * should be done continuously. + */ + int LOOP_CONTINUOUSLY = -1; + + /** + * Return the frame length of this clip. + */ + int getFrameLength(); + + /** + * Return the length of the clip in microseconds. + */ + long getMicrosecondLength(); + + /** + * Start looping the clip. Looping will occur count times, or, if count + * is LOOP_CONTINUOUSLY, will be done continuously. A count of 0 indicates + * that any current looping should stop. + * @param count the number of times to loop + */ + void loop(int count); + + /** + * Open a clip, given an audio format and some data. + * @param fmt the format of the data + * @param data a byte array containing the audio data + * @param offset the offset of the first byte of data in the array + * @param len the length of the audio data in the array, in bytes + * @throws LineUnavailableException if the line cannot be opened + * @throws SecurityException if the line cannot be opened for security + * reasons + */ + void open(AudioFormat fmt, byte[] data, int offset, int len) + throws LineUnavailableException; + + /** + * Open a clip, given an audio input stream. + * @param ais the input stream + * @throws LineUnavailableException if the line cannot be opened + * @throws SecurityException if the line cannot be opened for security + * reasons + * @throws IOException if there is an I/O error while reading the stream + */ + void open(AudioInputStream ais) + throws LineUnavailableException, IOException; + + /** + * Set the position to the indicated frame. + * @param where new frame position + */ + void setFramePosition(int where); + + /** + * Set the loop begin and end points. These are used by loop(int). + * @param begin the starting point + * @param end the ending point + */ + void setLoopPoints(int begin, int end); + + /** + * Set the position to the indicated microsecond. + * @param ms the new position in microseconds + */ + void setMicrosecondPosition(long ms); +} diff --git a/libjava/classpath/javax/sound/sampled/CompoundControl.java b/libjava/classpath/javax/sound/sampled/CompoundControl.java new file mode 100644 index 00000000000..8664abca808 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/CompoundControl.java @@ -0,0 +1,102 @@ +/* Control consisting of several other controls + Copyright (C) 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 javax.sound.sampled; + +/** + * A compound control provides control over several other controls. + * @since 1.3 + */ +public class CompoundControl extends Control +{ + /** + * This describes a single compound control. + * @since 1.3 + */ + public static class Type extends Control.Type + { + /** + * Create a new Type given its name. + * @param name the name of the type + */ + protected Type(String name) + { + super(name); + } + } + + private Control[] memberControls; + + /** + * Create a new compound control given its type and members. + * @param type the type of the compound control + * @param members the members of the compound control + */ + protected CompoundControl(Type type, Control[] members) + { + super(type); + // FIXME: clone? + this.memberControls = members; + } + + /** + * Return the members of this compound control. + */ + public Control[] getMemberControls() + { + // FIXME: clone? + return memberControls; + } + + /** + * Return a string description of this compound control. + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append(super.toString()); + result.append(": "); + for (int i = 0; i < memberControls.length; ++i) + { + if (i > 0) + result.append(", "); + result.append(memberControls[i].toString()); + } + return result.toString(); + } +} diff --git a/libjava/classpath/javax/sound/sampled/Control.java b/libjava/classpath/javax/sound/sampled/Control.java new file mode 100644 index 00000000000..810c2edd54d --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/Control.java @@ -0,0 +1,111 @@ +/* Control over an attribute of a line + Copyright (C) 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 javax.sound.sampled; + +/** + * A control provides the ability to affect some attribute of a line, + * for instance its volume. + * @since 1.3 + */ +public class Control +{ + /** + * This describes a single control. + * @since 1.3 + */ + public static class Type + { + private String name; + + /** + * Create a new Type given its name. + * @param name the name of the type + */ + protected Type(String name) + { + this.name = name; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the name of this Type. + */ + public String toString() + { + return name; + } + } + + private Type type; + + /** + * Create a new Control given its Type. + * @param type the type + */ + protected Control(Type type) + { + this.type = type; + } + + /** + * Return the Type of this Control. + */ + public Type getType() + { + return type; + } + + /** + * Return a String descrsibing this control. In particular the + * value will include the name of the associated Type. + */ + public String toString() + { + return type.toString(); + } +} diff --git a/libjava/classpath/javax/sound/sampled/DataLine.java b/libjava/classpath/javax/sound/sampled/DataLine.java new file mode 100644 index 00000000000..f755958fe87 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/DataLine.java @@ -0,0 +1,265 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +/** + * The DataLine interface adds data-related functionality to the Line + * interface. For example, it adds methods to start and stop the data + * on the line. + * @since 1.3 + */ +public interface DataLine extends Line +{ + /** + * This class extends Line.Info with information specific to DataLine. + * In particular it adds information about buffer sizes, and about supported + * audio formats. + * @since 1.3 + */ + class Info extends Line.Info + { + private int minBufferSize; + private int maxBufferSize; + private AudioFormat[] formats; + + /** + * Create a new Info given the line's class and a supported + * audio format. The buffer sizes default to AudioSystem.NOT_SPECIFIED. + * @param klass the class of the line + * @param fmt the supported format + */ + public Info(Class klass, AudioFormat fmt) + { + super(klass); + this.minBufferSize = AudioSystem.NOT_SPECIFIED; + this.maxBufferSize = AudioSystem.NOT_SPECIFIED; + this.formats = new AudioFormat[] { fmt }; + } + + /** + * Create a new Info given the line's class, the supported audio formats, + * the minimum buffer size, and the maximum buffer size. + * @param klass the class of the linee + * @param fmts the supported audio formats + * @param minSize the minimum buffer size + * @param maxSize the maximum buffer size + */ + public Info(Class klass, AudioFormat[] fmts, int minSize, int maxSize) + { + super(klass); + this.minBufferSize = minSize; + this.maxBufferSize = maxSize; + this.formats = fmts; + } + + /** + * Create a new Info given the line's class, a supported + * audio format, and a buffer size. Both the minimum and maximum + * sizes are set from this size. + * @param klass the class of the line + * @param fmt the supported format + * @param size the buffer size + */ + public Info(Class klass, AudioFormat fmt, int size) + { + super(klass); + this.minBufferSize = size; + this.maxBufferSize = size; + this.formats = new AudioFormat[] { fmt }; + } + + /** + * Return the supported audio formats. + */ + public AudioFormat[] getFormats() + { + // FIXME: clone? + return formats; + } + + /** + * Return the maximum buffer size. + */ + public int getMaxBufferSize() + { + return maxBufferSize; + } + + /** + * Return the minimum buffer size. + */ + public int getMinBufferSize() + { + return minBufferSize; + } + + /** + * Return true if the indicated audio format is supported by this + * Info, false otherwise. + * @param fmt the audio format + * @return true if the format is supported + */ + public boolean isFormatSupported(AudioFormat fmt) + { + for (int i = 0; i < formats.length; ++i) + { + if (fmt.matches(formats[i])) + return true; + } + return false; + } + + /** + * Return true if this Info matches another Info object. + */ + public boolean matches(Line.Info o) + { + if (! super.matches(o) || ! (o instanceof Info)) + return false; + Info other = (Info) o; + if (minBufferSize < other.minBufferSize + || maxBufferSize > other.maxBufferSize) + return false; + for (int i = 0; i < formats.length; ++i) + { + boolean ok = false; + for (int j = 0; j < other.formats.length; ++j) + { + if (formats[i].matches(other.formats[j])) + { + ok = true; + break; + } + } + if (! ok) + return false; + } + return true; + } + + /** + * Return a description of this Info object. + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append("formats: ["); + for (int i = 0; i < formats.length; ++i) + { + if (i > 0) + result.append(", "); + result.append(formats[i].toString()); + } + result.append("]; minBufferSize: "); + result.append(minBufferSize); + result.append("; maxBufferSize: "); + result.append(maxBufferSize); + return result.toString(); + } + } + + /** + * Return the number of bytes currently available on this DataLine. + */ + int available(); + + /** + * This method blocks until whatever data is buffered in the + * DataLine's internal buffer has been drained. + */ + void drain(); + + /** + * This flushes the DataLine by discarding any buffered data. + */ + void flush(); + + /** + * Returns the size of the DataLine's internal buffer, in bytes. + */ + int getBufferSize(); + + /** + * Return the current format of the data associated with this DataLine. + */ + AudioFormat getFormat(); + + /** + * Return the current frame position. + */ + int getFramePosition(); + + /** + * Return the volume level for this DataLine. + */ + float getLevel(); + + /** + * Return the current frame position. + * @since 1.5 + */ + long getLongFramePosition(); + + /** + * Return the number of microseconds this DataLine has been playing. + */ + long getMicrosecondPosition(); + + /** + * Return true if this line is active, meaning that it is actively + * performing audio I/O. + */ + boolean isActive(); + + /** + * Return true if this line is running, meaning that it has been + * started. When the line is stopped, this method will return false. + */ + boolean isRunning(); + + /** + * Start processing data. This will emit a START event. + */ + void start(); + + /** + * Stop processing data. This will emit a STOP event. + */ + void stop(); +} diff --git a/libjava/classpath/javax/sound/sampled/EnumControl.java b/libjava/classpath/javax/sound/sampled/EnumControl.java new file mode 100644 index 00000000000..798f3a91c96 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/EnumControl.java @@ -0,0 +1,126 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +/** + * An EnumControl is a Control which can take one of a specified set of + * values. + * @since 1.3 + */ +public class EnumControl extends Control +{ + /** + * This Type describes an EnumControl. + * @since 1.3 + */ + public static class Type extends Control.Type + { + /** This describes an enum control used for reverb. */ + public static final Type REVERB = new Type("Reverb"); + + /** + * Create a new Type given its name. + * @param name the name of the type + */ + protected Type(String name) + { + super(name); + } + } + + private Object[] values; + private Object value; + + /** + * Create a new enumerated control given its Type, the range of valid + * values, and its initial value. + * @param type the type + * @param values the valid values + * @param val the initial value + */ + protected EnumControl(Type type, Object[] values, Object val) + { + super(type); + // FIXME: error checking: values.length>0, val in values... ? + // FIXME: clone here? + this.values = values; + this.value = val; + } + + /** + * Return the current value of this control. + */ + public Object getValue() + { + return value; + } + + /** + * Return the valid values for this control. + */ + public Object[] getValues() + { + // FIXME: clone here? + return values; + } + + /** + * Set the value of this control. If the indicated value is not among + * the valid values, this method will throw an IllegalArgumentException. + * @param value the new value + * @throws IllegalArgumentException if the new value is invalid + */ + public void setValue(Object value) + { + for (int i = 0; i < values.length; ++i) + { + if (! values[i].equals(value)) + throw new IllegalArgumentException("value not supported"); + } + this.value = value; + } + + /** + * Return a string describing this control. + */ + public String toString() + { + return super.toString() + ": " + value; + } +} diff --git a/libjava/classpath/javax/sound/sampled/FloatControl.java b/libjava/classpath/javax/sound/sampled/FloatControl.java new file mode 100644 index 00000000000..409c90de2cd --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/FloatControl.java @@ -0,0 +1,267 @@ +/* Floating point control + Copyright (C) 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 javax.sound.sampled; + +/** @since 1.3 */ +public class FloatControl extends Control +{ + /** + * An instance of this class describes a particular floating point control. + * @since 1.3 + */ + public static class Type extends Control.Type + { + /** Auxiliary return gain. */ + public static final Type AUX_RETURN = new Type("AUX return"); + + /** Auxiliary send gain. */ + public static final Type AUX_SEND = new Type("AUX send"); + + /** Balance. */ + public static final Type BALANCE = new Type("Balance"); + + /** Master gain control. */ + public static final Type MASTER_GAIN = new Type("Master gain"); + + /** Control for panning. */ + public static final Type PAN = new Type("Pan"); + + /** Post-reverb gain. */ + public static final Type REVERB_RETURN = new Type("Reverb return"); + + /** Pre-reverb gain. */ + public static final Type REVERB_SEND = new Type("Reverb send"); + + /** Control the sample rate. */ + public static final Type SAMPLE_RATE = new Type("Sample rate"); + + /** Volume control. */ + public static final Type VOLUME = new Type("Volume"); + + /** + * Create a new type given its name. + * @param name the name of the type + */ + protected Type(String name) + { + super(name); + } + } + + private float minimum; + private float maximum; + private float precision; + private int updatePeriod; + private float value; + private String units; + private String minLabel; + private String maxLabel; + private String midLabel; + + /** + * Create a new FloatControl given its type and various parameters. + * The minimum, maximum, and midpoint labels will all be the empty string. + * + * @param type the type + * @param min the minimum valuee + * @param max the maximum value + * @param prec the precision + * @param update the update period + * @param init the initial value + * @param units the description of the units + */ + protected FloatControl(Type type, float min, float max, float prec, + int update, float init, String units) + { + super(type); + this.minimum = min; + this.maximum = max; + this.precision = prec; + this.updatePeriod = update; + this.value = init; + this.units = units; + this.minLabel = ""; + this.maxLabel = ""; + this.midLabel = ""; + } + + /** + * Create a new FloatControl given its type and various parameters. + * + * @param type the type + * @param min the minimum valuee + * @param max the maximum value + * @param prec the precision + * @param update the update period + * @param init the initial value + * @param units the description of the units + * @param minLabel the label for the minimum value + * @param midLabel the label for the midpoint + * @param maxLabel the label for the maximum value + */ + protected FloatControl(Type type, float min, float max, float prec, + int update, float init, String units, + String minLabel, String midLabel, String maxLabel) + { + super(type); + this.minimum = min; + this.maximum = max; + this.precision = prec; + this.updatePeriod = update; + this.value = init; + this.units = units; + this.minLabel = minLabel; + this.maxLabel = maxLabel; + this.midLabel = midLabel; + } + + /** + * Return the maximum value of this control. + */ + public float getMaximum() + { + return maximum; + } + + /** + * Return the label for the minimum value of this control. + */ + public String getMaxLabel() + { + return maxLabel; + } + + /** + * Return the label for the midpoint of this control. + */ + public String getMidLabel() + { + return midLabel; + } + + /** + * Return the minimum value of this control. + */ + public float getMinimum() + { + return minimum; + } + + /** + * Return the label for the minimum value of this control. + */ + public String getMinLabel() + { + return minLabel; + } + + /** + * Return the precision of this control. + */ + public float getPrecision() + { + return precision; + } + + /** + * Return the name of the units for this control. + */ + public String getUnits() + { + return units; + } + + /** + * Return the update period of this control. + */ + public int getUpdatePeriod() + { + return updatePeriod; + } + + /** + * Return the current value of this control. + */ + public float getValue() + { + return value; + } + + /** + * Set the new value of this control. + * @param value the new value + * @throws IllegalArgumentException if the new value is greater than the + * maximum or less than the minimum. + */ + public void setValue(float value) + { + if (value < minimum || value > maximum) + throw new IllegalArgumentException("value out of range"); + this.value = value; + } + + /** + * This tells the control to start at the starting value + * and to shift its value incrementally to the final value + * over the given time interval, specified in microseconds. + * The default implementation does not do this, but instead + * simply sets the value to the final value immediately. + * + * @param from the starting value + * @param to the final value + * @param ms the number of microseconds + */ + public void shift(float from, float to, int ms) + { + if (from < minimum || from > maximum + || to < minimum || to > maximum + || ms < 0) + throw new IllegalArgumentException("argument out of range"); + // The default just sets the value to TO. + this.value = to; + } + + /** + * Return a string describing this control. + */ + public String toString() + { + return super.toString() + ": " + value; + } +} diff --git a/libjava/classpath/javax/sound/sampled/Line.java b/libjava/classpath/javax/sound/sampled/Line.java new file mode 100644 index 00000000000..69bb9084f10 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/Line.java @@ -0,0 +1,150 @@ +/* An input or output line + Copyright (C) 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 javax.sound.sampled; + +/** + * A Line represents a single input or output audio line. + * @since 1.3 + */ +public interface Line +{ + /** + * An object of this type holds information about a Line. + * @since 1.3 + */ + class Info + { + private Class klass; + + /** + * Create a new Info object. The argument is the class of the line, + * for instance TargetDataLine.class. + * @param klass the class of the line + */ + public Info(Class klass) + { + this.klass = klass; + } + + /** + * Return the line's class. + */ + public Class getLineClass() + { + return klass; + } + + /** + * Return true if this Info object matches the given object. + * @param other the object to match + * @return true if they match, false otherwise + */ + public boolean matches(Info other) + { + return klass.equals(other.klass); + } + + /** + * Return a description of this Info object. + */ + public String toString() + { + return klass.toString(); + } + } + + /** + * Add a listener which will be notified whenever this Line changes state. + * @param listener the listener to notify + */ + void addLineListener(LineListener listener); + + /** + * Close this line. + */ + void close(); + + /** + * Return the control associated with this Line that matches the + * argument. + * @param what the type of the control to match + * @return the associated control + * @throws IllegalArgumentException if a control of this type is not + * available for this line + */ + Control getControl(Control.Type what); + + /** + * Return an array of controls associated with this Line. Note that + * this method will not return null -- if there are no controls, it + * will return a zero-length array. + */ + Control[] getControls(); + + /** + * Return the Info object associated with this Line. + */ + Info getLineInfo(); + + /** + * Return true if a Control matching the argument is available for this + * Line, false otherwise. + * @param what the type of the control to match + */ + boolean isControlSupported(Control.Type what); + + /** + * Return true if this line is open, false otherwise. + */ + boolean isOpen(); + + /** + * Open this line. + * @throws LineUnavailableException if the line is unavailable for some + * reason + */ + void open() throws LineUnavailableException; + + /** + * Remove the listener from this Line; after this call the listener will + * no longer be notified when this Line changes state. + * @param listener the listener to remove + */ + void removeLineListener(LineListener listener); +} diff --git a/libjava/classpath/javax/sound/sampled/LineEvent.java b/libjava/classpath/javax/sound/sampled/LineEvent.java new file mode 100644 index 00000000000..7bba2cd1d96 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/LineEvent.java @@ -0,0 +1,150 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +import java.util.EventObject; + +// FIXME: attempts to serialize this should fail + +/** + * This class holds information about a state change of a Line. + * @since 1.3 + */ +public class LineEvent extends EventObject +{ + /** + * This class represents the kinds of state changes that can occur + * to a Line. The standard states are availabe as static instances. + * @since 1.3 + */ + public static class Type + { + /** An event of this type is posted when a Line closes. */ + public static final Type CLOSE = new Type("close"); + + /** An event of this type is posted when a Line opens. */ + public static final Type OPEN = new Type("open"); + + /** An event of this type is posted when a Line starts. */ + public static final Type START = new Type("start"); + + /** An event of this type is posted when a Line stops. */ + public static final Type STOP = new Type("stop"); + + private String name; + + /** + * Create a new type with the indicated name. + * @param name the name + */ + protected Type(String name) + { + this.name = name; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the name of this Type. + */ + public String toString() + { + return name; + } + } + + private Type type; + private long framePosition; + private Line line; + + /** + * Create a new LineEvent with the indicated line, type, and frame position. + * @param line the line + * @param type the type of the event + * @param pos the frame position + */ + public LineEvent(Line line, Type type, long pos) + { + super(line); + this.line = line; + this.type = type; + this.framePosition = pos; + } + + /** + * Return the frame position associated with this event. + */ + public long getFramePosition() + { + return framePosition; + } + + /** + * Return the Line associated with this event. + */ + public Line getLine() + { + return line; + } + + /** + * Return the Type associated with this event. + */ + public Type getType() + { + return type; + } + + /** + * Return a description of this event. + */ + public String toString() + { + return ("type=" + type + "; framePosition=" + framePosition + + "line=" + line); + } +} diff --git a/libjava/classpath/javax/sound/sampled/LineListener.java b/libjava/classpath/javax/sound/sampled/LineListener.java new file mode 100644 index 00000000000..1b87c027902 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/LineListener.java @@ -0,0 +1,55 @@ +/* Listener for Lines + Copyright (C) 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 javax.sound.sampled; + +import java.util.EventListener; + +/** + * This interface is used by classes which wish to be notified + * when the state of a Line changes. + * @since 1.3 + */ +public interface LineListener extends EventListener +{ + /** + * This is called when the line's status changes. + * @param ev the event describing the change + */ + void update(LineEvent ev); +} diff --git a/libjava/classpath/javax/sound/sampled/LineUnavailableException.java b/libjava/classpath/javax/sound/sampled/LineUnavailableException.java new file mode 100644 index 00000000000..b203d7be213 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/LineUnavailableException.java @@ -0,0 +1,61 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +/** @since 1.3 */ +public class LineUnavailableException extends Exception +{ + private static final long serialVersionUID = -2046718279487432130L; + + /** + * Create a new LineUnavailableException. + */ + public LineUnavailableException() + { + } + + /** + * Create a new LineUnavailableException with the given message. + * @param msg the message + */ + public LineUnavailableException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/javax/sound/sampled/Mixer.java b/libjava/classpath/javax/sound/sampled/Mixer.java new file mode 100644 index 00000000000..b9afba3fe87 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/Mixer.java @@ -0,0 +1,206 @@ +/* Mixers + Copyright (C) 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 javax.sound.sampled; + +/** + * A Mixer is a Line which itself holds multiple lines. + * @since 1.3 + */ +public interface Mixer extends Line +{ + /** + * An Info object describes a mixer. + * @since 1.3 + */ + class Info + { + private String name; + private String description; + private String vendor; + private String version; + + /** + * Create a new mixer description. + * @param name the name of the mixer + * @param vendor the vendor + * @param desc a descriptive string + * @param vers the mixer's version + */ + public Info(String name, String vendor, String desc, String vers) + { + this.name = name; + this.description = desc; + this.vendor = vendor; + this.version = vers; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the name of the mixer. + */ + public String getName() + { + return name; + } + + /** + * Return the mixer's description. + */ + public String getDescription() + { + return description; + } + + /** + * Return the mixer's vendor. + */ + public String getVendor() + { + return vendor; + } + + /** + * Return the mixer's version. + */ + public String getVersion() + { + return version; + } + + public String toString() + { + return ("name=" + name + "; description=" + description + + "; vendor=" + vendor + "; version=" + version); + } + } + + /** + * Return a Line associated with this Mixer, given its description. + * @param info the description of the line to find + * @return the corresponding Line + * @throws LineUnavailableException if no Line matching the description + * exists in this Mixer + */ + Line getLine(Line.Info info) throws LineUnavailableException; + + /** + * Return the number of lines matching this description. + * @param info the description of the lines to find. + */ + int getMaxLines(Line.Info info); + + /** + * Return an Info object describing this Mixer. + */ + Info getMixerInfo(); + + /** + * Return an array of Info objects describing all the source lines + * available in this Mixer. + */ + Line.Info[] getSourceLineInfo(); + + /** + * Return an array of Info objects describing all the source lines + * available in this Mixer, which match the provided decsription. + * @param info the description of the source lines to find + */ + Line.Info[] getSourceLineInfo(Line.Info info); + + /** + * Return an array of all the source lines available in this Mixer. + */ + Line[] getSourceLines(); + + /** + * Return an array of Info objects describing all the target lines + * available in this Mixer. + */ + Line.Info[] getTargetLineInfo(); + + /** + * Return an array of Info objects describing all the target lines + * available in this Mixer, which match the provided decsription. + * @param info the description of the target lines to find + */ + Line.Info[] getTargetLineInfo(Line.Info info); + + /** + * Return an array of all the target lines available in this Mixer. + */ + Line[] getTargetLines(); + + /** + * Return true if a Line matching the given description is supported + * by this Mixer, false otherwise. + * @param info the description of the line to find + */ + boolean isLineSupported(Line.Info info); + + /** + * Return true if this Mixer supports synchronization of the given set + * of lines. + * @param lines the lines to check + * @param sync true if the synchronization must be accurate at all times + */ + boolean isSynchronizationSupported(Line[] lines, boolean sync); + + /** + * Start synchronization on the given set of lines. + * @param lines the lines to synchronize, or null for all the lines + * @param sync true if the synchronization must be accurate at all times + * @throws IllegalArgumentException if the lines cannot be synchronized + */ + void synchronize(Line[] lines, boolean sync); + + /** + * Stop synchronization for the given set of lines. + * @param lines the lines to unsynchronize, or null for all the lines + */ + void unsynchronize(Line[] lines); +} diff --git a/libjava/classpath/javax/sound/sampled/Port.java b/libjava/classpath/javax/sound/sampled/Port.java new file mode 100644 index 00000000000..7b3daafd3aa --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/Port.java @@ -0,0 +1,135 @@ +/* + Copyright (C) 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 javax.sound.sampled; + +/** + * A Port is a Line which represents an audio device, for instance + * a microphone. + * + * @since 1.3 + */ +public interface Port extends Line +{ + /** + * This describes a single port. + * @since 1.3 + */ + class Info extends Line.Info + { + // FIXME names? + + /** A CD player. */ + public static final Info COMPACT_DISC = new Info(Port.class, + "Compact Disc", + true); + + /** Headphones. */ + public static final Info HEADPHONE = new Info(Port.class, "Headphone", + false); + + /** Generic input line. */ + public static final Info LINE_IN = new Info(Port.class, "Line in", + true); + + /** Generic output line. */ + public static final Info LINE_OUT = new Info(Port.class, "Line out", + false); + + /** A microphone. */ + public static final Info MICROPHONE = new Info(Port.class, "Microphone", + true); + + /** A speaker. */ + public static final Info SPEAKER = new Info(Port.class, "Speaker", + false); + + private String name; + private boolean isSource; + + /** + * Create a new Info object, given the line's class, the name, + * and an argument indicating whether this is an input or an output. + * @param klass the class of the line + * @param name the name of the line + * @param isSource true if this is an input source + */ + public Info(Class klass, String name, boolean isSource) + { + super(klass); + this.name = name; + this.isSource = isSource; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the name of this object. + */ + public String getName() + { + return name; + } + + /** + * Return true if this describes an input line. + */ + public boolean isSource() + { + return isSource; + } + + public boolean matches(Line.Info other) + { + return super.matches(other) && equals(other); + } + + public String toString() + { + return super.toString() + "; name=" + name + "; isSource=" + isSource; + } + } +} diff --git a/libjava/classpath/javax/sound/sampled/ReverbType.java b/libjava/classpath/javax/sound/sampled/ReverbType.java new file mode 100644 index 00000000000..a089593777d --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/ReverbType.java @@ -0,0 +1,144 @@ +/* Reverb attributes + Copyright (C) 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 javax.sound.sampled; + +/** + * This represents a reverb effect which can be applied to an audio signal. + * @since 1.3 + */ +public class ReverbType +{ + private String name; + private int earlyReflectionDelay; + private float earlyReflectionIntensity; + private int lateReflectionDelay; + private float lateReflectionIntensity; + private int decayTime; + + /** + * Create a new ReverbType given its attributes. + * @param name the name of this type + * @param earlyDelay the early delay time in microseconds + * @param earlyInten the early intensity in decibels + * @param lateDelay the late delay time in microseconds + * @param lateInten the late intensity in decibels + * @param decay the decay time in microseconds + */ + protected ReverbType(String name, int earlyDelay, float earlyInten, + int lateDelay, float lateInten, int decay) + { + this.name = name; + this.earlyReflectionDelay = earlyDelay; + this.earlyReflectionIntensity = earlyInten; + this.lateReflectionDelay = lateDelay; + this.lateReflectionIntensity = lateInten; + this.decayTime = decay; + } + + public final boolean equals(Object o) + { + return super.equals(o); + } + + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Return the decay time. + */ + public int getDecayTime() + { + return decayTime; + } + + /** + * Return the early reflection delay. + */ + public int getEarlyReflectionDelay() + { + return earlyReflectionDelay; + } + + /** + * Return the early reflection intensity. + */ + public float getEarlyReflectionIntensity() + { + return earlyReflectionIntensity; + } + + /** + * Return the late reflection delay. + */ + public int getLateReflectionDelay() + { + return lateReflectionDelay; + } + + /** + * Return the late reflection intensity. + */ + public float getLateReflectionIntensity() + { + return lateReflectionIntensity; + } + + /** + * Return the name of this ReverbType. + * @since 1.5 + */ + public String getName() + { + return name; + } + + /** + * Return a description of this ReverbType. + */ + public String toString() + { + return ("name=" + name + "; earlyReflectionDelay=" + earlyReflectionDelay + + "; earlyReflectionIntensity=" + earlyReflectionIntensity + + "; lateReflectionDelay=" + lateReflectionDelay + + "; lateReflectionIntensity=" + lateReflectionIntensity + + "; decayTime=" + decayTime); + } +} diff --git a/libjava/classpath/javax/sound/sampled/SourceDataLine.java b/libjava/classpath/javax/sound/sampled/SourceDataLine.java new file mode 100644 index 00000000000..6e73d6d9c4e --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/SourceDataLine.java @@ -0,0 +1,77 @@ +/* Output data line. + Copyright (C) 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 javax.sound.sampled; + +/** + * This is a DataLine to which data may be written. + * @since 1.3 + */ +public interface SourceDataLine extends DataLine +{ + /** + * Open the line, given the desired audio format. + * @param fmt the format to use + * @throws LineUnavailableException if the line is not available for + * some reason + * @throws SecurityException if this is prevented by the security manager + */ + void open(AudioFormat fmt) + throws LineUnavailableException; + + /** + * Open the line, given the desired audio format and the buffer size. + * @param fmt the format to use + * @param size the buffer size + * @throws LineUnavailableException if the line is not available for + * some reason + * @throws SecurityException if this is prevented by the security manager + */ + void open(AudioFormat fmt, int size) + throws LineUnavailableException; + + /** + * Write audio data to this line. The data must be an integral number + * of frames, as determined by the audio format. + * @param buf a byte array of audio data + * @param offset index of the first byte in the array to use + * @param length the number of bytes to write + * @return the number of bytes written + */ + int write(byte[] buf, int offset, int length); +} diff --git a/libjava/classpath/javax/sound/sampled/TargetDataLine.java b/libjava/classpath/javax/sound/sampled/TargetDataLine.java new file mode 100644 index 00000000000..61ad344efc2 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/TargetDataLine.java @@ -0,0 +1,79 @@ +/* Input data line. + Copyright (C) 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 javax.sound.sampled; + +/** + * This is a DataLine from which data may be read. + * @since 1.3 + */ +public interface TargetDataLine extends DataLine +{ + /** + * Open the line using the indicated audio format. + * @param fmt the format to use + * @throws LineUnavailableException if the line is not available for + * some reason + * @throws SecurityException if this operation is prevented by the + * security manager + */ + void open(AudioFormat fmt) + throws LineUnavailableException; + + /** + * Open the line using the indicated audio format and buffer size. + * @param fmt the format to use + * @throws LineUnavailableException if the line is not available for + * some reason + * @throws SecurityException if this operation is prevented by the + * security manager + */ + void open(AudioFormat fmt, int size) + throws LineUnavailableException; + + /** + * Read data from the line into the given buffer. The requested data + * should be an integral number of framaes, as determined by the audio + * format. + * @param buf the buffer into which the data is put + * @param offset the initial offset at which to write + * @param length the maximum number of bytes to read + * @return the actual number of bytes read + */ + int read(byte[] buf, int offset, int length); +} diff --git a/libjava/classpath/javax/imageio/metadata/IIONodeList.java b/libjava/classpath/javax/sound/sampled/UnsupportedAudioFileException.java index 395d261b6c6..bced9c18f19 100644 --- a/libjava/classpath/javax/imageio/metadata/IIONodeList.java +++ b/libjava/classpath/javax/sound/sampled/UnsupportedAudioFileException.java @@ -1,5 +1,5 @@ -/* IIOAttr.java -- - Copyright (C) 2004 Free Software Foundation, Inc. +/* + Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -35,38 +35,31 @@ 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 javax.imageio.metadata; -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +package javax.sound.sampled; /** - * Simple NodeList implementation for IIOMetadataNode. - * - * @author jlquinn - * + * An exception of this type is thrown when an operation is attempted + * on a file whose format is unrecognized. + * @since 1.3 */ -class IIONodeList implements NodeList +public class UnsupportedAudioFileException extends Exception { - List children = new ArrayList(); - - /* (non-Javadoc) - * @see org.w3c.dom.NodeList#item(int) + private static final long serialVersionUID = -139127412623160368L; + + /** + * Create a new UnsupportedAudioFileException. */ - public Node item(int index) + public UnsupportedAudioFileException() { - return (index < children.size()) ? (Node)children.get(index) : null; } - /* (non-Javadoc) - * @see org.w3c.dom.NodeList#getLength() + /** + * Create a new UnsupportedAudioFileException with the indicated message. + * @param msg the message */ - public int getLength() + public UnsupportedAudioFileException(String msg) { - return children.size(); + super(msg); } - } diff --git a/libjava/classpath/javax/sound/sampled/spi/AudioFileReader.java b/libjava/classpath/javax/sound/sampled/spi/AudioFileReader.java new file mode 100644 index 00000000000..6df4cc22d43 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/spi/AudioFileReader.java @@ -0,0 +1,146 @@ +/* Audio file reader API + Copyright (C) 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 javax.sound.sampled.spi; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.UnsupportedAudioFileException; + +/** + * This abstract class defines the interface to audio file readers. + * A concrete provider subclass will implement the methods declared + * here. These methods can be used to determine the format of + * files, and to retrieve an AudioInputStream for a file. + * @since 1.3 + */ +public abstract class AudioFileReader +{ + /** + * The default constructor. Note that this class is abstract and + * thus not directly instantiable. + */ + public AudioFileReader() + { + } + + /** + * Return the format of the given file as deduced by this provider. + * If the format of the file is not recognized, throws an exception. + * This will also throw an exception if there is an I/O error when + * reading the file. + * @param file the file to examine + * @return the audio file format + * @throws UnsupportedAudioFileException if the file's format is not + * recognized + * @throws IOException if there is an I/O error while reading the file + */ + public abstract AudioFileFormat getAudioFileFormat(File file) + throws UnsupportedAudioFileException, IOException; + + /** + * Return the format of the given input stream as deduced by this provider. + * If the format of the stream is not recognized, throws an exception. + * This will also throw an exception if there is an I/O error when + * reading the stream. Note that providers typically use mark and reset + * on the stream when examining the data, and as a result an IOException + * may be thrown if the stream does not support these. + * @param is the stream to examine + * @return the audio file format + * @throws UnsupportedAudioFileException if the stream's format is not + * recognized + * @throws IOException if there is an I/O error while reading the stream + */ + public abstract AudioFileFormat getAudioFileFormat(InputStream is) + throws UnsupportedAudioFileException, IOException; + + /** + * Return the format of the given URL as deduced by this provider. + * If the format of the URL is not recognized, throws an exception. + * This will also throw an exception if there is an I/O error when + * reading the URL. + * @param url the URL to examine + * @return the audio file format + * @throws UnsupportedAudioFileException if the URL's format is not + * recognized + * @throws IOException if there is an I/O error while reading the URL + */ + public abstract AudioFileFormat getAudioFileFormat(URL url) + throws UnsupportedAudioFileException, IOException; + + /** + * Return an AudioInputStream for the given file. The file is assumed + * to hold valid audio data. + * @param file the file to read + * @return an AudioInputStream for the file + * @throws UnsupportedAudioFileException if the file's type is not + * recognized + * @throws IOException if there is an error while reading the file + */ + public abstract AudioInputStream getAudioInputStream(File file) + throws UnsupportedAudioFileException, IOException; + + /** + * Return an AudioInputStream wrapping the given input stream. The stream + * is assumed to hold valid audio data. + * @param is the input stream to wrap + * @return an AudioInputStream for the stream + * @throws UnsupportedAudioFileException if the stream's type is not + * recognized + * @throws IOException if there is an error while reading the stream + */ + public abstract AudioInputStream getAudioInputStream(InputStream is) + throws UnsupportedAudioFileException, IOException; + + /** + * Return an AudioInputStream for the given URL. The URL is assumed + * to hold valid audio data. + * @param url the URL to read + * @return an AudioInputStream for the URL + * @throws UnsupportedAudioFileException if the URL's type is not + * recognized + * @throws IOException if there is an error while reading the URL + */ + public abstract AudioInputStream getAudioInputStream(URL url) + throws UnsupportedAudioFileException, IOException; +} diff --git a/libjava/classpath/javax/sound/sampled/spi/AudioFileWriter.java b/libjava/classpath/javax/sound/sampled/spi/AudioFileWriter.java new file mode 100644 index 00000000000..955bb051715 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/spi/AudioFileWriter.java @@ -0,0 +1,131 @@ +/* Audio file writer API + Copyright (C) 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 javax.sound.sampled.spi; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioInputStream; + +/** + * This abstract class provides an API for writing audio files. Concrete + * subclasses implement the methods declared here. + * @since 1.3 + */ +public abstract class AudioFileWriter +{ + /** + * Creat a new audio file writer. + */ + public AudioFileWriter() + { + } + + /** + * Return an array of all audio file format types supported by this + * provider. + */ + public abstract AudioFileFormat.Type[] getAudioFileTypes(); + + /** + * Return an array of all the audio file format types supported by this + * provider, which can be written given the input stream. + * @param ais the audio input stream + */ + public abstract AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream ais); + + /** + * Return true if the indicated type is supported by this provider. + * @param type the audio file format type + */ + public boolean isFileTypeSupported(AudioFileFormat.Type type) + { + AudioFileFormat.Type[] types = getAudioFileTypes(); + for (int i = 0; i < types.length; ++i) + { + if (type.equals(types[i])) + return true; + } + return false; + } + + /** + * Return true if the indicated type is supported by this provider, + * and can be written from the given audio input stream. + * @param type the audio file format type + * @param ais the audio input stream to write + */ + public boolean isFileTypeSupported(AudioFileFormat.Type type, + AudioInputStream ais) + { + AudioFileFormat.Type[] types = getAudioFileTypes(ais); + for (int i = 0; i < types.length; ++i) + { + if (type.equals(types[i])) + return true; + } + return false; + } + + /** + * Write audio data to a file. + * @param ais the audio input stream to write + * @param type the desired audio file format type + * @param out the file to write to + * @return the number of bytes written + * @throws IOException if an I/O error occurs when writing + */ + public abstract int write(AudioInputStream ais, AudioFileFormat.Type type, + File out) + throws IOException; + + /** + * Write audio data to an output stream. + * @param ais the audio input stream to write + * @param type the desired audio file format type + * @param os the output stream + * @return the number of bytes written + * @throws IOException if an I/O error occurs when writing + */ + public abstract int write(AudioInputStream ais, AudioFileFormat.Type type, + OutputStream os) + throws IOException; +} diff --git a/libjava/classpath/javax/sound/sampled/spi/FormatConversionProvider.java b/libjava/classpath/javax/sound/sampled/spi/FormatConversionProvider.java new file mode 100644 index 00000000000..e96cc0454d1 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/spi/FormatConversionProvider.java @@ -0,0 +1,179 @@ +/* Format conversion API + Copyright (C) 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 javax.sound.sampled.spi; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; + +/** + * A format conversion provider supplies methods for converting between + * different audio formats. This abstract class defines the interface + * to this functionality; concrete subclasses will implement the methods + * declared here. + * @since 1.3 + */ +public abstract class FormatConversionProvider +{ + /** + * Create a new format conversion provider. + */ + public FormatConversionProvider() + { + } + + /** + * Return an audio input stream given the desired target encoding and + * another audio input stream. The data in the given stream will be + * converted to the desired encoding. + * @param encoding the encoding + * @param source the source audio input stream + * @return a new audio input stream + * @throws IllegalArgumentException if the conversion is not supported + */ + public abstract AudioInputStream getAudioInputStream(AudioFormat.Encoding encoding, + AudioInputStream source); + + /** + * Return an audio input stream given the desired target format and + * another audio input stream. The data in the given stream will be + * converted to the desired format. + * @param format the format + * @param source the source audio input stream + * @return a new audio input stream + * @throws IllegalArgumentException if the conversion is not supported + */ + public abstract AudioInputStream getAudioInputStream(AudioFormat format, + AudioInputStream source); + + /** + * Return an array of all the source encodings supported by this conversion + * provider. + */ + public abstract AudioFormat.Encoding[] getSourceEncodings(); + + /** + * Return an array of all the target encodings supported by this conversion + * provider. + */ + public abstract AudioFormat.Encoding[] getTargetEncodings(); + + /** + * Return an array of all the target encodings that are available for a given + * source format. + * @param fmt the source format + * @return an array of supported target encodings + */ + public abstract AudioFormat.Encoding[] getTargetEncodings(AudioFormat fmt); + + /** + * Return a array of all the target formats that match given target encoding, + * and to which this provider can convert the source format. + * @param targ the target encoding to match + * @param src the source format + * @return an array of supported target formats + */ + public abstract AudioFormat[] getTargetFormats(AudioFormat.Encoding targ, + AudioFormat src); + + /** + * Return true if this provider supports conversion from the given + * source format to the given target encoding. + * @param targ the target encoding + * @param src the source format + * @return true if the conversion is supported + */ + public boolean isConversionSupported(AudioFormat.Encoding targ, + AudioFormat src) + { + AudioFormat.Encoding[] encodings = getTargetEncodings(src); + for (int i = 0; i < encodings.length; ++i) + { + if (targ.equals(encodings[i])) + return true; + } + return false; + } + + /** + * Return true if this provider supports conversions from the given + * source format to the given target format. + * @param targ the source format + * @param src the target format + * @return true if the conversion is supported + */ + public boolean isConversionSupported(AudioFormat targ, AudioFormat src) + { + AudioFormat[] encodings = getTargetFormats(targ.getEncoding(), src); + return encodings.length > 0; + } + + /** + * Return true if an encoding matching the argument is supported as a + * source encoding by this provider. + * @param src the source encoding + * @return true if it is supported + */ + public boolean isSourceEncodingSupported(AudioFormat.Encoding src) + { + AudioFormat.Encoding[] srcs = getSourceEncodings(); + for (int i = 0; i < srcs.length; ++i) + { + if (src.equals(srcs[i])) + return true; + } + return false; + } + + /** + * Return true if an encoding matching the argument is supported as a + * target encoding by this provider. + * @param targ the target encoding + * @return true if it is supported + */ + public boolean isTargetEncodingSupported(AudioFormat.Encoding targ) + { + AudioFormat.Encoding[] encodings = getTargetEncodings(); + for (int i = 0; i < encodings.length; ++i) + { + if (targ.equals(encodings[i])) + return true; + } + return false; + } +} diff --git a/libjava/classpath/javax/sound/sampled/spi/MixerProvider.java b/libjava/classpath/javax/sound/sampled/spi/MixerProvider.java new file mode 100644 index 00000000000..1ae7b3bb7d8 --- /dev/null +++ b/libjava/classpath/javax/sound/sampled/spi/MixerProvider.java @@ -0,0 +1,86 @@ +/* Mixer API + Copyright (C) 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 javax.sound.sampled.spi; + +import javax.sound.sampled.Mixer; + +/** + * This abstract class defines an interface to mixer providers. + * Concrete subclasses will implement the methods in this class. + * @since 1.3 + */ +public abstract class MixerProvider +{ + /** + * Create a new mixer provider. + */ + public MixerProvider() + { + } + + /** + * Return a mixer that matches the given info object. + * @param info description of the mixer to match + * @return the mixer + * @throws IllegalArgumentException if no mixer matches the description + */ + public abstract Mixer getMixer(Mixer.Info info); + + /** + * Return an array of info objects describing all the mixers provided by + * this provider. + */ + public abstract Mixer.Info[] getMixerInfo(); + + /** + * Return true if a mixer matching the provided description is supported. + * @param info description of the mixer to match + * @return true if it is supported by this provider + */ + public boolean isMixerSupported(Mixer.Info info) + { + Mixer.Info[] infos = getMixerInfo(); + for (int i = 0; i < infos.length; ++i) + { + if (info.equals(infos[i])) + return true; + } + return false; + } +} diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java index 21c4fc0a26c..376b3a056ae 100644 --- a/libjava/classpath/javax/swing/AbstractButton.java +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -165,6 +165,8 @@ public abstract class AbstractButton extends JComponent */ public void stateChanged(ChangeEvent ev) { + AbstractButton.this.fireStateChanged(); + repaint(); } } @@ -375,6 +377,7 @@ public abstract class AbstractButton extends JComponent protected AccessibleAbstractButton() { + // Nothing to do here yet. } public AccessibleStateSet getAccessibleStateSet() @@ -509,11 +512,37 @@ public abstract class AbstractButton extends JComponent } /** - * Creates a new AbstractButton object. + * Creates a new AbstractButton object. Subclasses should call the following + * sequence in their constructor in order to initialize the button correctly: + * <pre> + * super(); + * init(text, icon); + * </pre> + * + * The {@link #init(String, Icon)} method is not called automatically by this + * constructor. + * + * @see #init(String, Icon) */ public AbstractButton() { - init("", null); + actionListener = createActionListener(); + changeListener = createChangeListener(); + itemListener = createItemListener(); + + horizontalAlignment = CENTER; + horizontalTextPosition = TRAILING; + verticalAlignment = CENTER; + verticalTextPosition = CENTER; + borderPainted = true; + contentAreaFilled = true; + focusPainted = true; + setFocusable(true); + setAlignmentX(CENTER_ALIGNMENT); + setAlignmentY(CENTER_ALIGNMENT); + setDisplayedMnemonicIndex(-1); + setOpaque(true); + text = ""; updateUI(); } @@ -524,7 +553,7 @@ public abstract class AbstractButton extends JComponent */ public ButtonModel getModel() { - return model; + return model; } /** @@ -569,25 +598,6 @@ public abstract class AbstractButton extends JComponent if (icon != null) default_icon = icon; - - actionListener = createActionListener(); - changeListener = createChangeListener(); - itemListener = createItemListener(); - - horizontalAlignment = CENTER; - horizontalTextPosition = TRAILING; - verticalAlignment = CENTER; - verticalTextPosition = CENTER; - borderPainted = true; - contentAreaFilled = true; - - focusPainted = true; - setFocusable(true); - - setAlignmentX(LEFT_ALIGNMENT); - setAlignmentY(CENTER_ALIGNMENT); - - setDisplayedMnemonicIndex(-1); } /** @@ -615,7 +625,8 @@ public abstract class AbstractButton extends JComponent */ public void setActionCommand(String actionCommand) { - model.setActionCommand(actionCommand); + if (model != null) + model.setActionCommand(actionCommand); } /** @@ -782,7 +793,10 @@ public abstract class AbstractButton extends JComponent */ public int getMnemonic() { - return getModel().getMnemonic(); + ButtonModel mod = getModel(); + if (mod != null) + return mod.getMnemonic(); + return -1; } /** @@ -810,11 +824,15 @@ public abstract class AbstractButton extends JComponent */ public void setMnemonic(int mne) { - int old = getModel().getMnemonic(); + ButtonModel mod = getModel(); + int old = -1; + if (mod != null) + old = mod.getMnemonic(); if (old != mne) { - getModel().setMnemonic(mne); + if (mod != null) + mod.setMnemonic(mne); if (text != null && !text.equals("")) { @@ -907,7 +925,9 @@ public abstract class AbstractButton extends JComponent */ public void setSelected(boolean s) { - getModel().setSelected(s); + ButtonModel mod = getModel(); + if (mod != null) + mod.setSelected(s); } /** @@ -918,7 +938,10 @@ public abstract class AbstractButton extends JComponent */ public boolean isSelected() { - return getModel().isSelected(); + ButtonModel mod = getModel(); + if (mod != null) + return mod.isSelected(); + return false; } /** @@ -929,8 +952,14 @@ public abstract class AbstractButton extends JComponent */ public void setEnabled(boolean b) { + // Do nothing if state does not change. + if (b == isEnabled()) + return; super.setEnabled(b); - getModel().setEnabled(b); + setFocusable(b); + ButtonModel mod = getModel(); + if (mod != null) + mod.setEnabled(b); } /** @@ -1608,16 +1637,9 @@ public abstract class AbstractButton extends JComponent * * @return The new ChangeListener */ - protected ChangeListener createChangeListener() + protected ChangeListener createChangeListener() { - return new ChangeListener() - { - public void stateChanged(ChangeEvent e) - { - AbstractButton.this.fireStateChanged(); - AbstractButton.this.repaint(); - } - }; + return new ButtonChangeListener(); } /** @@ -1669,18 +1691,22 @@ public abstract class AbstractButton extends JComponent */ public void doClick(int pressTime) { - getModel().setArmed(true); - getModel().setPressed(true); - try + ButtonModel mod = getModel(); + if (mod != null) { - java.lang.Thread.sleep(pressTime); - } - catch (java.lang.InterruptedException e) - { - // probably harmless + mod.setArmed(true); + mod.setPressed(true); + try + { + java.lang.Thread.sleep(pressTime); + } + catch (java.lang.InterruptedException e) + { + // probably harmless + } + mod.setPressed(false); + mod.setArmed(false); } - getModel().setPressed(false); - getModel().setArmed(false); } /** @@ -1979,6 +2005,7 @@ public abstract class AbstractButton extends JComponent */ public void updateUI() { + // TODO: What to do here? } /** diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java index d61113b0827..1a82f0a359d 100644 --- a/libjava/classpath/javax/swing/AbstractSpinnerModel.java +++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java @@ -61,6 +61,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel */ public AbstractSpinnerModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/ActionMap.java b/libjava/classpath/javax/swing/ActionMap.java index c14bafdb4be..65e193d2e79 100644 --- a/libjava/classpath/javax/swing/ActionMap.java +++ b/libjava/classpath/javax/swing/ActionMap.java @@ -80,6 +80,7 @@ public class ActionMap */ public ActionMap() { + // Nothing to do here. } /** @@ -170,7 +171,9 @@ public class ActionMap */ public Object[] keys() { - return actionMap.keySet().toArray(); + if (size() != 0) + return actionMap.keySet().toArray(); + return null; } /** @@ -187,7 +190,9 @@ public class ActionMap set.addAll(Arrays.asList(parent.allKeys())); set.addAll(actionMap.keySet()); - return set.toArray(); + if (set.size() != 0) + return set.toArray(); + return null; } /** diff --git a/libjava/classpath/javax/swing/BorderFactory.java b/libjava/classpath/javax/swing/BorderFactory.java index 45cf3bbe074..ca78deb1290 100644 --- a/libjava/classpath/javax/swing/BorderFactory.java +++ b/libjava/classpath/javax/swing/BorderFactory.java @@ -71,7 +71,7 @@ public class BorderFactory */ public static Border createLineBorder(Color color) { - return null; + return createLineBorder(color, 1); } /** diff --git a/libjava/classpath/javax/swing/BoundedRangeModel.java b/libjava/classpath/javax/swing/BoundedRangeModel.java index 5ca5a7e043e..54446acd5ea 100644 --- a/libjava/classpath/javax/swing/BoundedRangeModel.java +++ b/libjava/classpath/javax/swing/BoundedRangeModel.java @@ -165,13 +165,13 @@ public interface BoundedRangeModel * * @param value the value * @param extent the extent - * @param minnimum the minimum value + * @param minimum the minimum value * @param maximum the maximum value * @param adjusting a flag that indicates the model is being adjusted * continuously. */ void setRangeProperties(int value, int extent, int minimum, int maximum, - boolean adjusting); + boolean adjusting); /** * Adds a <code>ChangeListener</code> to this object. diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java index b2cb44aceb5..57519f6fcbd 100644 --- a/libjava/classpath/javax/swing/Box.java +++ b/libjava/classpath/javax/swing/Box.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.AWTError; import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.LayoutManager; @@ -63,13 +64,13 @@ public class Box extends JComponent implements Accessible /** * Provides accessibility support for <code>Box</code>es. */ - // FIXME: disable to make libjava compile; visibility rules are broken - protected class AccessibleBox // extends Container.AccessibleAWTContainer + protected class AccessibleBox extends Container.AccessibleAWTContainer { private static final long serialVersionUID = -7775079816389931944L; protected AccessibleBox() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -88,13 +89,14 @@ public class Box extends JComponent implements Accessible /** * Provides accessibility support for <code>Box.Filler</code>. */ - // FIXME: disable to make libjava compile; visibility rules are broken - protected class AccessibleBoxFiller // extends Component.AccessibleAWTComponent + protected class AccessibleBoxFiller + extends Component.AccessibleAWTComponent { private static final long serialVersionUID = 164963348357479321L; protected AccessibleBoxFiller() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -103,8 +105,6 @@ public class Box extends JComponent implements Accessible } } - protected AccessibleContext accessibleContext; - private transient Dimension min, pref, max; /** @@ -135,9 +135,8 @@ public class Box extends JComponent implements Accessible public AccessibleContext getAccessibleContext() { - // FIXME: disable to make libjava compile; visibility rules are broken - // if (accessibleContext == null) - // accessibleContext = new AccessibleBoxFiller(); + if (accessibleContext == null) + accessibleContext = new AccessibleBoxFiller(); return accessibleContext; } @@ -284,8 +283,8 @@ public class Box extends JComponent implements Accessible public AccessibleContext getAccessibleContext() { - // if (accessibleContext == null) - // accessibleContext = new AccessibleBox(); + if (accessibleContext == null) + accessibleContext = new AccessibleBox(); return accessibleContext; } diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java index 28bb53928ef..ebc0b4c211c 100644 --- a/libjava/classpath/javax/swing/BoxLayout.java +++ b/libjava/classpath/javax/swing/BoxLayout.java @@ -45,12 +45,6 @@ import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager2; import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Vector; - -import gnu.java.awt.AWTUtilities; /** * A layout that stacks the children of a container in a Box, either @@ -63,248 +57,6 @@ public class BoxLayout implements LayoutManager2, Serializable { /** - * This is an abstraction that allows the BoxLayout algorithm to - * be applied to both direction (X and Y) without duplicating the - * algorithm. It defines several methods that access properties of - * a component for a specific direction. - */ - static interface Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - int size(Dimension d); - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param the {@link Insets} object from which to return the lower bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - int lower(Insets insets); - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - float alignment(Component comp); - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - void setLocation(Component c, int coord1, int coord2); - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - void setSize(Component c, int size1, int size2); - } - - /** - * The horizontal direction. - */ - static class Horizontal implements Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - public int size(Dimension d) - { - return d.width; - } - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param insets the {@link Insets} object from which to return the lower - * bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - public int lower(Insets insets) - { - return insets.left; - } - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - public float alignment(Component comp) - { - return comp.getAlignmentX(); - } - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - public void setLocation(Component c, int coord1, int coord2) - { - c.setLocation(coord1, coord2); - } - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - public void setSize(Component c, int size1, int size2) - { - c.setSize(size1, size2); - } - } - /** - * The vertical direction. - */ - static class Vertical implements Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - public int size(Dimension d) - { - return d.height; - } - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param insets the {@link Insets} object from which to return the lower - * bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - public int lower(Insets insets) - { - return insets.top; - } - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - public float alignment(Component comp) - { - return comp.getAlignmentY(); - } - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - public void setLocation(Component c, int coord1, int coord2) - { - c.setLocation(coord2, coord1); - } - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - public void setSize(Component c, int size1, int size2) - { - c.setSize(size2, size1); - } - } - - /** - * A helper class that temporarily stores the size specs of a component. - */ - static class SizeReq - { - int size; - int min; - int pref; - int max; - float align; - Component comp; - SizeReq(Component comp, Direction dir) - { - this.min = dir.size(comp.getMinimumSize()); - this.pref = dir.size(comp.getPreferredSize()); - this.max = dir.size(comp.getMaximumSize()); - this.size = dir.size(comp.getSize()); - this.align = dir.alignment(comp); - this.comp = comp; - } - } - - /** * Specifies that components are laid out left to right. */ public static final int X_AXIS = 0; @@ -334,16 +86,50 @@ public class BoxLayout implements LayoutManager2, Serializable */ private Container container; - /* + /** * Current type of component layouting. Defaults to X_AXIS. */ private int way = X_AXIS; - /** Constant for the horizontal direction. */ - private static final Direction HORIZONTAL = new Horizontal(); + /** + * The size requirements of the containers children for the X direction. + */ + private SizeRequirements[] xChildren; + + /** + * The size requirements of the containers children for the Y direction. + */ + private SizeRequirements[] yChildren; + + /** + * The size requirements of the container to be laid out for the X direction. + */ + private SizeRequirements xTotal; + + /** + * The size requirements of the container to be laid out for the Y direction. + */ + private SizeRequirements yTotal; + + /** + * The offsets of the child components in the X direction. + */ + private int[] offsetsX; + + /** + * The offsets of the child components in the Y direction. + */ + private int[] offsetsY; + + /** + * The spans of the child components in the X direction. + */ + private int[] spansX; - /** Constant for the vertical direction. */ - private static final Direction VERTICAL = new Vertical(); + /** + * The spans of the child components in the Y direction. + */ + private int[] spansY; /** * Constructs a <code>BoxLayout</code> object. @@ -355,6 +141,9 @@ public class BoxLayout implements LayoutManager2, Serializable */ public BoxLayout(Container container, int way) { + if (way != X_AXIS && way != Y_AXIS && way != LINE_AXIS && way != PAGE_AXIS) + throw new AWTError("Invalid axis"); + int width = 0; int height = 0; this.container = container; @@ -369,6 +158,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void addLayoutComponent(String name, Component component) { + // Nothing to do here. } /** @@ -378,6 +168,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void removeLayoutComponent(Component component) { + // Nothing to do here. } private boolean isHorizontalIn(Container parent) @@ -401,45 +192,16 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension preferredLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = 0; - int y = 0; - - List children = AWTUtilities.getVisibleChildren(parent); + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); - if (isHorizontalIn(parent)) - { - x = insets.left + insets.right; - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getPreferredSize(); - x += sz.width; - y = Math.max(y, sz.height); - } - y += insets.bottom + insets.top; - } - else - { - y = insets.top + insets.bottom; - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getPreferredSize(); - y += sz.height; - x = Math.max(x, sz.width); - } - x += insets.left + insets.right; + checkTotalRequirements(); + Insets i = container.getInsets(); + return new Dimension(xTotal.preferred + i.left + i.right, + yTotal.preferred + i.top + i.bottom); } - - return new Dimension(x, y); } /** @@ -451,41 +213,16 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension minimumLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = insets.left + insets.right; - int y = insets.bottom + insets.top; - - List children = AWTUtilities.getVisibleChildren(parent); - - if (isHorizontalIn(parent)) + synchronized (container.getTreeLock()) { - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getMinimumSize(); - x += sz.width; - y = Math.max(y, sz.height); - } - } - else - { - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getMinimumSize(); - y += sz.height; - x = Math.max(x, sz.width); - } + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkTotalRequirements(); + Insets i = container.getInsets(); + return new Dimension(xTotal.minimum + i.left + i.right, + yTotal.minimum + i.top + i.bottom); } - - return new Dimension(x, y); } /** @@ -495,12 +232,20 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void layoutContainer(Container parent) { - if (isHorizontalIn(parent)) - layoutAlgorithm(parent, HORIZONTAL, VERTICAL); - else - layoutAlgorithm(parent, VERTICAL, HORIZONTAL); + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkLayout(); + Component[] children = container.getComponents(); + Insets in = container.getInsets(); + for (int i = 0; i < children.length; i++) + children[i].setBounds(offsetsX[i] + in.left, offsetsY[i] + in.top, + spansX[i], spansY[i]); + } } - + /** * Adds a component to the layout. Not used in BoxLayout * @@ -509,6 +254,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void addLayoutComponent(Component child, Object constraints) { + // Nothing to do here. } /** @@ -520,10 +266,14 @@ public class BoxLayout implements LayoutManager2, Serializable */ public float getLayoutAlignmentX(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - return 0; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkTotalRequirements(); + return xTotal.alignment; + } } /** @@ -535,10 +285,14 @@ public class BoxLayout implements LayoutManager2, Serializable */ public float getLayoutAlignmentY(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - return 0; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkTotalRequirements(); + return yTotal.alignment; + } } /** @@ -548,8 +302,20 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void invalidateLayout(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + synchronized (container.getTreeLock()) + { + xChildren = null; + yChildren = null; + xTotal = null; + yTotal = null; + offsetsX = null; + offsetsY = null; + spansX = null; + spansY = null; + } } /** @@ -562,188 +328,117 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension maximumLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = insets.left + insets.right; - int y = insets.top + insets.bottom; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); - List children = AWTUtilities.getVisibleChildren(parent); + checkTotalRequirements(); + Insets i = container.getInsets(); + return new Dimension(xTotal.maximum + i.left + i.right, + yTotal.maximum + i.top + i.bottom); + } + } - if (isHorizontalIn(parent)) + /** + * Makes sure that the xTotal and yTotal fields are set up correctly. A call + * to {@link #invalidateLayout} sets these fields to null and they have to be + * recomputed. + */ + private void checkTotalRequirements() + { + if (xTotal == null || yTotal == null) { - - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) + checkRequirements(); + if (isHorizontalIn(container)) { - Component comp = (Component) i.next(); - Dimension sz = comp.getMaximumSize(); - x += sz.width; - // Check for overflow. - if (x < 0) - x = Integer.MAX_VALUE; - y = Math.max(y, sz.height); + xTotal = SizeRequirements.getTiledSizeRequirements(xChildren); + yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren); } - } - else - { - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) + else { - Component comp = (Component) i.next(); - Dimension sz = comp.getMaximumSize(); - y += sz.height; - // Check for overflow - if (y < 0) - y = Integer.MAX_VALUE; - x = Math.max(x, sz.width); + xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren); + yTotal = SizeRequirements.getTiledSizeRequirements(yChildren); } - } - return new Dimension(x, y); + } } /** - * Lays out the Container <code>c</code> in the layout direction - * <code>layoutDir</code>. The direction that is crossing the layout - * direction is specified in <code>crossDir</code>. - * - * @param parent - * @param layoutDir - * @param crossDir + * Makes sure that the xChildren and yChildren fields are correctly set up. + * A call to {@link #invalidateLayout(Container)} sets these fields to null, + * so they have to be set up again. */ - void layoutAlgorithm(Container parent, Direction layoutDir, Direction crossDir) + private void checkRequirements() { - if (parent != container) - throw new AWTError("invalid parent"); - - Dimension parentSize = parent.getSize(); - Insets insets = parent.getInsets(); - Dimension innerSize = new Dimension(parentSize.width - insets.left - - insets.right, parentSize.height - - insets.bottom - insets.top); - - // Set all components to their preferredSizes and sum up the allocated - // space. Create SizeReqs for each component and store them in - // sizeReqs. Find the maximum size in the crossing direction. - List children = AWTUtilities.getVisibleChildren(parent); - Vector sizeReqs = new Vector(); - int allocated = 0; - for (Iterator i = children.iterator(); i.hasNext();) - { - Component c = (Component) i.next(); - SizeReq sizeReq = new SizeReq(c, layoutDir); - int preferred = layoutDir.size(c.getPreferredSize()); - sizeReq.size = preferred; - allocated += preferred; - sizeReqs.add(sizeReq); - } - - // Distribute remaining space (may be positive or negative) over components - int remainder = layoutDir.size(innerSize) - allocated; - distributeSpace(sizeReqs, remainder, layoutDir); - - // Resize and relocate components. If the component can be sized to - // take the full space in the crossing direction, then do so, otherwise - // align according to its alingnmentX or alignmentY property. - int loc = 0; - int offset1 = layoutDir.lower(insets); - int offset2 = crossDir.lower(insets); - for (Iterator i = sizeReqs.iterator(); i.hasNext();) + if (xChildren == null || yChildren == null) { - SizeReq sizeReq = (SizeReq) i.next(); - Component c = sizeReq.comp; - int availCrossSize = crossDir.size(innerSize); - int maxCross = crossDir.size(c.getMaximumSize()); - int crossSize = Math.min(availCrossSize, maxCross); - int crossRemainder = availCrossSize - crossSize; - int crossLoc = (int) (crossDir.alignment(c) * crossRemainder); - layoutDir.setSize(c, sizeReq.size, crossSize); - layoutDir.setLocation(c, offset1 + loc, offset2 + crossLoc); - loc += sizeReq.size; + Component[] children = container.getComponents(); + xChildren = new SizeRequirements[children.length]; + yChildren = new SizeRequirements[children.length]; + for (int i = 0; i < children.length; i++) + { + if (! children[i].isVisible()) + { + xChildren[i] = new SizeRequirements(); + yChildren[i] = new SizeRequirements(); + } + else + { + xChildren[i] = + new SizeRequirements(children[i].getMinimumSize().width, + children[i].getPreferredSize().width, + children[i].getMaximumSize().width, + children[i].getAlignmentX()); + yChildren[i] = + new SizeRequirements(children[i].getMinimumSize().height, + children[i].getPreferredSize().height, + children[i].getMaximumSize().height, + children[i].getAlignmentY()); + } + } } } /** - * Distributes some space over a set of components. This implementation - * tries to set the components as close as possible to their - * <code>preferredSize</code>s, and respects the components - * <code>minimumSize</code> and <code>maximumSize</code>. - * - * The algorithm is implemented as follows: - * - * <ul> - * <li>The <code>remainder</code> is divided by the number of components - * in <code>freeComponents</code>.</li> - * <li>The result is added to (or substracted from) the size of each - * component.</li> - * <li>If the <code>minimumSize</code> or <code>maximumSize</code> of a - * component is exceeded, then this component is set to its - * <code>minimumSize</code> or <code>maximumSize</code>, it is removed from - * <code>freeComponents</code> and the difference is added to a new - * remainder.</li> - * <li>Finally, if there is a new remainer != 0 and the - * <code>freeComponents.size() != 0</code>, then this method is called - * recursivly to distribute the newly allocated remaining space.</li> - * </ul> - * - * @param freeComponents a SizeReq collection for components that have space - * left so that they can be moved freely - * @param remainder the space that should be distributed between the - * components - * @param dir the direction in which we operate + * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set + * up correctly. A call to {@link #invalidateLayout} sets these fields + * to null and they have to be recomputed. */ - void distributeSpace(Collection freeComponents, int remainder, Direction dir) + private void checkLayout() { - // Sum up total available space in components. If the remainder is negative - // then we sum up the difference between minSize and size. If remainder - // is positive we sum up the difference between maxSize and size. - double totalAvailable = 0; - for (Iterator i = freeComponents.iterator(); i.hasNext();) - { - SizeReq sizeReq = (SizeReq) i.next(); - if (remainder >= 0) - totalAvailable += sizeReq.max - sizeReq.size; - else - totalAvailable += sizeReq.min - sizeReq.size; - } - if (totalAvailable == 0) - if (remainder >= 0) - totalAvailable = 1; - else - totalAvailable = -1; - - int newRemainder = 0; - Vector stillFree = new Vector(); - for (Iterator i = freeComponents.iterator(); i.hasNext();) + if (offsetsX == null || offsetsY == null || spansX == null + || spansY == null) { - // Add/substract share to component. - SizeReq sizeReq = (SizeReq) i.next(); - double available = 0; - if (remainder >= 0) - available = sizeReq.max - sizeReq.size; + checkRequirements(); + checkTotalRequirements(); + int len = container.getComponents().length; + offsetsX = new int[len]; + offsetsY = new int[len]; + spansX = new int[len]; + spansY = new int[len]; + + Insets in = container.getInsets(); + int width = container.getWidth() - in.left - in.right; + int height = container.getHeight() - in.top -in.bottom; + + if (isHorizontalIn(container)) + { + SizeRequirements.calculateTiledPositions(width, + xTotal, xChildren, + offsetsX, spansX); + SizeRequirements.calculateAlignedPositions(height, + yTotal, yChildren, + offsetsY, spansY); + } else - available = sizeReq.min - sizeReq.size; - int share = (int) ((available / totalAvailable) * remainder); - sizeReq.size += share; - // check for min/maximumSize - if (sizeReq.size < sizeReq.min) - { - newRemainder += sizeReq.size - sizeReq.min; - sizeReq.size = sizeReq.min; - } - else if (sizeReq.size > sizeReq.max) - { - newRemainder += sizeReq.size - sizeReq.max; - sizeReq.size = sizeReq.max; - } - else - stillFree.add(sizeReq); + { + SizeRequirements.calculateAlignedPositions(width, + xTotal, xChildren, + offsetsX, spansX); + SizeRequirements.calculateTiledPositions(height, + yTotal, yChildren, + offsetsY, spansY); + } } - // recursivly call this method if necessary - if (newRemainder != 0 && stillFree.size() > 0) - distributeSpace(stillFree, newRemainder, dir); } } diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java index 3de1d4b9f16..94f0109e634 100644 --- a/libjava/classpath/javax/swing/ButtonGroup.java +++ b/libjava/classpath/javax/swing/ButtonGroup.java @@ -79,6 +79,7 @@ public class ButtonGroup implements Serializable */ public ButtonGroup() { + // Nothing to do here. } /** @@ -89,6 +90,8 @@ public class ButtonGroup implements Serializable public void add(AbstractButton b) { b.getModel().setGroup(this); + if (b.isSelected()) + sel = b.getModel(); buttons.addElement(b); } @@ -158,7 +161,7 @@ public class ButtonGroup implements Serializable { ButtonModel old = sel; sel = m; - + if (old != null) old.setSelected(false); AbstractButton button = FindButton(old); diff --git a/libjava/classpath/javax/swing/ButtonModel.java b/libjava/classpath/javax/swing/ButtonModel.java index 1bdc5d1850d..03fac13d2fa 100644 --- a/libjava/classpath/javax/swing/ButtonModel.java +++ b/libjava/classpath/javax/swing/ButtonModel.java @@ -49,37 +49,253 @@ import javax.swing.event.ChangeListener; */ public interface ButtonModel extends ItemSelectable { - boolean isArmed(); - void setArmed(boolean b); + /** + * Returns <code>true</code> if the button is armed, <code>false</code> + * otherwise. + * + * A button is armed, when the user has pressed the mouse over it, but has + * not yet released the mouse. + * + * @return <code>true</code> if the button is armed, <code>false</code> + * otherwise + * + * @see #setArmed(boolean) + */ + boolean isArmed(); - boolean isEnabled(); - void setEnabled(boolean b); + /** + * Sets the armed flag of the button. + * + * A button is armed, when the user has pressed the mouse over it, but has + * not yet released the mouse. + * + * @param b <code>true</code> if the button is armed, <code>false</code> + * otherwise + * + * @see #isArmed() + */ + void setArmed(boolean b); - void setPressed(boolean b); - boolean isPressed(); + /** + * Returns <code>true</code> if the button is enabled, <code>false</code> + * otherwise. + * + * When a button is disabled, it is usually grayed out and the user cannot + * change its state. + * + * @return <code>true</code> if the button is enabled, <code>false</code> + * otherwise + * + * @see #setEnabled(boolean) + */ + boolean isEnabled(); + /** + * Sets the enabled flag of the button. + * + * When a button is disabled, it is usually grayed out and the user cannot + * change its state. + * + * @param b <code>true</code> if the button is enabled, <code>false</code> + * otherwise + * + * @see #isEnabled() + */ + void setEnabled(boolean b); - void removeActionListener(ActionListener l); - void addActionListener(ActionListener l); + /** + * Sets the pressed flag of the button. + * + * The button usually gets pressed when the user clicks on a button, it will + * be un-pressed when the user releases the mouse. + * + * @param b <code>true</code> if the button is pressed, <code>false</code> + * otherwise + * + * @see #isPressed() + */ + void setPressed(boolean b); - void addItemListener(ItemListener l); - void removeItemListener(ItemListener l); - - void addChangeListener(ChangeListener l); - void removeChangeListener(ChangeListener l); + /** + * Returns <code>true</code> if the button is pressed, <code>false</code> + * otherwise. + * + * The button usually gets pressed when the user clicks on a button, it will + * be un-pressed when the user releases the mouse. + * + * @return <code>true</code> if the button is pressed, <code>false</code> + * otherwise + * + * @see #setPressed(boolean) + */ + boolean isPressed(); - void setRollover(boolean b); - boolean isRollover(); + /** + * Removes an {@link ActionListener} from the list of registered listeners. + * + * @param l the action listener to remove + * + * @see #addActionListener(ActionListener) + */ + void removeActionListener(ActionListener l); - int getMnemonic(); - void setMnemonic(int key); + /** + * Adds an {@link ActionListener} to the list of registered listeners. + * + * An <code>ActionEvent</code> is usually fired when the user clicks on a + * button. + * + * @param l the action listener to add + * + * @see #removeActionListener(ActionListener) + */ + void addActionListener(ActionListener l); - void setActionCommand(String s); - String getActionCommand(); + /** + * Adds an {@link ItemListener} to the list of registered listeners. + * + * An <code>ItemEvent</code> is usually fired when a button's selected + * state changes. This applies only to buttons that support the selected + * flag. + * + * @param l the item listener to add + * + * @see #removeItemListener(ItemListener) + */ + void addItemListener(ItemListener l); - void setGroup(ButtonGroup group); + /** + * Adds an {@link ItemListener} to the list of registered listeners. + * + * @param l the item listener to add + * + * @see #removeItemListener(ItemListener) + */ + void removeItemListener(ItemListener l); - void setSelected(boolean b); - boolean isSelected(); + /** + * Adds an {@link ChangeListener} to the list of registered listeners. + * + * A <code>ChangeEvent</code> is fired when any one of the button's flags + * changes. + * + * @param l the change listener to add + * + * @see #removeChangeListener(ChangeListener) + */ + void addChangeListener(ChangeListener l); + + /** + * Adds an {@link ChangeListener} to the list of registered listeners. + * + * @param l the change listener to add + * + * @see #removeChangeListener(ChangeListener) + */ + void removeChangeListener(ChangeListener l); + + /** + * Sets the rollover flag of the button. + * + * A button is rollover-ed, when the user has moved the mouse over it, but has + * not yet pressed the mouse. + * + * @param b <code>true</code> if the button is rollover, <code>false</code> + * otherwise + * + * @see #isRollover() + */ + void setRollover(boolean b); + + /** + * Returns <code>true</code> if the button is rollover-ed, <code>false</code> + * otherwise. + * + * A button is rollover-ed, when the user has moved the mouse over it, but has + * not yet pressed the mouse. + * + * @return <code>true</code> if the button is rollover, <code>false</code> + * otherwise + * + * @see #setRollover(boolean) + */ + boolean isRollover(); + + /** + * Returns the keyboard mnemonic for the button. This specifies a shortcut + * or accelerator key that can be used to activate the button. + * + * @return the keyboard mnemonic for the button + * + * @see #setMnemonic(int) + */ + int getMnemonic(); + + /** + * Sets the keyboard mnemonic for the button. This specifies a shortcut + * or accelerator key that can be used to activate the button. + * + * @param key the keyboard mnemonic for the button + * + * @see #getMnemonic() + */ + void setMnemonic(int key); + + /** + * Sets the action command for the button. This will be used in + * <code>ActionEvents</code> fired by the button. + * + * @param s the action command to set + * + * @see #getActionCommand() + */ + void setActionCommand(String s); + + /** + * Returns the action command of the button. + * + * @return the action command of the button + * + * @see #setActionCommand(String) + */ + String getActionCommand(); + + /** + * Sets the button group for the button. Some kinds of button (e.g. radio + * buttons) allow only one button within a button group selected at any one + * time. + * + * @param group the button group to set + */ + void setGroup(ButtonGroup group); + + /** + * Sets the selected flag of the button. + * + * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons) + * can be in one of two states: selected or unselected. The selected state + * is usually toggled by clicking on the button. + * + * @param b <code>true</code> if the button is selected, <code>false</code> + * otherwise + * + * @see #isSelected() + */ + void setSelected(boolean b); + + /** + * Returns <code>true</code> if the button is selected, <code>false</code> + * otherwise. + * + * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons) + * can be in one of two states: selected or unselected. The selected state + * is usually toggled by clicking on the button. + * + * @return <code>true</code> if the button is selected, <code>false</code> + * otherwise + * + * @see #setSelected(boolean) + */ + boolean isSelected(); } diff --git a/libjava/classpath/javax/swing/CellEditor.java b/libjava/classpath/javax/swing/CellEditor.java index bdb1665750d..3d229b26675 100644 --- a/libjava/classpath/javax/swing/CellEditor.java +++ b/libjava/classpath/javax/swing/CellEditor.java @@ -83,7 +83,7 @@ public interface CellEditor /** * addCellEditorListener - * @param value0 TODO + * @param listener TODO */ void addCellEditorListener(CellEditorListener listener); diff --git a/libjava/classpath/javax/swing/CellRendererPane.java b/libjava/classpath/javax/swing/CellRendererPane.java index 886d5c5f2a8..c59afd3188a 100644 --- a/libjava/classpath/javax/swing/CellRendererPane.java +++ b/libjava/classpath/javax/swing/CellRendererPane.java @@ -54,9 +54,7 @@ import javax.accessibility.AccessibleRole; * * @author Andrew Selkirk */ -public class CellRendererPane - extends Container - implements Accessible +public class CellRendererPane extends Container implements Accessible { private static final long serialVersionUID = -7642183829532984273L; @@ -72,6 +70,7 @@ public class CellRendererPane */ protected AccessibleCellRendererPane() { + // Nothing to do here. } /** @@ -89,22 +88,13 @@ public class CellRendererPane */ protected AccessibleContext accessibleContext = null; - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - /** * Constructs a new CellRendererPane. */ public CellRendererPane() { - } // CellRendererPane() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- + // Nothing to do here. + } /** * Should not be called. @@ -113,7 +103,8 @@ public class CellRendererPane */ public void update(Graphics graphics) { - } // update() + //Nothing to do here. + } /** * Despite normal behaviour this does <em>not</em> cause the container @@ -121,7 +112,8 @@ public class CellRendererPane */ public void invalidate() { - } // invalidate() + // Overridden to do nothing. + } /** * Should not be called. @@ -130,6 +122,7 @@ public class CellRendererPane */ public void paint(Graphics graphics) { + // Overridden to do nothing. } /** @@ -147,7 +140,7 @@ public class CellRendererPane { super.addImpl(c, constraints, index); } - } // addImpl() + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -175,9 +168,10 @@ public class CellRendererPane // reparent c addImpl(c, null, 0); + Rectangle oldClip = graphics.getClipBounds(); // translate to (x,y) graphics.translate(x, y); - + graphics.clipRect(0, 0, w, h); // set bounds of c c.setBounds(0, 0, w, h); @@ -192,8 +186,8 @@ public class CellRendererPane // untranslate g graphics.translate(-x, -y); - - } // paintComponent() + graphics.setClip(oldClip); + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -215,7 +209,7 @@ public class CellRendererPane Container p, int x, int y, int w, int h) { paintComponent(graphics, c, p, x, y, w, h, false); - } // paintComponent() + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -233,7 +227,7 @@ public class CellRendererPane Container p, Rectangle r) { paintComponent(graphics, c, p, r.x, r.y, r.width, r.height); - } // paintComponent() + } /** * getAccessibleContext <em>TODO</em> diff --git a/libjava/classpath/javax/swing/ComboBoxEditor.java b/libjava/classpath/javax/swing/ComboBoxEditor.java index 4eb5fc56206..8e914e4b9fe 100644 --- a/libjava/classpath/javax/swing/ComboBoxEditor.java +++ b/libjava/classpath/javax/swing/ComboBoxEditor.java @@ -64,7 +64,7 @@ public interface ComboBoxEditor * combo box list then this method should be called to change editting item * to the new selected item. * - * @param selectedItem item that is currently selected in the combo box + * @param item item that is currently selected in the combo box */ void setItem(Object item); diff --git a/libjava/classpath/javax/swing/ComponentInputMap.java b/libjava/classpath/javax/swing/ComponentInputMap.java index f95c3104535..28aa8e22cf6 100644 --- a/libjava/classpath/javax/swing/ComponentInputMap.java +++ b/libjava/classpath/javax/swing/ComponentInputMap.java @@ -78,7 +78,8 @@ public class ComponentInputMap extends InputMap public void put(KeyStroke keystroke, Object value) { super.put(keystroke, value); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -87,7 +88,8 @@ public class ComponentInputMap extends InputMap public void clear() { super.clear(); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -98,7 +100,8 @@ public class ComponentInputMap extends InputMap public void remove(KeyStroke keystroke) { super.remove(keystroke); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -111,14 +114,19 @@ public class ComponentInputMap extends InputMap */ public void setParent(InputMap parentMap) { - if (! (parentMap instanceof ComponentInputMap)) - throw new IllegalArgumentException(); - - if (((ComponentInputMap) parentMap).getComponent() != component) - throw new IllegalArgumentException(); + if (parentMap != null && !(parentMap instanceof ComponentInputMap)) + throw new IllegalArgumentException("ComponentInputMaps can only have " + + "ComponentInputMaps for parents"); + + if (parentMap != null && + ((ComponentInputMap) parentMap).getComponent() != component) + throw new + IllegalArgumentException("ComponentInputMaps' parents must " + + "be associated with the same JComponents"); super.setParent(parentMap); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** diff --git a/libjava/classpath/javax/swing/DebugGraphics.java b/libjava/classpath/javax/swing/DebugGraphics.java index 137b82337af..126309a5844 100644 --- a/libjava/classpath/javax/swing/DebugGraphics.java +++ b/libjava/classpath/javax/swing/DebugGraphics.java @@ -42,6 +42,7 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Image; +import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.image.ImageObserver; @@ -84,15 +85,16 @@ public class DebugGraphics extends Graphics static PrintStream debugLogStream = System.out; /** - * graphics + * Counts the created DebugGraphics objects. This is used by the + * logging facility. */ - Graphics graphics; + static int counter = 0; /** - * color + * graphics */ - Color color = Color.BLACK; - + Graphics graphics; + /** * buffer */ @@ -123,7 +125,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics() { - // TODO + counter++; } /** @@ -134,7 +136,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics(Graphics graphics, JComponent component) { - this.graphics = graphics; + this(graphics); // FIXME: What shall we do with component ? } @@ -145,6 +147,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics(Graphics graphics) { + this(); this.graphics = graphics; } @@ -155,7 +158,10 @@ public class DebugGraphics extends Graphics */ public void setColor(Color color) { - this.color = color; + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting color: " + color); + + graphics.setColor(color); } /** @@ -166,7 +172,9 @@ public class DebugGraphics extends Graphics */ public Graphics create() { - return new DebugGraphics(graphics.create()); + DebugGraphics copy = new DebugGraphics(graphics.create()); + copy.debugOptions = debugOptions; + return copy; } /** @@ -182,7 +190,10 @@ public class DebugGraphics extends Graphics */ public Graphics create(int x, int y, int width, int height) { - return new DebugGraphics(graphics.create(x, y, width, height)); + DebugGraphics copy = new DebugGraphics(graphics.create(x, y, width, + height)); + copy.debugOptions = debugOptions; + return copy; } /** @@ -282,6 +293,9 @@ public class DebugGraphics extends Graphics */ public void setFont(Font font) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting font: " + font); + graphics.setFont(font); } @@ -292,7 +306,7 @@ public class DebugGraphics extends Graphics */ public Color getColor() { - return color; + return graphics.getColor(); } /** @@ -325,6 +339,9 @@ public class DebugGraphics extends Graphics */ public void translate(int x, int y) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Translating by: " + new Point(x, y)); + graphics.translate(x, y); } @@ -333,6 +350,9 @@ public class DebugGraphics extends Graphics */ public void setPaintMode() { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting paint mode"); + graphics.setPaintMode(); } @@ -343,6 +363,9 @@ public class DebugGraphics extends Graphics */ public void setXORMode(Color color) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting XOR mode: " + color); + graphics.setXORMode(color); } @@ -366,7 +389,16 @@ public class DebugGraphics extends Graphics */ public void clipRect(int x, int y, int width, int height) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().print(prefix() + " Setting clipRect: " + + new Rectangle(x, y, width, height)); + } + graphics.clipRect(x, y, width, height); + + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(" Netting clipRect: " + graphics.getClipBounds()); } /** @@ -379,6 +411,12 @@ public class DebugGraphics extends Graphics */ public void setClip(int x, int y, int width, int height) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Setting new clipRect: " + + new Rectangle(x, y, width, height)); + } + graphics.setClip(x, y, width, height); } @@ -399,6 +437,9 @@ public class DebugGraphics extends Graphics */ public void setClip(Shape shape) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting new clipRect: " + shape); + graphics.setClip(shape); } @@ -424,18 +465,27 @@ public class DebugGraphics extends Graphics */ public void drawRect(int x, int y, int width, int height) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + if ((debugOptions & LOG_OPTION) != 0) { - graphics.setColor(color); - graphics.drawRect(x, y, width, height); - sleep(debugFlashTime); + logStream().println(prefix() + " Drawing rect: " + + new Rectangle(x, y, width, height)); + } - graphics.setColor(debugFlashColor); - graphics.drawRect(x, y, width, height); - sleep(debugFlashTime); + if ((debugOptions & FLASH_OPTION) != 0) + { + Color color = graphics.getColor(); + for (int index = 0; index < (debugFlashCount - 1); ++index) + { + graphics.setColor(color); + graphics.drawRect(x, y, width, height); + sleep(debugFlashTime); + graphics.setColor(debugFlashColor); + graphics.drawRect(x, y, width, height); + sleep(debugFlashTime); + } + graphics.setColor(color); } - graphics.setColor(color); graphics.drawRect(x, y, width, height); } @@ -449,18 +499,27 @@ public class DebugGraphics extends Graphics */ public void fillRect(int x, int y, int width, int height) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + if ((debugOptions & LOG_OPTION) != 0) { - graphics.setColor(color); - graphics.fillRect(x, y, width, height); - sleep(debugFlashTime); + logStream().println(prefix() + " Filling rect: " + + new Rectangle(x, y, width, height)); + } - graphics.setColor(debugFlashColor); - graphics.fillRect(x, y, width, height); - sleep(debugFlashTime); + if ((debugOptions & FLASH_OPTION) != 0) + { + Color color = graphics.getColor(); + for (int index = 0; index < (debugFlashCount - 1); ++index) + { + graphics.setColor(color); + graphics.fillRect(x, y, width, height); + sleep(debugFlashTime); + graphics.setColor(debugFlashColor); + graphics.fillRect(x, y, width, height); + sleep(debugFlashTime); + } + graphics.setColor(color); } - graphics.setColor(color); graphics.fillRect(x, y, width, height); } @@ -474,6 +533,12 @@ public class DebugGraphics extends Graphics */ public void clearRect(int x, int y, int width, int height) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Clearing rect: " + + new Rectangle(x, y, width, height)); + } + graphics.clearRect(x, y, width, height); } @@ -490,6 +555,14 @@ public class DebugGraphics extends Graphics public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing round rect: " + + new Rectangle(x, y, width, height) + + " arcWidth: " + arcWidth + + " arcHeight: " + arcHeight); + } + graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight); } @@ -506,6 +579,14 @@ public class DebugGraphics extends Graphics public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Filling round rect: " + + new Rectangle(x, y, width, height) + + " arcWidth: " + arcWidth + + " arcHeight: " + arcHeight); + } + graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight); } @@ -519,6 +600,12 @@ public class DebugGraphics extends Graphics */ public void drawLine(int x1, int y1, int x2, int y2) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing line: from (" + x1 + ", " + + y1 + ") to (" + x2 + ", " + y2 + ")"); + } + graphics.drawLine(x1, y1, x2, y2); } @@ -533,6 +620,13 @@ public class DebugGraphics extends Graphics */ public void draw3DRect(int x, int y, int width, int height, boolean raised) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing 3D rect: " + + new Rectangle(x, y, width, height) + + "Raised bezel: " + raised); + } + graphics.draw3DRect(x, y, width, height, raised); } @@ -547,6 +641,13 @@ public class DebugGraphics extends Graphics */ public void fill3DRect(int x, int y, int width, int height, boolean raised) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Filling 3D rect: " + + new Rectangle(x, y, width, height) + + "Raised bezel: " + raised); + } + graphics.fill3DRect(x, y, width, height, raised); } @@ -560,6 +661,12 @@ public class DebugGraphics extends Graphics */ public void drawOval(int x, int y, int width, int height) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing oval: " + + new Rectangle(x, y, width, height)); + } + graphics.drawOval(x, y, width, height); } @@ -573,6 +680,12 @@ public class DebugGraphics extends Graphics */ public void fillOval(int x, int y, int width, int height) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Filling oval: " + + new Rectangle(x, y, width, height)); + } + graphics.fillOval(x, y, width, height); } @@ -589,6 +702,14 @@ public class DebugGraphics extends Graphics public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing arc: " + + new Rectangle(x, y, width, height) + + " startAngle: " + startAngle + + " arcAngle: " + arcAngle); + } + graphics.drawArc(x, y, width, height, startAngle, arcAngle); } @@ -605,6 +726,14 @@ public class DebugGraphics extends Graphics public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Filling arc: " + + new Rectangle(x, y, width, height) + + " startAngle: " + startAngle + + " arcAngle: " + arcAngle); + } + graphics.fillArc(x, y, width, height, startAngle, arcAngle); } @@ -617,6 +746,12 @@ public class DebugGraphics extends Graphics */ public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing polyline: nPoints: " + npoints + + " X's: " + xpoints + " Y's: " + ypoints); + } + graphics.drawPolyline(xpoints, ypoints, npoints); } @@ -629,6 +764,12 @@ public class DebugGraphics extends Graphics */ public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints + + " X's: " + xpoints + " Y's: " + ypoints); + } + graphics.drawPolygon(xpoints, ypoints, npoints); } @@ -641,6 +782,12 @@ public class DebugGraphics extends Graphics */ public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints + + " X's: " + xpoints + " Y's: " + ypoints); + } + graphics.fillPolygon(xpoints, ypoints, npoints); } @@ -653,6 +800,12 @@ public class DebugGraphics extends Graphics */ public void drawString(String string, int x, int y) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing string: \"" + string + + "\" at: " + new Point(x, y)); + } + graphics.drawString(string, x, y); } @@ -666,6 +819,12 @@ public class DebugGraphics extends Graphics public void drawString(AttributedCharacterIterator iterator, int x, int y) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing string: \"" + iterator + + "\" at: " + new Point(x, y)); + } + graphics.drawString(iterator, x, y); } @@ -681,6 +840,9 @@ public class DebugGraphics extends Graphics public void drawBytes(byte[] data, int offset, int length, int x, int y) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Drawing bytes at: " + new Point(x, y)); + graphics.drawBytes(data, offset, length, x, y); } @@ -696,18 +858,24 @@ public class DebugGraphics extends Graphics public void drawChars(char[] data, int offset, int length, int x, int y) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Drawing chars at: " + new Point(x, y)); + + if ((debugOptions & FLASH_OPTION) != 0) { + Color color = graphics.getColor(); + for (int index = 0; index < (debugFlashCount - 1); ++index) + { + graphics.setColor(color); + graphics.drawChars(data, offset, length, x, y); + sleep(debugFlashTime); + graphics.setColor(debugFlashColor); + graphics.drawChars(data, offset, length, x, y); + sleep(debugFlashTime); + } graphics.setColor(color); - graphics.drawChars(data, offset, length, x, y); - sleep(debugFlashTime); - - graphics.setColor(debugFlashColor); - graphics.drawChars(data, offset, length, x, y); - sleep(debugFlashTime); } - graphics.setColor(color); graphics.drawChars(data, offset, length, x, y); } @@ -723,6 +891,12 @@ public class DebugGraphics extends Graphics public boolean drawImage(Image image, int x, int y, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + " at: " + + new Point(x, y)); + } + return graphics.drawImage(image, x, y, observer); } @@ -741,6 +915,12 @@ public class DebugGraphics extends Graphics public boolean drawImage(Image image, int x, int y, int width, int height, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + + " at: " + new Rectangle(x, y, width, height)); + } + return graphics.drawImage(image, x, y, width, height, observer); } @@ -759,6 +939,13 @@ public class DebugGraphics extends Graphics public boolean drawImage(Image image, int x, int y, Color background, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + + " at: " + new Point(x, y) + + ", bgcolor: " + background); + } + return graphics.drawImage(image, x, y, background, observer); } @@ -779,6 +966,13 @@ public class DebugGraphics extends Graphics public boolean drawImage(Image image, int x, int y, int width, int height, Color background, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + + " at: " + new Rectangle(x, y, width, height) + + ", bgcolor: " + background); + } + return graphics.drawImage(image, x, y, width, height, background, observer); } @@ -802,6 +996,13 @@ public class DebugGraphics extends Graphics int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + + " destination: " + new Rectangle(dx1, dy1, dx2, dy2) + + " source: " + new Rectangle(sx1, sy1, sx2, sy2)); + } + return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } @@ -827,6 +1028,14 @@ public class DebugGraphics extends Graphics int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color background, ImageObserver observer) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Drawing image: " + image + + " destination: " + new Rectangle(dx1, dy1, dx2, dy2) + + " source: " + new Rectangle(sx1, sy1, sx2, sy2) + + ", bgcolor: " + background); + } + return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, background, observer); } @@ -843,6 +1052,13 @@ public class DebugGraphics extends Graphics public void copyArea(int x, int y, int width, int height, int destx, int desty) { + if ((debugOptions & LOG_OPTION) != 0) + { + logStream().println(prefix() + " Copying area from: " + + new Rectangle(x, y, width, height) + + " to: " + new Point(destx, desty)); + } + graphics.copyArea(x, y, width, height, destx, desty); } @@ -873,6 +1089,11 @@ public class DebugGraphics extends Graphics public void setDebugOptions(int options) { debugOptions = options; + if ((debugOptions & LOG_OPTION) != 0) + if (options == NONE_OPTION) + logStream().println(prefix() + "Disabling debug"); + else + logStream().println(prefix() + "Enabling debug"); } /** @@ -884,4 +1105,21 @@ public class DebugGraphics extends Graphics { return debugOptions; } + + /** + * Creates and returns the prefix that should be prepended to all logging + * messages. The prefix is made up like this: + * + * <code>Graphics(<counter>-1)</code> where counter is an integer number + * saying how many DebugGraphics objects have been created so far. The second + * number always seem to be 1 on Sun's JDK, this has to be investigated a + * little more. + * + * @return the prefix that should be prepended to all logging + * messages + */ + private String prefix() + { + return "Graphics(" + counter + "-1)"; + } } diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java index f7d09d5780d..7ecf3b85fc6 100644 --- a/libjava/classpath/javax/swing/DefaultButtonModel.java +++ b/libjava/classpath/javax/swing/DefaultButtonModel.java @@ -145,6 +145,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable */ public DefaultButtonModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/DefaultCellEditor.java b/libjava/classpath/javax/swing/DefaultCellEditor.java index 00e00864432..39e48551efb 100644 --- a/libjava/classpath/javax/swing/DefaultCellEditor.java +++ b/libjava/classpath/javax/swing/DefaultCellEditor.java @@ -69,7 +69,7 @@ public class DefaultCellEditor private static final long serialVersionUID = 3564035141373880027L; /** - * Delegates a couple of method calls (such as {@link #isCellEditable) + * Delegates a couple of method calls (such as {@link #isCellEditable} * to the component it contains and listens for events that indicate * that editing has stopped. */ @@ -88,12 +88,13 @@ public class DefaultCellEditor */ protected EditorDelegate() { + // Nothing to do here. } /** * setValue * - * @param event TODO + * @param value TODO */ public void setValue(Object value) { @@ -387,7 +388,7 @@ public class DefaultCellEditor /** * getTableCellEditorComponent * - * @param tree TODO + * @param table TODO * @param value TODO * @param isSelected TODO * @param row TODO diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java index b48b968d697..ea261a33bbf 100644 --- a/libjava/classpath/javax/swing/DefaultComboBoxModel.java +++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java @@ -1,5 +1,5 @@ /* DefaultComboBoxModel.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,13 +41,14 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Vector; +import javax.swing.event.ListDataEvent; + /** - * The default implementation of {@link MutableComboBoxModel}. - * This model keeps track - * of elements contained in the JComboBox as well as the current combo box - * selection. Whenever selection in the JComboBox changes, the ComboBoxModel - * will fire ListDataEvents to ComboBox's ListDataListeners. + * A model that stores a list of elements and a selected item (which may be + * <code>null</code>). Changes to the model are signalled to listeners using + * {@link ListDataEvent}. This model is designed for use by the + * {@link JComboBox} component. * * @author Andrew Selkirk * @author Olga Rodimina @@ -59,17 +60,17 @@ public class DefaultComboBoxModel extends AbstractListModel private static final long serialVersionUID = 6698657703676921904L; /** - * List containing items in the combo box + * Storage for the elements in the model's list. */ private Vector list; /** - * Currently selected item in the combo box list + * The selected item (<code>null</code> indicates no selection). */ private Object selectedItem = null; /** - * Constructor DefaultComboBoxModel. Create empty JComboBox. + * Creates a new model, initially empty. */ public DefaultComboBoxModel() { @@ -77,64 +78,92 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Constructs new DefaultComboBoxModel object and initializes its item list - * to values in the given array. + * Creates a new model and initializes its item list to the values in the + * given array. The selected item is set to the first item in the array, or + * <code>null</code> if the array length is zero. * - * @param items array containing items of the combo box. + * @param items an array containing items for the model (<code>null</code> + * not permitted). + * + * @throws NullPointerException if <code>items</code> is <code>null</code>. */ public DefaultComboBoxModel(Object[] items) { list = new Vector(Arrays.asList(items)); + if (list.size() > 0) + selectedItem = list.get(0); } /** - * Consturcts new DefaultComboBoxModel object and initializes its item list - * to values in the given vector. + * Creates a new model and initializes its item list to the values in the + * given vector. The selected item is set to the first item in the vector, + * or <code>null</code> if the vector length is zero. * - * @param vector Vector containing items for this combo box. + * @param vector a vector containing items for the model (<code>null</code> + * not permitted). + * + * @throws NullPointerException if <code>vector</code> is <code>null</code>. */ public DefaultComboBoxModel(Vector vector) { this.list = vector; + if (vector.size() > 0) + selectedItem = vector.get(0); } /** - * This method adds element to the combo box list. It fires ListDataEvent - * indicating that component was added to the combo box to all of the - * JComboBox's registered ListDataListeners. + * Adds an element to the model's item list and sends a {@link ListDataEvent} + * to all registered listeners. If the new element is the first item added + * to the list, it is set as the selected item. * - * @param object item to add to the combo box list + * @param object item to add to the model's item list. */ public void addElement(Object object) { list.add(object); - fireIntervalAdded(this, list.size() - 1, list.size()); + fireIntervalAdded(this, list.size() - 1, list.size() - 1); + if (list.size() == 1) + setSelectedItem(object); } /** - * This method removes element at the specified index from the combo box - * list. It fires ListDataEvent indicating that component was removed from - * the combo box list to all of the JComboBox's registered - * ListDataListeners. + * Removes the element at the specified index from the model's item list + * and sends a {@link ListDataEvent} to all registered listeners. If the + * element removed was the selected item, then the preceding element becomes + * the new selected item (or the next element, if there is no preceding + * element). * - * @param index index specifying location of the element to remove in the - * combo box list. + * @param index the index of the item to remove. + * + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds. */ public void removeElementAt(int index) { + int selected = getIndexOf(selectedItem); list.remove(index); + if (selected == index) // choose a new selected item + { + if (selected > 0) + selectedItem = getElementAt(selected - 1); + else + selectedItem = getElementAt(selected); + } fireIntervalRemoved(this, index, index); } /** - * This method inserts given object to the combo box list at the specified - * index. It fires ListDataEvent indicating that component was inserted to - * the combo box list to all of the JComboBox's registered - * ListDataListeners. + * Adds an element at the specified index in the model's item list + * and sends a {@link ListDataEvent} to all registered listeners. * * @param object element to insert * @param index index specifing position in the list where given element * should be inserted. + * + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds. + * + * @see #addElement(Object) */ public void insertElementAt(Object object, int index) { @@ -143,11 +172,13 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Removes given object from the combo box list. It fires ListDataEvent - * indicating that component was removed from the combo box list to all of - * the JComboBox's registered ListDataListeners. + * Removes an element from the model's item list and sends a + * {@link ListDataEvent} to all registered listeners. If the item to be + * removed is the current selected item, a new selected item will be set. + * If the element is not found in the model's item list, this method does + * nothing. * - * @param object Element that will be removed from the combo box list + * @param object the element to remove. */ public void removeElement(Object object) { @@ -157,21 +188,25 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Removes all the items from the JComboBox's item list. It fires - * ListDataEvent indicating that all the elements were removed from the - * combo box list to all of the JComboBox's registered ListDataListeners. + * Removes all the items from the model's item list, resets and selected item + * to <code>null</code>, and sends a {@link ListDataEvent} to all registered + * listeners. */ public void removeAllElements() { - list.clear(); - int listSize = getSize(); - fireIntervalAdded(this, 0, listSize); + selectedItem = null; + int size = getSize(); + if (size > 0) + { + list.clear(); + fireIntervalRemoved(this, 0, size - 1); + } } /** - * Returns number of items in the combo box list + * Returns the number of items in the model's item list. * - * @return number of items in the combo box list + * @return The number of items in the model's item list. */ public int getSize() { @@ -179,32 +214,32 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Selects given object in the combo box list. This method fires - * ListDataEvent to all registered ListDataListeners of the JComboBox. The - * start and end index of the event is set to -1 to indicate combo box's - * selection has changed, and not its contents. - * - * <p>If the given object is not contained in the combo box list then nothing - * happens.</p> + * Sets the selected item for the model and sends a {@link ListDataEvent} to + * all registered listeners. The start and end index of the event is set to + * -1 to indicate the model's selection has changed, and not its contents. * - * @param object item to select in the JComboBox + * @param object the new selected item (<code>null</code> permitted). */ public void setSelectedItem(Object object) { - - // Updates the selected item only if the given object - // is null or in the list (this is how the JDK behaves). - if(object == null || list.contains(object)) { - selectedItem = object; - fireContentsChanged(this, -1, -1); - } - + if (selectedItem == null) + { + if (object == null) + return; + } + else + { + if (selectedItem.equals(object)) + return; + } + selectedItem = object; + fireContentsChanged(this, -1, -1); } /** - * Returns currently selected item in the combo box list + * Returns the selected item. * - * @return currently selected item in the combo box list + * @return The selected item (possibly <code>null</code>). */ public Object getSelectedItem() { @@ -212,24 +247,27 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Returns element in the combo box list located at the given index + * Returns the element at the specified index in the model's item list. * - * @param index specifying location of the element in the list + * @param index the element index. * - * @return return element in the combo box list located at the given index + * @return The element at the specified index in the model's item list, or + * <code>null</code> if the <code>index</code> is outside the bounds + * of the list. */ public Object getElementAt(int index) { + if (index < 0 || index >= list.size()) + return null; return list.elementAt(index); } /** - * Returns index of the specified object in the combo box list. + * Returns the index of the specified element in the model's item list. * - * @param object element to look for in the combo box list . + * @param object the element. * - * @return Index specifying position of the specified element in combo box - * list. + * @return The index of the specified element in the model's item list. */ public int getIndexOf(Object object) { diff --git a/libjava/classpath/javax/swing/DefaultDesktopManager.java b/libjava/classpath/javax/swing/DefaultDesktopManager.java index 2b8977e9d6d..7f62c948625 100644 --- a/libjava/classpath/javax/swing/DefaultDesktopManager.java +++ b/libjava/classpath/javax/swing/DefaultDesktopManager.java @@ -91,6 +91,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable */ public DefaultDesktopManager() { + // Nothing to do here. } /** @@ -223,6 +224,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } @@ -302,6 +304,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } @@ -329,6 +332,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } } diff --git a/libjava/classpath/javax/swing/DefaultListCellRenderer.java b/libjava/classpath/javax/swing/DefaultListCellRenderer.java index 5a34ba7aa18..9a8e07071b5 100644 --- a/libjava/classpath/javax/swing/DefaultListCellRenderer.java +++ b/libjava/classpath/javax/swing/DefaultListCellRenderer.java @@ -68,6 +68,7 @@ public class DefaultListCellRenderer extends JLabel { public UIResource() { + super(); } } @@ -124,62 +125,75 @@ public class DefaultListCellRenderer extends JLabel public void validate() { + // Overridden to do nothing. } public void revalidate() { + // Overridden to do nothing. } public void repaint(long tm, int x, int y, int w, int h) { + // Overridden to do nothing. } public void repaint(Rectangle rect) { + // Overridden to do nothing. } protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, char oldValue, char newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, short oldValue, short newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, int oldValue, int newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, long oldValue, long newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, float oldValue, float newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, double oldValue, double newValue) { + // Overridden to do nothing. } public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + // Overridden to do nothing. } } diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java index f8d544d9b20..ce1dfdd79c5 100644 --- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java +++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java @@ -238,14 +238,32 @@ public class DefaultListSelectionModel implements Cloneable, */ public void setLeadSelectionIndex(int leadIndex) { + // Only set the lead selection index to < 0 if anchorSelectionIndex < 0. + if (leadIndex < 0) + { + if (anchorSelectionIndex < 0) + leadSelectionIndex = -1; + else + return; + } + + // Only touch the lead selection index if the anchor is >= 0. + if (anchorSelectionIndex < 0) + return; + + if (selectionMode == SINGLE_SELECTION) + setSelectionInterval (leadIndex, leadIndex); + int oldLeadIndex = leadSelectionIndex; + if (oldLeadIndex == -1) + oldLeadIndex = leadIndex; if (setLeadCalledFromAdd == false) oldSel = sel.clone(); leadSelectionIndex = leadIndex; if (anchorSelectionIndex == -1) - return; - + return; + int R1 = Math.min(anchorSelectionIndex, oldLeadIndex); int R2 = Math.max(anchorSelectionIndex, oldLeadIndex); int S1 = Math.min(anchorSelectionIndex, leadIndex); @@ -254,8 +272,6 @@ public class DefaultListSelectionModel implements Cloneable, int lo = Math.min(R1, S1); int hi = Math.max(R2, S2); - BitSet oldRange = sel.get(lo, hi+1); - if (isSelectedIndex(anchorSelectionIndex)) { sel.clear(R1, R2+1); @@ -265,10 +281,7 @@ public class DefaultListSelectionModel implements Cloneable, { sel.set(R1, R2+1); sel.clear(S1, S2+1); - } - - BitSet newRange = sel.get(lo, hi+1); - newRange.xor(oldRange); + } int beg = sel.nextSetBit(0), end = -1; for(int i=beg; i >= 0; i=sel.nextSetBit(i+1)) @@ -278,6 +291,27 @@ public class DefaultListSelectionModel implements Cloneable, } /** + * Moves the lead selection index to <code>leadIndex</code> without + * changing the selection values. + * + * If leadAnchorNotificationEnabled is true, send a notification covering the + * old and new lead cells. + * + * @param leadIndex the new lead selection index + * @since 1.5 + */ + public void moveLeadSelectionIndex (int leadIndex) + { + if (leadSelectionIndex == leadIndex) + return; + + leadSelectionIndex = leadIndex; + if (isLeadAnchorNotificationEnabled()) + fireValueChanged(Math.min(leadSelectionIndex, leadIndex), + Math.max(leadSelectionIndex, leadIndex)); + } + + /** * Gets the value of the {@link #leadAnchorNotificationEnabled} property. * * @return The current property value @@ -388,6 +422,9 @@ public class DefaultListSelectionModel implements Cloneable, */ public boolean isSelectedIndex(int a) { + // TODO: Probably throw an exception here? + if (a >= sel.length() || a < 0) + return false; return sel.get(a); } @@ -415,7 +452,7 @@ public class DefaultListSelectionModel implements Cloneable, oldSel = sel.clone(); if (selectionMode == SINGLE_SELECTION) - sel.clear(); + setSelectionInterval(index0, index1); // COMPAT: Like Sun (but not like IBM), we allow calls to // addSelectionInterval when selectionMode is @@ -426,10 +463,7 @@ public class DefaultListSelectionModel implements Cloneable, isSelectedIndex(index1) || isSelectedIndex(Math.max(lo-1,0)) || isSelectedIndex(Math.min(hi+1,sel.size())))) - sel.clear(); - - if (selectionMode == SINGLE_SELECTION) - index0 = index1; + sel.clear(); // We have to update the anchorSelectionIndex and leadSelectionIndex // variables diff --git a/libjava/classpath/javax/swing/DesktopManager.java b/libjava/classpath/javax/swing/DesktopManager.java index 300d66517ba..620c7ffb857 100644 --- a/libjava/classpath/javax/swing/DesktopManager.java +++ b/libjava/classpath/javax/swing/DesktopManager.java @@ -95,7 +95,7 @@ public interface DesktopManager * This method should give focus to the JInternalFrame and its default focus * owner. * - * @param frame The JInternalFrame to activate. + * @param vframe The JInternalFrame to activate. */ void activateFrame(JInternalFrame vframe); diff --git a/libjava/classpath/javax/swing/FocusManager.java b/libjava/classpath/javax/swing/FocusManager.java index 179fa6f82d2..a2109ee06c6 100644 --- a/libjava/classpath/javax/swing/FocusManager.java +++ b/libjava/classpath/javax/swing/FocusManager.java @@ -38,10 +38,19 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTEvent; import java.awt.Component; +import java.awt.Container; import java.awt.DefaultKeyboardFocusManager; +import java.awt.FocusTraversalPolicy; +import java.awt.KeyEventDispatcher; +import java.awt.KeyEventPostProcessor; import java.awt.KeyboardFocusManager; +import java.awt.Window; import java.awt.event.KeyEvent; +import java.beans.PropertyChangeListener; +import java.beans.VetoableChangeListener; +import java.util.Set; /** * This class has been obsoleted by the new @@ -54,46 +63,409 @@ public abstract class FocusManager extends DefaultKeyboardFocusManager { /** - * DisabledFocusManager + * A FocusManager that wraps an AWT KeyboardFocusManager and forwards all + * method calls to it. This is used for compatibility with the new focus + * system. + * + * @author Roman Kennke (kennke@aicas.com) */ - static class DisabledFocusManager + private static class WrappingFocusManager extends FocusManager { + /** + * The wrapped KeyboardFocusManager. + */ + private KeyboardFocusManager wrapped; + + /** + * Creates a new instance of WrappedFocusManager. + * + * @param fm the focus manager to wrap + */ + WrappingFocusManager(KeyboardFocusManager fm) + { + wrapped = fm; + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#dispatchEvent(AWTEvent)}. + * + * @param ev the event to dispatch + * + * @return <code>true</code> if the event has been dispatched, + * <code>false</code> otherwise + */ + public boolean dispatchEvent(AWTEvent ev) + { + return wrapped.dispatchEvent(ev); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#dispatchKeyEvent(KeyEvent)}. + * + * @param ev the event to dispatch + * + * @return <code>true</code> if the event has been dispatched, + * <code>false</code> otherwise + */ + public boolean dispatchKeyEvent(KeyEvent ev) + { + return wrapped.dispatchKeyEvent(ev); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#downFocusCycle(Container)}. + * + * @param c the container + */ + public void downFocusCycle(Container c) + { + wrapped.downFocusCycle(c); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#upFocusCycle(Container)}. + * + * @param c the container + */ + public void upFocusCycle(Container c) + { + wrapped.upFocusCycle(c); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#focusNextComponent(Component)}. + * + * @param c the component + */ + public void focusNextComponent(Component c) + { + wrapped.focusNextComponent(c); + } + + /** + * Wraps + * {@link DefaultKeyboardFocusManager#focusPreviousComponent(Component)}. + * + * @param c the component + */ + public void focusPreviousComponent(Component c) + { + wrapped.focusPreviousComponent(c); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#postProcessKeyEvent(KeyEvent)}. + * + * @param e the key event + * + * @return a boolead + */ + public boolean postProcessKeyEvent(KeyEvent e) + { + return wrapped.postProcessKeyEvent(e); + } + + /** + * Wraps + * {@link DefaultKeyboardFocusManager#processKeyEvent(Component, KeyEvent)}. + * + * @param c the component + * @param e the key event + */ + public void processKeyEvent(Component c, KeyEvent e) + { + wrapped.processKeyEvent(c, e); + } + + /** + * Wraps + * {@link KeyboardFocusManager#addKeyEventDispatcher(KeyEventDispatcher)}. + * + * @param d the dispatcher + */ + public void addKeyEventDispatcher(KeyEventDispatcher d) + { + wrapped.addKeyEventDispatcher(d); + } + + /** + * Wraps + * {@link KeyboardFocusManager#addKeyEventPostProcessor(KeyEventPostProcessor)}. + * + * @param p the post processor + */ + public void addKeyEventPostProcessor(KeyEventPostProcessor p) + { + wrapped.addKeyEventPostProcessor(p); + } + + /** + * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(PropertyChangeListener)}. + * + * @param l the property change listener + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + wrapped.addPropertyChangeListener(l); + } + + /** + * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(String, PropertyChangeListener)}. + * + * @param p the property name + * @param l the property change listener + */ + public void addPropertyChangeListener(String p, PropertyChangeListener l) + { + wrapped.addPropertyChangeListener(p, l); + } + + /** + * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(String, VetoableChangeListener)}. + * + * @param p the property name + * @param l the vetoable change listener + */ + public void addVetoableChangeListener(String p, VetoableChangeListener l) + { + wrapped.addVetoableChangeListener(p, l); + } + + /** + * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(VetoableChangeListener)}. + * + * @param l the vetoable change listener + */ + public void addVetoableChangeListener(VetoableChangeListener l) + { + wrapped.addVetoableChangeListener(l); + } + + /** + * Wraps {@link KeyboardFocusManager#clearGlobalFocusOwner()}. + */ + public void clearGlobalFocusOwner() + { + wrapped.clearGlobalFocusOwner(); + } + + /** + * Wraps {@link KeyboardFocusManager#getActiveWindow()}. + * + * @return the active window + */ + public Window getActiveWindow() + { + return wrapped.getActiveWindow(); + } + + /** + * Wraps {@link KeyboardFocusManager#getCurrentFocusCycleRoot()}. + * + * @return the focus cycle root + */ + public Container getCurrentFocusCycleRoot() + { + return wrapped.getCurrentFocusCycleRoot(); + } + + /** + * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalKeys(int)}. + * + * @param i the ID + * + * @return the focus traversal keys + */ + public Set getDefaultFocusTraversalKeys(int i) + { + return wrapped.getDefaultFocusTraversalKeys(i); + } + + /** + * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalPolicy()}. + * + * @return the focus traversal policy + */ + public FocusTraversalPolicy getDefaultFocusTraversalPolicy() + { + return wrapped.getDefaultFocusTraversalPolicy(); + } + + /** + * Wraps {@link KeyboardFocusManager#getFocusedWindow()}. + * + * @return the focused window + */ + public Window getFocusedWindow() + { + return wrapped.getFocusedWindow(); + } + + /** + * Wraps {@link KeyboardFocusManager#getFocusOwner()}. + * + * @return the focus owner + */ + public Component getFocusOwner() + { + return wrapped.getFocusOwner(); + } + + /** + * Wraps {@link KeyboardFocusManager#getPermanentFocusOwner()}. + * + * @return the focus owner + */ + public Component getPermanentFocusOwner() + { + return wrapped.getPermanentFocusOwner(); + } + + /** + * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners()}. + * + * @return the property change listeners + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return wrapped.getPropertyChangeListeners(); + } + + /** + * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners(String)}. + * + * @param n the property name + * + * @return the property change listeners + */ + public PropertyChangeListener[] getPropertyChangeListeners(String n) + { + return wrapped.getPropertyChangeListeners(n); + } + + /** + * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners()}. + * + * @return the vetoable change listeners + */ + public VetoableChangeListener[] getVetoableChangeListeners() + { + return wrapped.getVetoableChangeListeners(); + } + + /** + * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners(String)}. + * + * @param n the property name + * + * @return the vetoable change listeners + */ + public VetoableChangeListener[] getVetoableChangeListeners(String n) + { + return wrapped.getVetoableChangeListeners(n); + } + + + /** + * Wraps + * {@link KeyboardFocusManager#removeKeyEventDispatcher(KeyEventDispatcher)}. + * + * @param d the key event dispatcher to remove + */ + public void removeKeyEventDispatcher(KeyEventDispatcher d) + { + wrapped.removeKeyEventDispatcher(d); + } + + /** + * Wraps + * {@link KeyboardFocusManager#removeKeyEventPostProcessor(KeyEventPostProcessor)}. + * + * @param p the post processor + */ + public void removeKeyEventPostProcessor(KeyEventPostProcessor p) + { + wrapped.removeKeyEventPostProcessor(p); + } + + /** + * Wraps + * {@link KeyboardFocusManager#removePropertyChangeListener(PropertyChangeListener)}. + * + * @param l the listener + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + wrapped.removePropertyChangeListener(l); + } + + /** + * Wraps + * {@link KeyboardFocusManager#removePropertyChangeListener(String, PropertyChangeListener)}. + * + * @param n the property name + * @param l the listener + */ + public void removePropertyChangeListener(String n, PropertyChangeListener l) + { + wrapped.removePropertyChangeListener(n, l); + } + + /** + * Wraps + * {@link KeyboardFocusManager#removeVetoableChangeListener(VetoableChangeListener)}. + * + * @param l the listener + */ + public void removeVetoableChangeListener(VetoableChangeListener l) + { + wrapped.removeVetoableChangeListener(l); + } /** - * Constructor DisabledFocusManager + * Wraps + * {@link KeyboardFocusManager#removeVetoableChangeListener(String, VetoableChangeListener)}. + * + * @param n the property name + * @param l the listener */ - DisabledFocusManager() + public void removeVetoableChangeListener(String n, VetoableChangeListener l) { - // TODO + wrapped.removeVetoableChangeListener(n, l); } /** - * processKeyEvent - * @param component TODO - * @param event TODO + * Wraps + * {@link KeyboardFocusManager#setDefaultFocusTraversalKeys(int, Set)}. + * + * @param id the ID + * @param k the keystrokes */ - public void processKeyEvent(Component component, KeyEvent event) + public void setDefaultFocusTraversalKeys(int id, Set k) { - // TODO + wrapped.setDefaultFocusTraversalKeys(id, k); } /** - * focusNextComponent - * @param component TODO + * Wraps {@link KeyboardFocusManager#setDefaultFocusTraversalPolicy(FocusTraversalPolicy)}. + * + * @param p the focus traversal policy */ - public void focusNextComponent(Component component) + public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy p) { - // TODO + wrapped.setDefaultFocusTraversalPolicy(p); } /** - * focusPreviousComponent - * @param value0 TODO + * Wraps + * {@link KeyboardFocusManager#setGlobalCurrentFocusCycleRoot(Container)}. + * + * @param r the focus cycle root */ - public void focusPreviousComponent(Component value0) + public void setGlobalCurrentFocusCycleRoot(Container r) { - // TODO + wrapped.setGlobalCurrentFocusCycleRoot(r); } } @@ -117,20 +489,9 @@ public abstract class FocusManager */ public static FocusManager getCurrentManager() { - KeyboardFocusManager fm = - KeyboardFocusManager.getCurrentKeyboardFocusManager(); - if (fm instanceof FocusManager) - return (FocusManager) fm; - else - { - System.err.println("The Swing FocusManager API has been obsoleted by"); - System.err.println("the new KeyboardFocusManager system."); - System.err.println("You should either not use the Swing FocusManager"); - System.err.println("API or set the system property"); - System.err.println - ("gnu.java.awt.FocusManager=javax.swing.FocusManager"); - } - return null; + KeyboardFocusManager m = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + return new WrappingFocusManager(m); } /** diff --git a/libjava/classpath/javax/swing/ImageIcon.java b/libjava/classpath/javax/swing/ImageIcon.java index b650cd81f23..b6ed949d8dc 100644 --- a/libjava/classpath/javax/swing/ImageIcon.java +++ b/libjava/classpath/javax/swing/ImageIcon.java @@ -73,6 +73,7 @@ public class ImageIcon */ protected AccessibleImageIcon() { + // Nothing to do here. } /** @@ -204,7 +205,10 @@ public class ImageIcon private static final long serialVersionUID = 532615968316031794L; /** A dummy Component that is used in the MediaTracker. */ - protected static Component component = new Component(){}; + protected static Component component = new Component() + { + // No need to implement this. + }; /** The MediaTracker used to monitor the loading of images. */ protected static MediaTracker tracker = new MediaTracker(component); @@ -227,6 +231,7 @@ public class ImageIcon */ public ImageIcon() { + // Nothing to do here. } /** @@ -417,7 +422,7 @@ public class ImageIcon } catch (InterruptedException ex) { - ; // ignore this for now + // Ignore this for now. } finally { diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java index a7ec38c4117..cc65dfeed3e 100644 --- a/libjava/classpath/javax/swing/InputMap.java +++ b/libjava/classpath/javax/swing/InputMap.java @@ -171,8 +171,12 @@ public class InputMap */ public KeyStroke[] keys() { - KeyStroke[] array = new KeyStroke[size()]; - return (KeyStroke[]) inputMap.keySet().toArray(array); + if (size() != 0) + { + KeyStroke[] array = new KeyStroke[size()]; + return (KeyStroke[]) inputMap.keySet().toArray(array); + } + return null; } /** @@ -189,7 +193,9 @@ public class InputMap set.addAll(Arrays.asList(parent.allKeys())); set.addAll(inputMap.keySet()); - KeyStroke[] array = new KeyStroke[size()]; + if (set.size() == 0) + return null; + KeyStroke[] array = new KeyStroke[set.size()]; return (KeyStroke[]) set.toArray(array); } diff --git a/libjava/classpath/javax/swing/InputVerifier.java b/libjava/classpath/javax/swing/InputVerifier.java index 8e02ab813a3..eeb81b5d503 100644 --- a/libjava/classpath/javax/swing/InputVerifier.java +++ b/libjava/classpath/javax/swing/InputVerifier.java @@ -53,6 +53,7 @@ public abstract class InputVerifier */ public InputVerifier() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JApplet.java b/libjava/classpath/javax/swing/JApplet.java index cafb2dabbb8..3ee1046c802 100644 --- a/libjava/classpath/javax/swing/JApplet.java +++ b/libjava/classpath/javax/swing/JApplet.java @@ -47,6 +47,7 @@ import java.awt.Graphics; import java.awt.LayoutManager; import java.awt.event.KeyEvent; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; /** @@ -55,8 +56,28 @@ import javax.accessibility.AccessibleContext; * @author original author unknown */ public class JApplet extends Applet - implements RootPaneContainer + implements RootPaneContainer, Accessible { + /** + * Provides accessibility support for <code>JApplet</code>. + */ + protected class AccessibleJApplet extends Applet.AccessibleApplet + { + /** + * Creates a new instance of <code>AccessibleJApplet</code>. + */ + public AccessibleJApplet() + { + super(); + // Nothing to do here. + } + } + + /** + * The accessible context for this <code>JApplet</code>. + */ + protected AccessibleContext accessibleContext; + private static final long serialVersionUID = 7269359214497372587L; protected JRootPane rootPane; @@ -64,20 +85,13 @@ public class JApplet extends Applet /** * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0 */ - protected boolean rootPaneCheckingEnabled=false; - - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container - */ - private boolean initStageDone = false; + protected boolean rootPaneCheckingEnabled = false; public JApplet() { super.setLayout(new BorderLayout(1, 1)); getRootPane(); // Will do set/create. - initStageDone = true; // Init stage is now over. + setRootPaneCheckingEnabled(true); // Init stage is now over. } public Dimension getPreferredSize() @@ -89,13 +103,8 @@ public class JApplet extends Applet { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + "instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -155,20 +164,17 @@ public class JApplet extends Applet { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JApplet directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJApplet(); + return accessibleContext; } public JMenuBar getJMenuBar() diff --git a/libjava/classpath/javax/swing/JButton.java b/libjava/classpath/javax/swing/JButton.java index 5653fbf42f1..ff0ecfccfd4 100644 --- a/libjava/classpath/javax/swing/JButton.java +++ b/libjava/classpath/javax/swing/JButton.java @@ -75,9 +75,6 @@ public class JButton extends AbstractButton boolean def; boolean is_def; - /** The AccessibleContext for this JButton. */ - AccessibleJButton accessibleContext; - public JButton() { this(null, null); @@ -166,6 +163,10 @@ public class JButton extends AbstractButton */ public void removeNotify() { + JRootPane root = SwingUtilities.getRootPane(this); + if (root != null && root.getDefaultButton() == this) + root.setDefaultButton(null); + super.removeNotify(); } public void setDefaultCapable(boolean defaultCapable) diff --git a/libjava/classpath/javax/swing/JCheckBox.java b/libjava/classpath/javax/swing/JCheckBox.java index a743308dcca..74fda8f6dbe 100644 --- a/libjava/classpath/javax/swing/JCheckBox.java +++ b/libjava/classpath/javax/swing/JCheckBox.java @@ -38,7 +38,9 @@ exception statement from your version. */ package javax.swing; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; /** * A small box that displays a check or not, depending on it's @@ -54,8 +56,32 @@ import javax.accessibility.AccessibleContext; * * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public class JCheckBox extends JToggleButton +public class JCheckBox extends JToggleButton implements Accessible { + + /** + * Provides accessibility support for <code>JCheckBox</code>. + */ + protected class AccessibleJCheckBox extends AccessibleJToggleButton + { + /** + * Creates a new instance of <code>AccessibleJCheckBox</code>. + */ + public AccessibleJCheckBox() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of <code>JCheckBox</code>, + * {@link AccessibleRole#CHECK_BOX}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.CHECK_BOX; + } + } + private static final long serialVersionUID = -5246739313864538930L; public static final String BORDER_PAINTED_FLAT_CHANGED_PROPERTY = @@ -71,61 +97,47 @@ public class JCheckBox extends JToggleButton public JCheckBox() { - super(); - init(); + this(null, null, false); } public JCheckBox(Action action) { super(action); - init(); } public JCheckBox(Icon icon) { - super(icon); - init(); + this(null, icon, false); } public JCheckBox(Icon icon, boolean selected) { - super(icon, selected); - init(); + this(null, icon, selected); } public JCheckBox(String text) { - super(text); - init(); + this(text, null, false); } public JCheckBox(String text, boolean selected) { - super(text, selected); - init(); + this(text, null, selected); } public JCheckBox(String text, Icon icon) { - super(text, icon); - init(); + this(text, icon, false); } public JCheckBox(String text, Icon icon, boolean selected) { super(text, icon, selected); - init(); + setHorizontalAlignment(LEADING); + setBorderPainted(false); } /** - * Gets the AccessibleContext associated with this JCheckBox. - */ - public AccessibleContext getAccessibleContext() - { - return null; - } - - /** * Returns a string that specifies the name of the Look and Feel class * that renders this component. */ @@ -149,4 +161,16 @@ public class JCheckBox extends JToggleButton firePropertyChange("borderPaintedFlat", borderPaintedFlat, newValue); borderPaintedFlat = newValue; } + + /** + * Returns the accessible context for this <code>JCheckBox</code>. + * + * @return the accessible context for this <code>JCheckBox</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJCheckBox(); + return accessibleContext; + } } diff --git a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java index f9dd56500fe..815244259be 100644 --- a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java +++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java @@ -38,9 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectOutputStream; - import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; @@ -48,8 +45,9 @@ import javax.accessibility.AccessibleRole; /** * A menu item that displays a checkbox. Its behaviour is very similar * to {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check - * and uncheck this menu item by clicking on it. Also {@link #setSelected()} - * and {@link #setState()} can be use used for the same purpose. + * and uncheck this menu item by clicking on it. Also + * {@link AbstractButton#setSelected} and {@link #setState} can be use used + * for the same purpose. * <code>JCheckBoxMenuItem</code> uses * <code>ToggleButtonModel</code> to keep track of its selection. * @@ -152,10 +150,6 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, this.setVisible(true); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. @@ -248,6 +242,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, */ protected AccessibleJCheckBoxMenuItem() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JColorChooser.java b/libjava/classpath/javax/swing/JColorChooser.java index 4016b82f3fd..a9650ffb7e0 100644 --- a/libjava/classpath/javax/swing/JColorChooser.java +++ b/libjava/classpath/javax/swing/JColorChooser.java @@ -87,6 +87,7 @@ public class JColorChooser extends JComponent implements Accessible */ protected AccessibleJColorChooser() { + // Nothing to do here. } /** @@ -247,6 +248,7 @@ public class JColorChooser extends JComponent implements Accessible } catch (InterruptedException e) { + // TODO: Should this be handled? } } diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java index 47d18323a25..cd30840a6aa 100644 --- a/libjava/classpath/javax/swing/JComboBox.java +++ b/libjava/classpath/javax/swing/JComboBox.java @@ -46,8 +46,6 @@ import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.Vector; import javax.accessibility.Accessible; @@ -58,6 +56,7 @@ import javax.accessibility.AccessibleSelection; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.PopupMenuListener; +import javax.swing.event.PopupMenuEvent; import javax.swing.plaf.ComboBoxUI; /** @@ -212,10 +211,6 @@ public class JComboBox extends JComponent implements ItemSelectable, this(new DefaultComboBoxModel()); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns true JComboBox is editable and false otherwise * @@ -310,7 +305,8 @@ public class JComboBox extends JComponent implements ItemSelectable, // Stores old data model for event notification. ComboBoxModel oldDataModel = dataModel; dataModel = newDataModel; - + selectedItemReminder = newDataModel.getSelectedItem(); + // Notifies the listeners of the model change. firePropertyChange("model", oldDataModel, dataModel); } @@ -551,14 +547,37 @@ public class JComboBox extends JComponent implements ItemSelectable, return -1; } + /** + * Returns an object that is used as the display value when calculating the + * preferred size for the combo box. This value is, of course, never + * displayed anywhere. + * + * @return The prototype display value (possibly <code>null</code>). + * + * @since 1.4 + * @see #setPrototypeDisplayValue(Object) + */ public Object getPrototypeDisplayValue() { return prototypeDisplayValue; } - public void setPrototypeDisplayValue(Object newPrototypeDisplayValue) + /** + * Sets the object that is assumed to be the displayed item when calculating + * the preferred size for the combo box. A {@link PropertyChangeEvent} (with + * the name <code>prototypeDisplayValue</code>) is sent to all registered + * listeners. + * + * @param value the new value (<code>null</code> permitted). + * + * @since 1.4 + * @see #getPrototypeDisplayValue() + */ + public void setPrototypeDisplayValue(Object value) { - prototypeDisplayValue = newPrototypeDisplayValue; + Object oldValue = prototypeDisplayValue; + prototypeDisplayValue = value; + firePropertyChange("prototypeDisplayValue", oldValue, value); } /** @@ -820,6 +839,47 @@ public class JComboBox extends JComponent implements ItemSelectable, } /** + * Fires a popupMenuCanceled() event to all <code>PopupMenuListeners</code>. + * + * Note: This method is intended for use by plaf classes only. + */ + public void firePopupMenuCanceled() + { + PopupMenuListener[] listeners = getPopupMenuListeners(); + PopupMenuEvent e = new PopupMenuEvent(this); + for(int i = 0; i < listeners.length; i++) + listeners[i].popupMenuCanceled(e); + } + + /** + * Fires a popupMenuWillBecomeInvisible() event to all + * <code>PopupMenuListeners</code>. + * + * Note: This method is intended for use by plaf classes only. + */ + public void firePopupMenuWillBecomeInvisible() + { + PopupMenuListener[] listeners = getPopupMenuListeners(); + PopupMenuEvent e = new PopupMenuEvent(this); + for(int i = 0; i < listeners.length; i++) + listeners[i].popupMenuWillBecomeInvisible(e); + } + + /** + * Fires a popupMenuWillBecomeVisible() event to all + * <code>PopupMenuListeners</code>. + * + * Note: This method is intended for use by plaf classes only. + */ + public void firePopupMenuWillBecomeVisible() + { + PopupMenuListener[] listeners = getPopupMenuListeners(); + PopupMenuEvent e = new PopupMenuEvent(this); + for(int i = 0; i < listeners.length; i++) + listeners[i].popupMenuWillBecomeVisible(e); + } + + /** * This method is invoked whenever selected item changes in the combo box's * data model. It fires ItemEvent and ActionEvent to all registered * ComboBox's ItemListeners and ActionListeners respectively, indicating @@ -836,8 +896,9 @@ public class JComboBox extends JComponent implements ItemSelectable, // Fire ItemEvent to indicate that new item is selected Object newSelection = getSelectedItem(); - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - newSelection, ItemEvent.SELECTED)); + if (newSelection != null) + fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, + newSelection, ItemEvent.SELECTED)); // Fire Action Event to JComboBox's registered listeners fireActionEvent(); @@ -961,19 +1022,19 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public void processKeyEvent(KeyEvent e) { - } - - /** - * This method always returns false to indicate that JComboBox itself is - * not focus traversable. - * - * @return false to indicate that JComboBox itself is not focus traversable. - * - * @deprecated - */ - public boolean isFocusTraversable() - { - return false; + if (e.getKeyCode() == KeyEvent.VK_TAB) + setPopupVisible(false); + else if (keySelectionManager != null) + { + int i = keySelectionManager.selectionForKey(e.getKeyChar(), + getModel()); + if (i >= 0) + setSelectedIndex(i); + else + super.processKeyEvent(e); + } + else + super.processKeyEvent(e); } /** @@ -983,6 +1044,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public void setKeySelectionManager(KeySelectionManager aManager) { + keySelectionManager = aManager; } /** @@ -1147,6 +1209,7 @@ public class JComboBox extends JComponent implements ItemSelectable, protected AccessibleJComboBox() { + // Nothing to do here. } public int getAccessibleChildrenCount() @@ -1206,18 +1269,22 @@ public class JComboBox extends JComponent implements ItemSelectable, public void addAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void removeAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void clearAccessibleSelection() { + // TODO: Implement this properly. } public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } } diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index dc7689b0930..021a2a34080 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -44,6 +44,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.FocusTraversalPolicy; import java.awt.Font; @@ -53,6 +54,7 @@ import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.Window; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; @@ -64,7 +66,6 @@ import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Rectangle2D; -import java.awt.image.ImageObserver; import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -83,6 +84,8 @@ import javax.accessibility.AccessibleKeyBinding; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleStateSet; import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.TitledBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import javax.swing.event.EventListenerList; @@ -121,9 +124,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleFocusHandler implements FocusListener { - protected AccessibleFocusHandler(){} - public void focusGained(FocusEvent event){} - public void focusLost(FocusEvent valevent){} + protected AccessibleFocusHandler() + { + // TODO: Implement this properly. + } + public void focusGained(FocusEvent event) + { + // TODO: Implement this properly. + } + public void focusLost(FocusEvent valevent) + { + // TODO: Implement this properly. + } } /** @@ -132,9 +144,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleContainerHandler implements ContainerListener { - protected AccessibleContainerHandler() {} - public void componentAdded(ContainerEvent event) {} - public void componentRemoved(ContainerEvent valevent) {} + protected AccessibleContainerHandler() + { + // TODO: Implement this properly. + } + public void componentAdded(ContainerEvent event) + { + // TODO: Implement this properly. + } + public void componentRemoved(ContainerEvent valevent) + { + // TODO: Implement this properly. + } } private static final long serialVersionUID = -7047089700479897799L; @@ -142,39 +163,217 @@ public abstract class JComponent extends Container implements Serializable protected ContainerListener accessibleContainerHandler; protected FocusListener accessibleFocusHandler; - protected AccessibleJComponent() {} - public void addPropertyChangeListener(PropertyChangeListener listener) {} - public void removePropertyChangeListener(PropertyChangeListener listener) {} - public int getAccessibleChildrenCount() { return 0; } - public Accessible getAccessibleChild(int value0) { return null; } - public AccessibleStateSet getAccessibleStateSet() { return null; } - public String getAccessibleName() { return null; } - public String getAccessibleDescription() { return null; } - public AccessibleRole getAccessibleRole() { return null; } - protected String getBorderTitle(Border value0) { return null; } - public String getToolTipText() { return null; } - public String getTitledBorderText() { return null; } - public AccessibleKeyBinding getAccessibleKeyBinding() { return null; } + /** + * Manages the property change listeners; + */ + private SwingPropertyChangeSupport changeSupport; + + protected AccessibleJComponent() + { + changeSupport = new SwingPropertyChangeSupport(this); + } + + /** + * Adds a property change listener to the list of registered listeners. + * + * @param listener the listener to add + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Removes a propery change listener from the list of registered listeners. + * + * @param listener the listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns the number of accessible children of this object. + * + * @return the number of accessible children of this object + */ + public int getAccessibleChildrenCount() + { + int count = 0; + Component[] children = getComponents(); + for (int i = 0; i < children.length; ++i) + { + if (children[i] instanceof Accessible) + count++; + } + return count; + } + + /** + * Returns the accessible child component at index <code>i</code>. + * + * @param i the index of the accessible child to return + * + * @return the accessible child component at index <code>i</code> + */ + public Accessible getAccessibleChild(int i) + { + int index = 0; + Component[] children = getComponents(); + Accessible found = null; + for (int j = 0; index != i; j++) + { + if (children[j] instanceof Accessible) + index++; + if (index == i) + found = (Accessible) children[index]; + } + // TODO: Figure out what to do when i is not a valid index. + return found; + } + + /** + * Returns the accessible state set of this component. + * + * @return the accessible state set of this component + */ + public AccessibleStateSet getAccessibleStateSet() + { + // FIXME: Figure out which states should be set here, and which are + // inherited from the super class. + return super.getAccessibleStateSet(); + } + + /** + * Returns the localized name for this object. Generally this should + * almost never return {@link Component#getName()} since that is not + * a localized name. If the object is some kind of text component (like + * a menu item), then the value of the object may be returned. Also, if + * the object has a tooltip, the value of the tooltip may also be + * appropriate. + * + * @return the localized name for this object or <code>null</code> if this + * object has no name + */ + public String getAccessibleName() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * Returns the localized description of this object. + * + * @return the localized description of this object or <code>null</code> + * if this object has no description + */ + public String getAccessibleDescription() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * Returns the accessible role of this component. + * + * @return the accessible role of this component + * + * @see AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + // TODO: Check if this is correct. + return AccessibleRole.SWING_COMPONENT; + } + + /** + * Recursivly searches a border hierarchy (starting at <code>border) for + * a titled border and returns the title if one is found, <code>null</code> + * otherwise. + * + * @param border the border to start search from + * + * @return the border title of a possibly found titled border + */ + protected String getBorderTitle(Border border) + { + String title = null; + if (border instanceof CompoundBorder) + { + CompoundBorder compound = (CompoundBorder) border; + Border inner = compound.getInsideBorder(); + title = getBorderTitle(inner); + if (title == null) + { + Border outer = compound.getOutsideBorder(); + title = getBorderTitle(outer); + } + } + else if (border instanceof TitledBorder) + { + TitledBorder titled = (TitledBorder) border; + title = titled.getTitle(); + } + return title; + } + + /** + * Returns the tooltip text for this accessible component. + * + * @return the tooltip text for this accessible component + */ + public String getToolTipText() + { + return JComponent.this.getToolTipText(); + } + + /** + * Returns the title of the border of this accessible component if + * this component has a titled border, otherwise returns <code>null</code>. + * + * @return the title of the border of this accessible component if + * this component has a titled border, otherwise returns + * <code>null</code> + */ + public String getTitledBorderText() + { + return getBorderTitle(getBorder()); + } + + /** + * Returns the keybindings associated with this accessible component or + * <code>null</code> if the component does not support key bindings. + * + * @return the keybindings associated with this accessible component + */ + public AccessibleKeyBinding getAccessibleKeyBinding() + { + // TODO: Implement this properly. + return null; + } } /** * An explicit value for the component's preferred size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getPreferredSize} method on the {@link #ui} property. + * ComponentUI#getPreferredSize} method on the {@link #ui} property. */ Dimension preferredSize; /** * An explicit value for the component's minimum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMinimumSize} method on the {@link #ui} property. + * ComponentUI#getMinimumSize} method on the {@link #ui} property. */ Dimension minimumSize; /** * An explicit value for the component's maximum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMaximumSize} method on the {@link #ui} property. + * ComponentUI#getMaximumSize} method on the {@link #ui} property. */ Dimension maximumSize; @@ -191,7 +390,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentX = 0.5f; + float alignmentX = -1.0F; /** * A value between 0.0 and 1.0 indicating the preferred vertical @@ -206,7 +405,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentY = 0.5f; + float alignmentY = -1.0F; /** * The border painted around this component. @@ -219,14 +418,14 @@ public abstract class JComponent extends Container implements Serializable * The text to show in the tooltip associated with this component. * * @see #setToolTipText - * @see #getToolTipText + * @see #getToolTipText() */ String toolTipText; /** * <p>Whether to double buffer this component when painting. This flag - * should generally be <code>false</code>, except for top level - * components such as {@link JFrame} or {@link JApplet}.</p> + * should generally be <code>true</code>, to ensure good painting + * performance.</p> * * <p>All children of a double buffered component are painted into the * double buffer automatically, so only the top widget in a window needs @@ -234,22 +433,21 @@ public abstract class JComponent extends Container implements Serializable * * @see #setDoubleBuffered * @see #isDoubleBuffered - * @see #paintLock * @see #paint */ - boolean doubleBuffered = false; + boolean doubleBuffered = true; /** * A set of flags indicating which debugging graphics facilities should * be enabled on this component. The values should be a combination of - * {@link DebugGraphics.NONE_OPTION}, {@link DebugGraphics.LOG_OPTION}, - * {@link DebugGraphics.FLASH_OPTION}, or {@link - * DebugGraphics.BUFFERED_OPTION}. + * {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION}, + * {@link DebugGraphics#FLASH_OPTION}, or {@link + * DebugGraphics#BUFFERED_OPTION}. * - * @see setDebugGraphicsOptions - * @see getDebugGraphicsOptions + * @see #setDebugGraphicsOptions + * @see #getDebugGraphicsOptions * @see DebugGraphics - * @see getComponentGraphics + * @see #getComponentGraphics */ int debugGraphicsOptions; @@ -299,7 +497,7 @@ public abstract class JComponent extends Container implements Serializable * try to request focus, but the request might fail. Thus it is only * a hint guiding swing's behavior. * - * @see #requestFocus + * @see #requestFocus() * @see #isRequestFocusEnabled * @see #setRequestFocusEnabled */ @@ -312,12 +510,18 @@ public abstract class JComponent extends Container implements Serializable * timed intervals, continuing off in the direction the mouse exited the * component, until the mouse is released or re-enters the component. * - * @see setAutoscrolls - * @see getAutoscrolls + * @see #setAutoscrolls + * @see #getAutoscrolls */ boolean autoscrolls = false; /** + * Indicates whether the current paint call is already double buffered or + * not. + */ + static boolean isPaintingDoubleBuffered = false; + + /** * Listeners for events other than {@link PropertyChangeEvent} are * handled by this listener list. PropertyChangeEvents are handled in * {@link #changeSupport}. @@ -342,7 +546,7 @@ public abstract class JComponent extends Container implements Serializable private InputMap inputMap_whenFocused; private InputMap inputMap_whenAncestorOfFocused; - private InputMap inputMap_whenInFocusedWindow; + private ComponentInputMap inputMap_whenInFocusedWindow; private ActionMap actionMap; /** @since 1.3 */ private boolean verifyInputWhenFocusTarget; @@ -350,16 +554,17 @@ public abstract class JComponent extends Container implements Serializable private TransferHandler transferHandler; - /** - * A lock held during recursive painting; this is used to serialize - * access to the double buffer, and also to select the "top level" - * object which should acquire the double buffer in a given widget - * tree (which may have multiple double buffered children). - * - * @see #doubleBuffered - * @see #paint + /** + * Indicates if this component is currently painting a tile or not. + */ + private boolean paintingTile; + + /** + * A cached Rectangle object to be reused. Be careful when you use that, + * so that it doesn't get modified in another context within the same + * method call chain. */ - private static final Object paintLock = new Object(); + private static transient Rectangle rectCache; /** * The default locale of the component. @@ -404,6 +609,13 @@ public abstract class JComponent extends Container implements Serializable public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** + * Indicates if this component is completely dirty or not. This is used + * by the RepaintManager's + * {@link RepaintManager#isCompletelyDirty(JComponent)} method. + */ + boolean isCompletelyDirty = false; + + /** * Creates a new <code>JComponent</code> instance. */ public JComponent() @@ -452,7 +664,9 @@ public abstract class JComponent extends Container implements Serializable /** * Add a client property <code>value</code> to this component, associated * with <code>key</code>. If there is an existing client property - * associated with <code>key</code>, it will be replaced. + * associated with <code>key</code>, it will be replaced. A + * {@link PropertyChangeEvent} is sent to registered listeners (with the + * name of the property being <code>key.toString()</code>). * * @param key The key of the client property association to add * @param value The value of the client property association to add @@ -463,10 +677,13 @@ public abstract class JComponent extends Container implements Serializable */ public final void putClientProperty(Object key, Object value) { + Hashtable t = getClientProperties(); + Object old = t.get(key); if (value != null) - getClientProperties().put(key, value); + t.put(key, value); else - getClientProperties().remove(key); + t.remove(key); + firePropertyChange(key.toString(), old, value); } /** @@ -771,7 +988,8 @@ public abstract class JComponent extends Container implements Serializable { VetoableChangeListener[] listeners = getVetoableChangeListeners(); - PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + PropertyChangeEvent evt = + new PropertyChangeEvent(this, propertyName, oldValue, newValue); for (int i = 0; i < listeners.length; i++) listeners[i].vetoableChange(evt); @@ -797,7 +1015,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentX() { - return alignmentX; + float ret = alignmentX; + if (alignmentX < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentX(); + + return ret; } /** @@ -810,7 +1033,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentY() { - return alignmentY; + float ret = alignmentY; + if (alignmentY < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentY(); + + return ret; } /** @@ -832,9 +1060,13 @@ public abstract class JComponent extends Container implements Serializable */ public void setBorder(Border newBorder) { - Border oldBorder = border; + Border oldBorder = getBorder(); + if (oldBorder == newBorder) + return; + border = newBorder; firePropertyChange("border", oldBorder, newBorder); + repaint(); } /** @@ -885,10 +1117,19 @@ public abstract class JComponent extends Container implements Serializable * @see #paint */ protected Graphics getComponentGraphics(Graphics g) - { - g.setFont (this.getFont()); - g.setColor (this.getForeground()); - return g; + { + Graphics g2 = g; + int options = getDebugGraphicsOptions(); + if (options != DebugGraphics.NONE_OPTION) + { + if (!(g2 instanceof DebugGraphics)) + g2 = new DebugGraphics(g); + DebugGraphics dg = (DebugGraphics) g2; + dg.setDebugOptions(dg.getDebugOptions() | options); + } + g2.setFont(this.getFont()); + g2.setColor(this.getForeground()); + return g2; } /** @@ -901,7 +1142,19 @@ public abstract class JComponent extends Container implements Serializable */ public int getDebugGraphicsOptions() { - return 0; + String option = System.getProperty("gnu.javax.swing.DebugGraphics"); + int options = debugGraphicsOptions; + if (option != null && option.length() != 0) + { + if (options < 0) + options = 0; + + if (option.equals("LOG")) + options |= DebugGraphics.LOG_OPTION; + else if (option.equals("FLASH")) + options |= DebugGraphics.FLASH_OPTION; + } + return options; } /** @@ -1298,6 +1551,7 @@ public abstract class JComponent extends Container implements Serializable */ public void grabFocus() { + // TODO: Implement this properly. } /** @@ -1366,13 +1620,16 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return <code>true</code> if this component is currently painting a tile. + * Return <code>true</code> if this component is currently painting a tile, + * this means that paint() is called again on another child component. This + * method returns <code>false</code> if this component does not paint a tile + * or if the last tile is currently painted. * - * @return Whether the component is painting a tile + * @return whether the component is painting a tile */ public boolean isPaintingTile() { - return false; + return paintingTile; } /** @@ -1406,16 +1663,6 @@ public abstract class JComponent extends Container implements Serializable * RepaintManager}. Client code should usually call {@link #repaint()} to * trigger painting.</p> * - * <p>This method will acquire a double buffer from the {@link - * RepaintManager} if the component's {@link #doubleBuffered} property is - * <code>true</code> and the <code>paint</code> call is the - * <em>first</em> recursive <code>paint</code> call inside swing.</p> - * - * <p>The method will also modify the provided {@link Graphics} context - * via the {@link #getComponentGraphics} method. If you want to customize - * the graphics object used for painting, you should override that method - * rather than <code>paint</code>.</p> - * * <p>The body of the <code>paint</code> call involves calling {@link * #paintComponent}, {@link #paintBorder}, and {@link #paintChildren} in * order. If you want to customize painting behavior, you should override @@ -1431,32 +1678,30 @@ public abstract class JComponent extends Container implements Serializable */ public void paint(Graphics g) { - Graphics g2 = g; - Image doubleBuffer = null; RepaintManager rm = RepaintManager.currentManager(this); - - if (isDoubleBuffered() - && (rm.isDoubleBufferingEnabled()) - && (! Thread.holdsLock(paintLock))) - { - doubleBuffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); - } - - synchronized (paintLock) + // We do a little stunt act here to switch on double buffering if it's + // not already on. If we are not already doublebuffered, then we jump + // into the method paintDoubleBuffered, which turns on the double buffer + // and then calls paint(g) again. In the second call we go into the else + // branch of this if statement and actually paint things to the double + // buffer. When this method completes, the call stack unwinds back to + // paintDoubleBuffered, where the buffer contents is finally drawn to the + // screen. + if (!isPaintingDoubleBuffered && isDoubleBuffered() + && rm.isDoubleBufferingEnabled()) + paintDoubleBuffered(g); + else { - if (doubleBuffer != null) - { - g2 = doubleBuffer.getGraphics(); - g2.setClip(g.getClipBounds()); - } - - g2 = getComponentGraphics(g2); + if (g.getClip() == null) + g.setClip(0, 0, getWidth(), getHeight()); + Graphics g2 = getComponentGraphics(g); paintComponent(g2); paintBorder(g2); paintChildren(g2); - - if (doubleBuffer != null) - g.drawImage(doubleBuffer, 0, 0, (ImageObserver) null); + Rectangle clip = g2.getClipBounds(); + if (clip.x == 0 && clip.y == 0 && clip.width == getWidth() + && clip.height == getHeight()) + RepaintManager.currentManager(this).markCompletelyClean(this); } } @@ -1495,7 +1740,69 @@ public abstract class JComponent extends Container implements Serializable */ protected void paintChildren(Graphics g) { - super.paint(g); + Shape originalClip = g.getClip(); + Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); + g.clipRect(inner.x, inner.y, inner.width, inner.height); + Component[] children = getComponents(); + + // Find the bottommost component that needs to be painted. This is a + // component that completely covers the current clip and is opaque. In + // this case we don't need to paint the components below it. + int startIndex = children.length - 1; + // No need to check for overlapping components when this component is + // optimizedDrawingEnabled (== it tiles its children). + if (! isOptimizedDrawingEnabled()) + { + Rectangle clip = g.getClipBounds(); + for (int i = 0; i < children.length; i++) + { + Rectangle childBounds = children[i].getBounds(); + if (children[i].isOpaque() + && SwingUtilities.isRectangleContainingRectangle(childBounds, + g.getClipBounds())) + { + startIndex = i; + break; + } + } + } + // paintingTile becomes true just before we start painting the component's + // children. + paintingTile = true; + for (int i = startIndex; i >= 0; --i) + { + // paintingTile must be set to false before we begin to start painting + // the last tile. + if (i == 0) + paintingTile = false; + + if (!children[i].isVisible()) + continue; + + Rectangle bounds = children[i].getBounds(rectCache); + Rectangle oldClip = g.getClipBounds(); + if (oldClip == null) + oldClip = bounds; + + if (!g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) + continue; + + boolean translated = false; + try + { + g.clipRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.translate(bounds.x, bounds.y); + translated = true; + children[i].paint(g); + } + finally + { + if (translated) + g.translate(-bounds.x, -bounds.y); + g.setClip(oldClip); + } + } + g.setClip(originalClip); } /** @@ -1518,7 +1825,7 @@ public abstract class JComponent extends Container implements Serializable Graphics g2 = g; if (!(g instanceof Graphics2D)) g2 = g.create(); - ui.update(getComponentGraphics(g2), this); + ui.update(g2, this); if (!(g instanceof Graphics2D)) g2.dispose(); } @@ -1544,24 +1851,93 @@ public abstract class JComponent extends Container implements Serializable * that root pane. This method is called from the {@link RepaintManager} * and should always be called within the painting thread. * + * <p>This method will acquire a double buffer from the {@link + * RepaintManager} if the component's {@link #doubleBuffered} property is + * <code>true</code> and the <code>paint</code> call is the + * <em>first</em> recursive <code>paint</code> call inside swing.</p> + * + * <p>The method will also modify the provided {@link Graphics} context + * via the {@link #getComponentGraphics} method. If you want to customize + * the graphics object used for painting, you should override that method + * rather than <code>paint</code>.</p> + * * @param r The dirty rectangle to paint */ public void paintImmediately(Rectangle r) { - Component root = SwingUtilities.getRoot(this); - if (root == null || ! root.isShowing()) + // Try to find a root pane for this component. + //Component root = findPaintRoot(r); + Component root = findPaintRoot(r); + // If no paint root is found, then this component is completely overlapped + // by another component and we don't need repainting. + if (root == null) return; - Graphics g = root.getGraphics(); - if (g == null) + if (root == null || !root.isShowing()) return; - Rectangle clip = SwingUtilities.convertRectangle(this, r, root); - g.setClip(clip); - root.paint(g); + Rectangle rootClip = SwingUtilities.convertRectangle(this, r, root); + if (root instanceof JComponent) + ((JComponent) root).paintImmediately2(rootClip); + else + root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height); + } + + /** + * Performs the actual work of paintImmediatly on the repaint root. + * + * @param r the area to be repainted + */ + void paintImmediately2(Rectangle r) + { + RepaintManager rm = RepaintManager.currentManager(this); + Graphics g = getGraphics(); + g.setClip(r.x, r.y, r.width, r.height); + if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) + paintDoubleBuffered(g); + else + paintSimple(g); g.dispose(); } /** + * Performs double buffered repainting. + * + * @param g the graphics context to paint to + */ + void paintDoubleBuffered(Graphics g) + { + + Rectangle r = g.getClipBounds(); + if (r == null) + r = new Rectangle(0, 0, getWidth(), getHeight()); + RepaintManager rm = RepaintManager.currentManager(this); + + // Paint on the offscreen buffer. + Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); + Graphics g2 = buffer.getGraphics(); + g2 = getComponentGraphics(g2); + g2.setClip(r.x, r.y, r.width, r.height); + isPaintingDoubleBuffered = true; + paint(g2); + isPaintingDoubleBuffered = false; + g2.dispose(); + + // Paint the buffer contents on screen. + g.drawImage(buffer, 0, 0, this); + } + + /** + * Performs normal painting without double buffering. + * + * @param g the graphics context to use + */ + void paintSimple(Graphics g) + { + Graphics g2 = getComponentGraphics(g); + paint(g2); + } + + /** * Return a string representation for this component, for use in * debugging. * @@ -1685,7 +2061,11 @@ public abstract class JComponent extends Container implements Serializable break; case WHEN_IN_FOCUSED_WINDOW: - inputMap_whenInFocusedWindow = map; + if (map != null && !(map instanceof ComponentInputMap)) + throw new + IllegalArgumentException("WHEN_IN_FOCUSED_WINDOW " + + "InputMap must be a ComponentInputMap"); + inputMap_whenInFocusedWindow = (ComponentInputMap)map; break; case UNDEFINED_CONDITION: @@ -1711,7 +2091,7 @@ public abstract class JComponent extends Container implements Serializable case WHEN_IN_FOCUSED_WINDOW: if (inputMap_whenInFocusedWindow == null) - inputMap_whenInFocusedWindow = new InputMap(); + inputMap_whenInFocusedWindow = new ComponentInputMap(this); return inputMap_whenInFocusedWindow; case UNDEFINED_CONDITION: @@ -1797,6 +2177,7 @@ public abstract class JComponent extends Container implements Serializable */ protected void processComponentKeyEvent(KeyEvent e) { + // This method does nothing, it is meant to be overridden by subclasses. } /** @@ -1825,40 +2206,56 @@ public abstract class JComponent extends Container implements Serializable // 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in // the focused window are searched. - if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_FOCUSED, e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 1 from above comment. - e.consume(); - else if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 2 from above comment. - e.consume(); - else + KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); + boolean pressed = e.getID() == KeyEvent.KEY_PRESSED; + + if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed)) { - // This is step 3 from above comment. - Container current = this; - while ((current = current.getParent()) instanceof JComponent) + // This is step 1 from above comment. + e.consume(); + return; + } + else if (processKeyBinding + (keyStroke, e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed)) + { + // This is step 2 from above comment. + e.consume(); + return; + } + + // This is step 3 from above comment. + Container current = getParent(); + while (current != null) + { + // If current is a JComponent, see if it handles the event in its + // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps. + if ((current instanceof JComponent) && + ((JComponent)current).processKeyBinding + (keyStroke, e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed)) { - if (((JComponent)current).processKeyBinding - (KeyStroke.getKeyStrokeForEvent(e), e, - WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - { - e.consume(); - break; - } - if (current instanceof Window || current instanceof Applet - || current instanceof JInternalFrame) - break; - } - if (e.isConsumed()) - return; + e.consume(); + return; + } + + // Stop when we've tried a top-level container and it didn't handle it + if (current instanceof Window || current instanceof Applet) + break; - // This is step 4 from above comment. - // FIXME: Implement. Note, should use ComponentInputMaps rather - // than walking the entire containment hierarchy. + // Move up the hierarchy + current = current.getParent(); } + + // Current being null means the JComponent does not currently have a + // top-level ancestor, in which case we don't need to check + // WHEN_IN_FOCUSED_WINDOW bindings. + if (current == null || e.isConsumed()) + return; + + // This is step 4 from above comment. KeyboardManager maintains mappings + // related to WHEN_IN_FOCUSED_WINDOW bindings so that we don't have to + // traverse the containment hierarchy each time. + if (KeyboardManager.getManager().processKeyStroke(current, keyStroke, e)) + e.consume(); } protected boolean processKeyBinding(KeyStroke ks, @@ -1975,8 +2372,19 @@ public abstract class JComponent extends Container implements Serializable */ public void revalidate() { - invalidate(); - RepaintManager.currentManager(this).addInvalidComponent(this); + if (! EventQueue.isDispatchThread()) + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + revalidate(); + } + }); + else + { + invalidate(); + RepaintManager.currentManager(this).addInvalidComponent(this); + } } /** @@ -1999,7 +2407,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentX(float a) { - alignmentX = a; + if (a < 0.0F) + alignmentX = 0.0F; + else if (a > 1.0) + alignmentX = 1.0F; + else + alignmentX = a; } /** @@ -2009,7 +2422,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentY(float a) { - alignmentY = a; + if (a < 0.0F) + alignmentY = 0.0F; + else if (a > 1.0) + alignmentY = 1.0F; + else + alignmentY = a; } /** @@ -2049,9 +2467,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setEnabled(boolean enable) { - boolean oldEnabled = isEnabled(); + if (enable == isEnabled()) + return; super.setEnabled(enable); - firePropertyChange("enabled", oldEnabled, enable); + firePropertyChange("enabled", !enable, enable); + repaint(); } /** @@ -2061,7 +2481,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setFont(Font f) { + if (f == getFont()) + return; super.setFont(f); + revalidate(); + repaint(); } /** @@ -2071,7 +2495,10 @@ public abstract class JComponent extends Container implements Serializable */ public void setBackground(Color bg) { + if (bg == getBackground()) + return; super.setBackground(bg); + repaint(); } /** @@ -2081,42 +2508,51 @@ public abstract class JComponent extends Container implements Serializable */ public void setForeground(Color fg) { + if (fg == getForeground()) + return; super.setForeground(fg); + repaint(); } /** - * Set the value of the {@link #maximumSize} property. + * Set the value of the {@link #maximumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param max The new value of the property */ public void setMaximumSize(Dimension max) { Dimension oldMaximumSize = maximumSize; - maximumSize = max; + maximumSize = new Dimension(max); firePropertyChange("maximumSize", oldMaximumSize, maximumSize); } /** - * Set the value of the {@link #minimumSize} property. + * Set the value of the {@link #minimumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param min The new value of the property */ public void setMinimumSize(Dimension min) { Dimension oldMinimumSize = minimumSize; - minimumSize = min; + minimumSize = new Dimension(min); firePropertyChange("minimumSize", oldMinimumSize, minimumSize); } /** - * Set the value of the {@link #preferredSize} property. + * Set the value of the {@link #preferredSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param pref The new value of the property */ public void setPreferredSize(Dimension pref) { Dimension oldPreferredSize = preferredSize; - preferredSize = pref; + preferredSize = new Dimension(pref); firePropertyChange("preferredSize", oldPreferredSize, preferredSize); } @@ -2131,6 +2567,7 @@ public abstract class JComponent extends Container implements Serializable */ public void setNextFocusableComponent(Component aComponent) { + // TODO: Implement this properly. } /** @@ -2191,11 +2628,29 @@ public abstract class JComponent extends Container implements Serializable /** * Set the value of the visible property. * + * If the value is changed, then the AncestorListeners of this component + * and all its children (recursivly) are notified. + * * @param v The new value of the property */ public void setVisible(boolean v) { + // No need to do anything if the actual value doesn't change. + if (isVisible() == v) + return; + super.setVisible(v); + + // Notify AncestorListeners. + if (v == true) + fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED); + else + fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED); + + Container parent = getParent(); + if (parent != null) + parent.repaint(getX(), getY(), getWidth(), getHeight()); + revalidate(); } /** @@ -2248,7 +2703,8 @@ public abstract class JComponent extends Container implements Serializable ui.installUI(this); firePropertyChange("UI", oldUI, newUI); - + revalidate(); + repaint(); } /** @@ -2427,47 +2883,22 @@ public abstract class JComponent extends Container implements Serializable */ public void addNotify() { + // Register the WHEN_IN_FOCUSED_WINDOW keyboard bindings + // Note that here we unregister all bindings associated with + // this component and then re-register them. This may be more than + // necessary if the top-level ancestor hasn't changed. Should + // maybe improve this. + KeyboardManager km = KeyboardManager.getManager(); + km.clearBindingsForComp(this); + km.registerEntireMap((ComponentInputMap) + this.getInputMap(WHEN_IN_FOCUSED_WINDOW)); super.addNotify(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.keys(); - for (int i = 0; i < ikeys.length; i++) - { - Object o = input.get(ikeys[i]); - parentInput.put(ikeys[i], o); - } - - Object[] akeys = actions.keys(); - for (int i = 0; i < akeys.length; i++) - { - Action a = actions.get(akeys[i]); - parentAction.put(akeys[i], a); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // Notify AncestorListeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", null, parent); + firePropertyChange("ancestor", null, getParent()); } /** @@ -2490,43 +2921,14 @@ public abstract class JComponent extends Container implements Serializable { super.removeNotify(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.allKeys(); - for (int i = 0; i < ikeys.length; i++) - { - parentInput.remove(ikeys[i]); - } - - Object[] akeys = actions.allKeys(); - for (int i = 0; i < akeys.length; i++) - { - parentAction.remove(akeys[i]); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // FIXME: remove the WHEN_IN_FOCUSED_WINDOW bindings from the + // KeyboardManager + + // Notify ancestor listeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", parent, null); + firePropertyChange("ancestor", getParent(), null); } /** @@ -2733,6 +3135,215 @@ public abstract class JComponent extends Container implements Serializable */ public void reshape(int x, int y, int w, int h) { + int oldX = getX(); + int oldY = getY(); super.reshape(x, y, w, h); + // Notify AncestorListeners. + if (oldX != getX() || oldY != getY()) + fireAncestorEvent(this, AncestorEvent.ANCESTOR_MOVED); + } + + /** + * Fires an AncestorEvent to this component's and all of its child + * component's AncestorListeners. + * + * @param ancestor the component that triggered the event + * @param id the kind of ancestor event that should be fired + */ + void fireAncestorEvent(JComponent ancestor, int id) + { + // Fire event for registered ancestor listeners of this component. + AncestorListener[] listeners = getAncestorListeners(); + if (listeners.length > 0) + { + AncestorEvent ev = new AncestorEvent(this, id, + ancestor, ancestor.getParent()); + for (int i = 0; i < listeners.length; i++) + { + switch (id) + { + case AncestorEvent.ANCESTOR_MOVED: + listeners[i].ancestorMoved(ev); + break; + case AncestorEvent.ANCESTOR_ADDED: + listeners[i].ancestorAdded(ev); + break; + case AncestorEvent.ANCESTOR_REMOVED: + listeners[i].ancestorRemoved(ev); + break; + } + } + } + // Dispatch event to all children. + Component[] children = getComponents(); + for (int i = 0; i < children.length; i++) + { + if (!(children[i] instanceof JComponent)) + continue; + JComponent jc = (JComponent) children[i]; + jc.fireAncestorEvent(ancestor, id); + } + } + + /** + * Finds a suitable paint root for painting this component. This method first + * checks if this component is overlapped using + * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then + * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque + * component for this paint root. If no paint is necessary, then we return + * <code>null</code>. + * + * @param c the clip of this component + * + * @return the paint root or <code>null</code> if no painting is necessary + */ + private Component findPaintRoot(Rectangle c) + { + Component p = findOverlapFreeParent(c); + if (p == null) + return null; + Component root = findOpaqueParent(p); + return root; + } + + /** + * Scans the containment hierarchy upwards for components that overlap the + * this component in the specified clip. This method returns + * <code>this</code>, if no component overlaps this component. It returns + * <code>null</code> if another component completely covers this component + * in the specified clip (no repaint necessary). If another component partly + * overlaps this component in the specified clip, then the parent of this + * component is returned (this is the component that must be used as repaint + * root). For efficient lookup, the method + * {@link #isOptimizedDrawingEnabled()} is used. + * + * @param clip the clip of this component + * + * @return the paint root, or <code>null</code> if no paint is necessary + */ + private Component findOverlapFreeParent(Rectangle clip) + { + Rectangle currentClip = clip; + Component found = this; + Container parent = this; + while (parent != null && !(parent instanceof Window)) + { + Container newParent = parent.getParent(); + if (newParent == null) + break; + // If the parent is optimizedDrawingEnabled, then its children are + // tiled and cannot have an overlapping child. Go directly to next + // parent. + if (newParent instanceof JComponent + && ((JComponent) newParent).isOptimizedDrawingEnabled()) + { + parent = newParent; + continue; + } + + // First we must check if the new parent itself somehow clips the + // target rectangle. This can happen in JViewports. + Rectangle parRect = new Rectangle(0, 0, newParent.getWidth(), + newParent.getHeight()); + Rectangle target = SwingUtilities.convertRectangle(found, + currentClip, + newParent); + if (target.contains(parRect) || target.intersects(parRect)) + { + found = newParent; + currentClip = target; + parent = newParent; + continue; + } + + // Otherwise we must check if one of the children of this parent + // overlaps with the current component. + Component[] children = newParent.getComponents(); + // This flag is used to skip components that are 'below' the component + // in question. + boolean skip = true; + for (int i = children.length - 1; i >= 0; i--) + { + if (children[i] == parent) + skip = false; + if (skip) + continue; + Component c = children[i]; + Rectangle compBounds = c.getBounds(); + // If the component completely overlaps the clip in question, we + // don't need to repaint. Return null. + if (compBounds.contains(target)) + return null; + if (compBounds.intersects(target)) + { + // We found a parent whose children overlap with our current + // component. Make this the current component. + found = newParent; + currentClip = target; + break; + } + } + parent = newParent; + } + return found; + } + + /** + * Finds the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque. If <code>c</code> itself is opaque, + * this returns <code>c</code> itself. + * + * @param c the start component for the search + * @return the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque; If <code>c</code> itself is opaque, + * this returns <code>c</code> itself + */ + private Component findOpaqueParent(Component c) + { + Component found = c; + while (true) + { + if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) + break; + else if (!(found instanceof JComponent)) + break; + Container p = found.getParent(); + if (p == null) + break; + else + found = p; + } + return found; + } + + /** + * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map + * is changed. + * + * @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW + * map + */ + void updateComponentInputMap(ComponentInputMap changed) + { + // Since you can change a component's input map via + // setInputMap, we have to check if <code>changed</code> + // is still in our WHEN_IN_FOCUSED_WINDOW map hierarchy + InputMap curr = getInputMap(WHEN_IN_FOCUSED_WINDOW); + while (curr != null && curr != changed) + curr = curr.getParent(); + + // If curr is null then changed is not in the hierarchy + if (curr == null) + return; + + // Now we have to update the keyboard manager's hashtable + KeyboardManager km = KeyboardManager.getManager(); + + // This is a poor strategy, should be improved. We currently + // delete all the old bindings for the component and then register + // the current bindings. + km.clearBindingsForComp(changed.getComponent()); + km.registerEntireMap((ComponentInputMap) + getInputMap(WHEN_IN_FOCUSED_WINDOW)); } } diff --git a/libjava/classpath/javax/swing/JDesktopPane.java b/libjava/classpath/javax/swing/JDesktopPane.java index f4c80eca7d6..43ab71e7e9f 100644 --- a/libjava/classpath/javax/swing/JDesktopPane.java +++ b/libjava/classpath/javax/swing/JDesktopPane.java @@ -97,6 +97,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible */ protected AccessibleJDesktopPane() { + // Nothing to do here. } /** @@ -246,6 +247,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible } catch (PropertyVetoException e) { + // We do nothing when the attempt is vetoed. } } selectedFrame = null; @@ -259,6 +261,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible } catch (PropertyVetoException e) { + // We do nothing when the attempt is vetoed. } } diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java index 0f528ab1b45..b3f7c011f68 100644 --- a/libjava/classpath/javax/swing/JDialog.java +++ b/libjava/classpath/javax/swing/JDialog.java @@ -66,6 +66,21 @@ import javax.accessibility.AccessibleContext; public class JDialog extends Dialog implements Accessible, WindowConstants, RootPaneContainer { + /** + * Provides accessibility support for <code>JDialog</code>s. + */ + protected class AccessibleJDialog extends Dialog.AccessibleAWTDialog + { + /** + * Creates a new instance of <code>AccessibleJDialog</code>. + */ + public AccessibleJDialog() + { + super(); + // Nothing to do here. + } + } + private static final long serialVersionUID = -864070866424508218L; /** DOCUMENT ME! */ @@ -87,13 +102,6 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, /** Whether JDialogs are decorated by the Look and Feel. */ private static boolean decorated; - /** - * Whether we're in the init stage or not. - * If so, adds and layouts are for top-level, otherwise they're for the - * content pane - */ - private boolean initStageDone = false; - /* Creates a new non-modal JDialog with no title * using a shared Frame as the owner. */ @@ -244,7 +252,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, invalidate(); // Now that initStageDone is true, adds and layouts apply to contentPane, // not top-level. - initStageDone = true; + setRootPaneCheckingEnabled(true); } /** @@ -315,13 +323,8 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set top-level layout. Use" - + " getConentPane().setLayout instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -445,15 +448,10 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not add directly to JDialog." - + " Use getContentPane().add instead."); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } /** @@ -588,6 +586,8 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJDialog(); + return accessibleContext; } } diff --git a/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java index e2f1319a2a7..9ddf970deea 100644 --- a/libjava/classpath/javax/swing/JEditorPane.java +++ b/libjava/classpath/javax/swing/JEditorPane.java @@ -41,15 +41,27 @@ package javax.swing; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; import java.net.URL; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleHyperlink; +import javax.accessibility.AccessibleHypertext; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultEditorKit; +import javax.swing.text.Document; import javax.swing.text.EditorKit; +import javax.swing.text.Element; import javax.swing.text.JTextComponent; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; /** * A powerful text editor component that can handle different types of @@ -76,6 +88,384 @@ import javax.swing.text.JTextComponent; */ public class JEditorPane extends JTextComponent { + /** + * Provides accessibility support for <code>JEditorPane</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJEditorPane extends AccessibleJTextComponent + { + + /** + * Creates a new <code>AccessibleJEditorPane</code> object. + */ + protected AccessibleJEditorPane() + { + super(); + } + + /** + * Returns a description of this <code>AccessibleJEditorPane</code>. If + * this property is not set, then this returns the content-type of the + * editor pane. + * + * @return a description of this AccessibleJEditorPane + */ + public String getAccessibleDescription() + { + String descr = super.getAccessibleDescription(); + if (descr == null) + return getContentType(); + else + return descr; + } + + /** + * Returns the accessible state of this <code>AccessibleJEditorPane</code>. + * + * @return the accessible state of this <code>AccessibleJEditorPane</code> + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet state = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added here to the super's state. + return state; + } + } + + /** + * Provides accessibility support for <code>JEditorPane</code>s, when the + * editor kit is an instance of {@link HTMLEditorKit}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane + { + /** + * Returns the accessible text of the <code>JEditorPane</code>. This will + * be an instance of + * {@link JEditorPaneAccessibleHypertextSupport}. + * + * @return the accessible text of the <code>JEditorPane</code> + */ + public AccessibleText getAccessibleText() + { + return new JEditorPaneAccessibleHypertextSupport(); + } + } + + /** + * This is the accessible text that is returned by + * {@link AccessibleJEditorPaneHTML#getAccessibleText()}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class JEditorPaneAccessibleHypertextSupport + extends AccessibleJEditorPane implements AccessibleHypertext + { + + /** + * The accessible representation of a HTML link. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class HTMLLink extends AccessibleHyperlink + { + + /** + * The element in the document that represents the link. + */ + Element element; + + /** + * Creates a new <code>HTMLLink</code>. + * + * @param el the link element + */ + public HTMLLink(Element el) + { + this.element = el; + } + + /** + * Returns <code>true</code> if this <code>HTMLLink</code> is still + * valid. A <code>HTMLLink</code> can become invalid when the document + * changes. + * + * @return <code>true</code> if this <code>HTMLLink</code> is still + * valid + */ + public boolean isValid() + { + // I test here if the element at our element's start offset is the + // same as the element in the document at this offset. If this is true, + // I consider the link valid, if not, then this link no longer + // represented by this HTMLLink and therefor invalid. + HTMLDocument doc = (HTMLDocument) getDocument(); + return doc.getCharacterElement(element.getStartOffset()) == element; + } + + /** + * Returns the number of AccessibleActions in this link object. In + * general, link have 1 AccessibleAction associated with them. There are + * special cases where links can have multiple actions associated, like + * in image maps. + * + * @return the number of AccessibleActions in this link object + */ + public int getAccessibleActionCount() + { + // TODO: Implement the special cases. + return 1; + } + + /** + * Performs the specified action on the link object. This ususally means + * activating the link. + * + * @return <code>true</code> if the action has been performed + * successfully, <code>false</code> otherwise + */ + public boolean doAccessibleAction(int i) + { + String href = (String) element.getAttributes().getAttribute("href"); + HTMLDocument doc = (HTMLDocument) getDocument(); + try + { + URL url = new URL(doc.getBase(), href); + setPage(url); + String desc = doc.getText(element.getStartOffset(), + element.getEndOffset() - element.getStartOffset()); + HyperlinkEvent ev = + new HyperlinkEvent(JEditorPane.this, + HyperlinkEvent.EventType.ACTIVATED, url, desc, + element); + fireHyperlinkUpdate(ev); + return true; + } + catch (Exception ex) + { + return false; + } + } + + /** + * Returns the description of the action at action index <code>i</code>. + * This method returns the text within the element associated with this + * link. + * + * @param i the action index + * + * @return the description of the action at action index <code>i</code> + */ + public String getAccessibleActionDescription(int i) + { + HTMLDocument doc = (HTMLDocument) getDocument(); + try + { + return doc.getText(element.getStartOffset(), + element.getEndOffset() - element.getStartOffset()); + } + catch (BadLocationException ex) + { + throw (AssertionError) + new AssertionError("BadLocationException must not be thrown " + + "here.") + .initCause(ex); + } + } + + /** + * Returns an {@link URL} object, that represents the action at action + * index <code>i</code>. + * + * @param i the action index + * + * @return an {@link URL} object, that represents the action at action + * index <code>i</code> + */ + public Object getAccessibleActionObject(int i) + { + String href = (String) element.getAttributes().getAttribute("href"); + HTMLDocument doc = (HTMLDocument) getDocument(); + try + { + URL url = new URL(doc.getBase(), href); + return url; + } + catch (MalformedURLException ex) + { + return null; + } + } + + /** + * Returns an object that represents the link anchor. For examples, if + * the link encloses a string, then a <code>String</code> object is + * returned, if the link encloses an <img> tag, then an + * <code>ImageIcon</code> object is returned. + * + * @return an object that represents the link anchor + */ + public Object getAccessibleActionAnchor(int i) + { + // TODO: This is only the String case. Implement all cases. + return getAccessibleActionDescription(i); + } + + /** + * Returns the start index of the hyperlink element. + * + * @return the start index of the hyperlink element + */ + public int getStartIndex() + { + return element.getStartOffset(); + } + + /** + * Returns the end index of the hyperlink element. + * + * @return the end index of the hyperlink element + */ + public int getEndIndex() + { + return element.getEndOffset(); + } + + } + + /** + * Returns the number of hyperlinks in the document. + * + * @return the number of hyperlinks in the document + */ + public int getLinkCount() + { + HTMLDocument doc = (HTMLDocument) getDocument(); + HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); + int count = 0; + while (linkIter.isValid()) + { + count++; + linkIter.next(); + } + return count; + } + + /** + * Returns the <code>i</code>-th hyperlink in the document or + * <code>null</code> if there is no hyperlink with the specified index. + * + * @param i the index of the hyperlink to return + * + * @return the <code>i</code>-th hyperlink in the document or + * <code>null</code> if there is no hyperlink with the specified + * index + */ + public AccessibleHyperlink getLink(int i) + { + HTMLDocument doc = (HTMLDocument) getDocument(); + HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); + int count = 0; + while (linkIter.isValid()) + { + count++; + if (count == i) + break; + linkIter.next(); + } + if (linkIter.isValid()) + { + int offset = linkIter.getStartOffset(); + // TODO: I fetch the element for the link via getCharacterElement(). + // I am not sure that this is correct, maybe we must use + // getParagraphElement()? + Element el = doc.getCharacterElement(offset); + HTMLLink link = new HTMLLink(el); + return link; + } + else + return null; + } + + /** + * Returns the index of the link element at the character position + * <code>c</code> within the document, or <code>-1</code> if there is no + * link at the specified position. + * + * @param c the character index from which to fetch the link index + * + * @return the index of the link element at the character position + * <code>c</code> within the document, or <code>-1</code> if there + * is no link at the specified position + */ + public int getLinkIndex(int c) + { + HTMLDocument doc = (HTMLDocument) getDocument(); + HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); + int count = 0; + while (linkIter.isValid()) + { + if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c) + break; + count++; + linkIter.next(); + } + if (linkIter.isValid()) + return count; + else + return -1; + } + + /** + * Returns the link text of the link at index <code>i</code>, or + * <code>null</code>, if there is no link at the specified position. + * + * @param i the index of the link + * + * @return the link text of the link at index <code>i</code>, or + * <code>null</code>, if there is no link at the specified + * position + */ + public String getLinkText(int i) + { + HTMLDocument doc = (HTMLDocument) getDocument(); + HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A); + int count = 0; + while (linkIter.isValid()) + { + count++; + if (count == i) + break; + linkIter.next(); + } + if (linkIter.isValid()) + { + int offset = linkIter.getStartOffset(); + // TODO: I fetch the element for the link via getCharacterElement(). + // I am not sure that this is correct, maybe we must use + // getParagraphElement()? + Element el = doc.getCharacterElement(offset); + try + { + String text = doc.getText(el.getStartOffset(), + el.getEndOffset() - el.getStartOffset()); + return text; + } + catch (BadLocationException ex) + { + throw (AssertionError) + new AssertionError("BadLocationException must not be thrown " + + "here.") + .initCause(ex); + } + } + else + return null; + } + } + private static final long serialVersionUID = 3140472492599046285L; private URL page; @@ -128,9 +518,21 @@ public class JEditorPane extends JTextComponent listeners[index].hyperlinkUpdate(event); } + /** + * Returns the accessible context associated with this editor pane. + * + * @return the accessible context associated with this editor pane + */ public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + { + if (getEditorKit() instanceof HTMLEditorKit) + accessibleContext = new AccessibleJEditorPaneHTML(); + else + accessibleContext = new AccessibleJEditorPane(); + } + return accessibleContext; } public final String getContentType() @@ -169,12 +571,18 @@ public class JEditorPane extends JTextComponent public boolean getScrollableTracksViewportHeight() { - return false; + /* Container parent = getParent(); + return (parent instanceof JViewport && + parent.isValid());*/ + return isValid(); } public boolean getScrollableTracksViewportWidth() { - return false; + /*Container parent = getParent(); + return (parent instanceof JViewport && + parent.isValid());*/ + return isValid(); } public URL getPage() @@ -211,9 +619,26 @@ public class JEditorPane extends JTextComponent /** * This method initializes from a stream. */ - public void read(InputStream in, Object desc) - throws IOException + public void read(InputStream in, Object desc) throws IOException { + EditorKit kit = getEditorKit(); + if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument) + { + Document doc = (Document) desc; + try + { + kit.read(in, doc, 0); + } + catch (BadLocationException ex) + { + assert false : "BadLocationException must not be thrown here."; + } + } + else + { + Reader inRead = new InputStreamReader(in); + super.read(inRead, desc); + } } /** @@ -222,6 +647,7 @@ public class JEditorPane extends JTextComponent public static void registerEditorKitForContentType(String type, String classname) { + // TODO: Implement this properly. } /** @@ -231,6 +657,7 @@ public class JEditorPane extends JTextComponent String classname, ClassLoader loader) { + // TODO: Implement this properly. } /** @@ -239,6 +666,7 @@ public class JEditorPane extends JTextComponent */ public void replaceSelection(String content) { + // TODO: Implement this properly. } /** @@ -247,6 +675,7 @@ public class JEditorPane extends JTextComponent */ public void scrollToReference(String reference) { + // TODO: Implement this properly. } public final void setContentType(String type) @@ -281,6 +710,8 @@ public class JEditorPane extends JTextComponent firePropertyChange("editorKit", oldValue, newValue); invalidate(); repaint(); + // Reset the accessibleContext since this depends on the editorKit. + accessibleContext = null; } public void setEditorKitForContentType(String type, EditorKit k) diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 7569061ab2e..1598641f1b9 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/javax/swing/JFileChooser.java @@ -42,10 +42,13 @@ import java.awt.Frame; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; import java.io.File; import java.util.ArrayList; + import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.JDialog; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; @@ -70,171 +73,324 @@ public class JFileChooser extends JComponent implements Accessible { private static final long serialVersionUID = 3162921138695327837L; - /** DOCUMENT ME! */ + /** + * A dialog type for selecting a file to open. + * @see #setDialogType(int) + */ public static final int OPEN_DIALOG = 0; - /** DOCUMENT ME! */ + /** + * A dialog type for selecting a file to save. + * @see #setDialogType(int) + */ public static final int SAVE_DIALOG = 1; - /** DOCUMENT ME! */ + /** + * A dialog type for some custom purpose. + * @see #setDialogType(int) + */ public static final int CUSTOM_DIALOG = 2; - /** DOCUMENT ME! */ + /** + * A return value indicating the file chooser has been closed by cancelling. + * + * @see #showOpenDialog(Component) + * @see #showSaveDialog(Component) + */ public static final int CANCEL_OPTION = 1; - /** DOCUMENT ME! */ + /** + * A return value indicating the file chooser has been closed by approving + * the selection. + * @see #showOpenDialog(Component) + * @see #showSaveDialog(Component) + */ public static final int APPROVE_OPTION = 0; - /** DOCUMENT ME! */ + /** + * A return value indicating the file chooser has been closed by some error. + * @see #showOpenDialog(Component) + * @see #showSaveDialog(Component) + */ public static final int ERROR_OPTION = -1; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of files only. + * @see #setFileSelectionMode(int) + */ public static final int FILES_ONLY = 0; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of directories only. + * @see #setFileSelectionMode(int) + */ public static final int DIRECTORIES_ONLY = 1; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of files and directories. + * @see #setFileSelectionMode(int) + */ public static final int FILES_AND_DIRECTORIES = 2; - /** DOCUMENT ME! */ + /** + * Action command string for cancelling the current selection. + * @see #cancelSelection() + */ public static final String CANCEL_SELECTION = "CancelSelection"; - /** DOCUMENT ME! */ + /** + * Action command string for approving the current selection. + * @see #cancelSelection() + */ public static final String APPROVE_SELECTION = "ApproveSelection"; - /** DOCUMENT ME! */ + /** + * The name of the property for the approve button text. + * @see #setApproveButtonText(String) + */ public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY = "ApproveButtonTextChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the approve button tool tip text. + * @see #setApproveButtonToolTipText(String) + */ public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY = "ApproveButtonToolTipTextChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the approve button mnemonic. + * @see #setApproveButtonMnemonic(int) + */ public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY = "ApproveButtonMnemonicChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for control button visibility. + * @see #setControlButtonsAreShown(boolean) + */ public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY = "ControlButtonsAreShownChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the current directory. + * @see #setCurrentDirectory(File) + */ public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged"; - /** DOCUMENT ME! */ + /** + * The name of the property for the selected file. + * @see #setSelectedFile(File) + */ public static final String SELECTED_FILE_CHANGED_PROPERTY = "SelectedFileChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the selected files. + * @see #setSelectedFiles(File[]) + */ public static final String SELECTED_FILES_CHANGED_PROPERTY = "SelectedFilesChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for multi-selection. + * @see #setMultiSelectionEnabled(boolean) + */ public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "MultiSelectionEnabledChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'file system view' property. + * @see #setFileSystemView(FileSystemView) + */ public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY = "FileSystemViewChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file view' property. + * @see #setFileView(FileView) + */ public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file hiding enabled' property. + * @see #setFileHidingEnabled(boolean) + */ public static final String FILE_HIDING_CHANGED_PROPERTY = "FileHidingChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file filter' property. + * @see #setFileFilter(FileFilter) + */ public static final String FILE_FILTER_CHANGED_PROPERTY = "fileFilterChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file selection mode' property. + * @see #setFileSelectionMode(int) + */ public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY = "fileSelectionChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'accessory' property. + * @see #setAccessory(JComponent) + */ public static final String ACCESSORY_CHANGED_PROPERTY = "AccessoryChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'accept all file filter used' property. + * @see #setAcceptAllFileFilterUsed(boolean) + */ public static final String ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY = "acceptAllFileFilterUsedChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'dialog title' property. + * @see #setDialogTitle(String) + */ public static final String DIALOG_TITLE_CHANGED_PROPERTY = "DialogTitleChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'dialog type' property. + * @see #setDialogType(int) + */ public static final String DIALOG_TYPE_CHANGED_PROPERTY = "DialogTypeChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'choosable file filters' property. + * @see #addChoosableFileFilter(FileFilter) + */ public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY = "ChoosableFileFilterChangedProperty"; - /** DOCUMENT ME! */ + /** + * The accessible context. + * @see #getAccessibleContext() + */ protected AccessibleContext accessibleContext; - /** DOCUMENT ME! */ + /** + * The file system view. + * @see #setFileSystemView(FileSystemView) + */ private FileSystemView fsv; - /** DOCUMENT ME! */ + /** + * The accessory component. + * @see #setAccessory(JComponent) + */ private JComponent accessory; - /** DOCUMENT ME! */ + /** + * The approve button mnemonic. + * @see #setApproveButtonMnemonic(int) + */ private int approveButtonMnemonic = 0; - /** DOCUMENT ME! */ + /** + * The approve button text. + * @see #setApproveButtonText(String) + */ private String approveButtonText; - /** DOCUMENT ME! */ + /** + * The approve button tool tip text. + * @see #setApproveButtonToolTipText(String) + */ private String approveButtonToolTipText; - /** DOCUMENT ME! */ + /** + * The choosable file filters. + * @see #addChoosableFileFilter(FileFilter) + */ private ArrayList choosableFilters = new ArrayList(); - /** DOCUMENT ME! */ + /** + * A flag controlling whether the accept all file filter is used. + * @see #setAcceptAllFileFilterUsed(boolean) + */ private boolean isAcceptAll = true; - /** DOCUMENT ME! */ + /** + * The dialog title. + * @see #setDialogTitle(String) + */ private String dialogTitle; - /** DOCUMENT ME! */ + /** + * The dialog type. + * @see #setDialogType(int) + */ private int dialogType = OPEN_DIALOG; - /** DOCUMENT ME! */ + /** + * The return value for the dialog. + * @see #showOpenDialog(Component) + * @see #showSaveDialog(Component) + */ private int retval = ERROR_OPTION; - /** DOCUMENT ME! */ + /** + * A flag indicating whether the file chooser allows multiple selection. + * @see #isMultiSelectionEnabled() + */ private boolean multiSelection = false; - /** DOCUMENT ME! */ + /** + * A flag indicating whether file hiding is enabled. + * @see #isFileHidingEnabled() + */ private boolean fileHiding = true; - /** DOCUMENT ME! */ + /** + * The file selection mode. + * @see #setFileSelectionMode(int) + */ private int fileSelectionMode = FILES_AND_DIRECTORIES; - /** DOCUMENT ME! */ + /** + * The file view. + * @see #setFileView(FileView) + */ private FileView fv = null; - /** DOCUMENT ME! */ + /** + * A flag controlling whether or not the control buttons are visible. + * @see #setControlButtonsAreShown(boolean) + */ private boolean controlButtonsShown = true; - /** DOCUMENT ME! */ + /** + * The current directory. + * @see #setCurrentDirectory(File) + */ private File currentDir = null; - /** DOCUMENT ME! */ + /** + * The current file filter. + * @see #setFileFilter(FileFilter) + */ private FileFilter currentFilter = null; - /** DOCUMENT ME! */ + /** + * An array of selected files. + * @see #setSelectedFiles(File[]) + */ private File[] selectedFiles; - /** DOCUMENT ME! */ + /** + * The selected file. + * @see #setSelectedFile(File) + */ private File selectedFile; /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. */ public JFileChooser() { @@ -243,9 +399,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectoryPath DOCUMENT ME! + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). */ public JFileChooser(String currentDirectoryPath) { @@ -254,12 +412,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object with the specified directory and - * FileSystemView. + * Creates a new <code>JFileChooser</code> object with the specified + * directory and {@link FileSystemView}. * - * @param currentDirectoryPath the directory that should initially be - * shown the filechooser - * @param fsv the FileSystemView object to use + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(String currentDirectoryPath, FileSystemView fsv) { @@ -268,9 +428,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectory DOCUMENT ME! + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). */ public JFileChooser(File currentDirectory) { @@ -279,9 +441,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param fsv DOCUMENT ME! + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(FileSystemView fsv) { @@ -290,10 +453,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectory DOCUMENT ME! - * @param fsv DOCUMENT ME! + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(File currentDirectory, FileSystemView fsv) { @@ -302,9 +468,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets up the file chooser. This method is called by all the constructors. * - * @param view DOCUMENT ME! + * @param view the file system view (if <code>null</code>, the default file + * system view is used). + * + * @see FileSystemView#getFileSystemView() */ protected void setup(FileSystemView view) { @@ -336,9 +505,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the selected file, if there is one. * - * @return DOCUMENT ME! + * @return The selected file (possibly <code>null</code>). + * + * @see #setSelectedFile(File) */ public File getSelectedFile() { @@ -346,9 +517,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the selected file and sends a {@link PropertyChangeEvent} to all + * registered listeners. The property name is + * {@link #SELECTED_FILE_CHANGED_PROPERTY}. * - * @param file DOCUMENT ME! + * @param file the file (<code>null</code> permitted). */ public void setSelectedFile(File file) { @@ -361,9 +534,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the selected file or files. * - * @return DOCUMENT ME! + * @return An array of the selected files, or <code>null</code> if there are + * no selected files. */ public File[] getSelectedFiles() { @@ -375,9 +549,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the selected files and sends a {@link PropertyChangeEvent} (with the + * name {@link #SELECTED_FILES_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param selectedFiles DOCUMENT ME! + * @param selectedFiles the selected files (<code>null</code> permitted). */ public void setSelectedFiles(File[] selectedFiles) { @@ -393,9 +569,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the current directory. * - * @return DOCUMENT ME! + * @return The current directory. */ public File getCurrentDirectory() { @@ -403,9 +579,15 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the current directory and fires a {@link PropertyChangeEvent} (with + * the property name {@link #DIRECTORY_CHANGED_PROPERTY}) to all registered + * listeners. If <code>dir</code> is <code>null</code>, the current + * directory is set to the default directory returned by the file system + * view. * - * @param dir DOCUMENT ME! + * @param dir the new directory (<code>null</code> permitted). + * + * @see FileSystemView#getDefaultDirectory() */ public void setCurrentDirectory(File dir) { @@ -421,7 +603,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Called by the UI delegate when the parent directory is changed. */ public void changeToParentDirectory() { @@ -430,7 +612,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Rescans the current directory (this is handled by the UI delegate). */ public void rescanCurrentDirectory() { @@ -438,9 +620,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Ensures the the specified file is visible (this is handled by the + * UI delegate). * - * @param f DOCUMENT ME! + * @param f the file. */ public void ensureFileIsVisible(File f) { @@ -448,11 +631,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #OPEN_DIALOG} type. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @return A return value indicating how the dialog was closed (one of + * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and + * {@link #ERROR_OPTION}). * * @throws HeadlessException DOCUMENT ME! */ @@ -472,11 +658,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #SAVE_DIALOG} type. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @return A return value indicating how the dialog was closed (one of + * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and + * {@link #ERROR_OPTION}). * * @throws HeadlessException DOCUMENT ME! */ @@ -493,12 +682,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #CUSTOM_DIALOG} type. * - * @param parent DOCUMENT ME! - * @param approveButtonText DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @return A return value indicating how the dialog was closed (one of + * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and + * {@link #ERROR_OPTION}). * * @throws HeadlessException DOCUMENT ME! */ @@ -517,11 +708,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Creates a modal dialog in which to display the file chooser. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @return The dialog. * * @throws HeadlessException DOCUMENT ME! */ @@ -542,9 +733,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the flag that controls whether or not the control buttons are + * shown on the file chooser. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setControlButtonsAreShown(boolean) */ public boolean getControlButtonsAreShown() { @@ -552,9 +746,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the flag that controls whether or not the control buttons are + * shown and, if it changes, sends a {@link PropertyChangeEvent} (with the + * property name {@link #CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY}) to + * all registered listeners. * - * @param b DOCUMENT ME! + * @param b the new value for the flag. */ public void setControlButtonsAreShown(boolean b) { @@ -567,9 +764,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the type of file chooser. * - * @return DOCUMENT ME! + * @return {@link #OPEN_DIALOG}, {@link #SAVE_DIALOG} or + * {@link #CUSTOM_DIALOG}. + * + * @see #setDialogType(int) */ public int getDialogType() { @@ -577,9 +777,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the dialog type and fires a {@link PropertyChangeEvent} (with the + * property name {@link #DIALOG_TYPE_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param dialogType DOCUMENT ME! + * @param dialogType the dialog type (one of: {@link #OPEN_DIALOG}, + * {@link #SAVE_DIALOG}, {@link #CUSTOM_DIALOG}). + * + * @throws IllegalArgumentException if <code>dialogType</code> is not valid. */ public void setDialogType(int dialogType) { @@ -596,9 +801,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the dialog title and sends a {@link PropertyChangeEvent} (with the + * property name {@link #DIALOG_TITLE_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param dialogTitle DOCUMENT ME! + * @param dialogTitle the dialog title (<code>null</code> permitted). + * + * @see #getDialogTitle() */ public void setDialogTitle(String dialogTitle) { @@ -611,9 +820,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the dialog title. * - * @return DOCUMENT ME! + * @return The dialog title (possibly <code>null</code>). + * + * @see #setDialogTitle(String) */ public String getDialogTitle() { @@ -621,9 +832,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the tool tip text for the approve button and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param toolTipText DOCUMENT ME! + * @param toolTipText the text. */ public void setApproveButtonToolTipText(String toolTipText) { @@ -637,9 +851,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the tool tip text for the approve button. * - * @return DOCUMENT ME! + * @return The tool tip text for the approve button. + * + * @see #setApproveButtonToolTipText(String) */ public String getApproveButtonToolTipText() { @@ -647,9 +863,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the approve button mnemonic, or zero if no mnemonic has been set. * - * @return DOCUMENT ME! + * @return The approve button mnemonic. + * + * @see #setApproveButtonMnemonic(int) */ public int getApproveButtonMnemonic() { @@ -657,9 +875,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the mnemonic for the approve button and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param mnemonic DOCUMENT ME! + * @param mnemonic the mnemonic. + * + * @see #setApproveButtonMnemonic(char) */ public void setApproveButtonMnemonic(int mnemonic) { @@ -673,9 +896,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the mnemonic for the approve button and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param mnemonic DOCUMENT ME! + * @param mnemonic the mnemonic. + * + * @see #setApproveButtonMnemonic(int) */ public void setApproveButtonMnemonic(char mnemonic) { @@ -683,9 +911,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the approve button text and fires a {@link PropertyChangeEvent} + * (with the property name {@link #APPROVE_BUTTON_TEXT_CHANGED_PROPERTY}) to + * all registered listeners. * - * @param approveButtonText DOCUMENT ME! + * @param approveButtonText the text (<code>null</code> permitted). + * + * @see #getApproveButtonText() */ public void setApproveButtonText(String approveButtonText) { @@ -699,9 +931,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the approve button text. * - * @return DOCUMENT ME! + * @return The approve button text (possibly <code>null</code>). + * + * @see #setApproveButtonText(String) */ public String getApproveButtonText() { @@ -709,19 +943,22 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the available file filters for this file chooser. * - * @return DOCUMENT ME! + * @return The available file filters. */ public FileFilter[] getChoosableFileFilters() { - return (FileFilter[]) choosableFilters.toArray(new FileFilter[0]); + return (FileFilter[]) choosableFilters.toArray(new FileFilter[choosableFilters.size()]); } /** - * DOCUMENT ME! + * Adds a file filter to the list of available filters and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param filter DOCUMENT ME! + * @param filter the filter. */ public void addChoosableFileFilter(FileFilter filter) { @@ -732,11 +969,15 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Removes a file filter from the list of available filters and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param f DOCUMENT ME! + * @param f the file filter. * - * @return DOCUMENT ME! + * @return <code>true</code> if the filter was removed and + * <code>false</code> otherwise. */ public boolean removeChoosableFileFilter(FileFilter f) { @@ -749,7 +990,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Clears the list of choosable file filters and installs the 'accept all' + * filter from the UI delegate. */ public void resetChoosableFileFilters() { @@ -759,9 +1001,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the 'accept all' file filter from the UI delegate. * - * @return DOCUMENT ME! + * @return The 'accept all' file filter. */ public FileFilter getAcceptAllFileFilter() { @@ -769,9 +1011,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the flag that controls whether or not the 'accept all' file + * filter is included in the list of filters. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setAcceptAllFileFilterUsed(boolean) */ public boolean isAcceptAllFileFilterUsed() { @@ -779,9 +1024,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the flag that controls whether or not the 'accept all' file filter + * is included in the list of filters, and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param b DOCUMENT ME! + * @param b the new value of the flag. */ public void setAcceptAllFileFilterUsed(boolean b) { @@ -794,9 +1043,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the accessory component for the file chooser. The default + * value is <code>null</code>. * - * @return DOCUMENT ME! + * @return The accessory component (possibly <code>null</code>). + * + * @see #setAccessory(JComponent) */ public JComponent getAccessory() { @@ -804,9 +1056,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the accessory component for the file chooser and sends a + * {@link PropertyChangeEvent} to all registered listeners. The property + * name is {@link #ACCESSORY_CHANGED_PROPERTY}. * - * @param newAccessory DOCUMENT ME! + * @param newAccessory the accessory component. */ public void setAccessory(JComponent newAccessory) { @@ -819,9 +1073,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the file selection mode and sends a {@link PropertyChangeEvent} + * to all registered listeners. The property name is + * {@link #FILE_SELECTION_MODE_CHANGED_PROPERTY}. * - * @param mode DOCUMENT ME! + * @param mode the mode ({@link #FILES_ONLY}, {@link #DIRECTORIES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}). + * + * @throws IllegalArgumentException if the mode is invalid. */ public void setFileSelectionMode(int mode) { @@ -838,9 +1097,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the file selection mode, one of: {@link #FILES_ONLY}, + * {@link #DIRECTORIES_ONLY} or {@link #FILES_AND_DIRECTORIES}. The + * default is {@link #FILES_ONLY}. * - * @return DOCUMENT ME! + * @return The file selection mode. + * + * @see #setFileSelectionMode(int) */ public int getFileSelectionMode() { @@ -848,9 +1111,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if file selection is enabled, and + * <code>false</code> otherwise. File selection is enabled when the + * file selection mode is {@link #FILES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. * - * @return DOCUMENT ME! + * @return <code>true</code> if file selection is enabled. + * + * @see #getFileSelectionMode() */ public boolean isFileSelectionEnabled() { @@ -859,9 +1127,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if directory selection is enabled, and + * <code>false</code> otherwise. Directory selection is enabled when the + * file selection mode is {@link #DIRECTORIES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. * - * @return DOCUMENT ME! + * @return <code>true</code> if file selection is enabled. + * + * @see #getFileSelectionMode() */ public boolean isDirectorySelectionEnabled() { @@ -870,9 +1143,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the flag that controls whether multiple selections are allowed in + * this filechooser and sends a {@link PropertyChangeEvent} (with the + * property name {@link #MULTI_SELECTION_ENABLED_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param b DOCUMENT ME! + * @param b the new value of the flag. */ public void setMultiSelectionEnabled(boolean b) { @@ -885,9 +1161,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if multiple selections are allowed within this + * file chooser, and <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setMultiSelectionEnabled(boolean) */ public boolean isMultiSelectionEnabled() { @@ -895,9 +1174,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if hidden files are to be hidden, and + * <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setFileHidingEnabled(boolean) */ public boolean isFileHidingEnabled() { @@ -905,9 +1187,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the flag that controls whether or not hidden files are displayed, + * and sends a {@link PropertyChangeEvent} (with the property name + * {@link #FILE_HIDING_CHANGED_PROPERTY}) to all registered listeners. * - * @param b DOCUMENT ME! + * @param b the new value of the flag. */ public void setFileHidingEnabled(boolean b) { @@ -920,9 +1204,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the file filter and sends a {@link PropertyChangeEvent} (with the + * property name {@link #FILE_FILTER_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param filter DOCUMENT ME! + * @param filter the filter. */ public void setFileFilter(FileFilter filter) { @@ -935,9 +1221,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the file filter. * - * @return DOCUMENT ME! + * @return The file filter. + * + * @see #setFileFilter(FileFilter) */ public FileFilter getFileFilter() { @@ -945,9 +1233,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets a custom {@link FileView} for the file chooser and sends a + * {@link PropertyChangeEvent} to all registered listeners. The property + * name is {@link #FILE_VIEW_CHANGED_PROPERTY}. + * + * @param fileView the file view (<code>null</code> permitted). * - * @param fileView DOCUMENT ME! + * @see #getFileView() */ public void setFileView(FileView fileView) { @@ -960,9 +1252,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the custom {@link FileView} for the file chooser. * - * @return DOCUMENT ME! + * @return The file view (possibly <code>null</code>). */ public FileView getFileView() { @@ -970,71 +1262,83 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the name of the file, generated by the current (or default) + * {@link FileView}. * - * @return DOCUMENT ME! - */ - private FileView getInternalFileView() - { - if (fv == null) - return getUI().getFileView(this); - return fv; - } - - /** - * DOCUMENT ME! - * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file name. */ public String getName(File f) { - return getInternalFileView().getName(f); + String name = null; + if (fv != null) + name = fv.getName(f); + if (name == null) + name = getUI().getFileView(this).getName(f); + return name; } /** - * DOCUMENT ME! + * Returns the description of the file, generated by the current (or default) + * {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file description. */ public String getDescription(File f) { - return getInternalFileView().getDescription(f); + String result = null; + if (fv != null) + result = fv.getDescription(f); + if (result == null) + result = getUI().getFileView(this).getDescription(f); + return result; } /** - * DOCUMENT ME! + * Returns the type description for the file, generated by the current (or + * default) {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file type description. */ public String getTypeDescription(File f) { - return getInternalFileView().getTypeDescription(f); + String result = null; + if (fv != null) + result = getFileView().getTypeDescription(f); + if (result == null) + result = getUI().getFileView(this).getTypeDescription(f); + return result; } /** - * DOCUMENT ME! + * Returns the icon provided by the current (or default) {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return An icon representing the file. */ public Icon getIcon(File f) { - return getInternalFileView().getIcon(f); + Icon result = null; + if (fv != null) + result = fv.getIcon(f); + if (result == null) + result = getUI().getFileView(this).getIcon(f); + return result; } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file or directory. * - * @return DOCUMENT ME! + * @return A boolean. */ public boolean isTraversable(File f) { @@ -1042,11 +1346,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is accepted by the current + * file filter. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return A boolean. */ public boolean accept(File f) { @@ -1056,9 +1361,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the file system view for the file chooser and sends a + * {@link PropertyChangeEvent} to all registered listeners. * - * @param fsv DOCUMENT ME! + * @param fsv the file system view. */ public void setFileSystemView(FileSystemView fsv) { @@ -1071,9 +1377,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the file system view being used by this file chooser. * - * @return DOCUMENT ME! + * @return The file system view. + * + * @see #setFileSystemView(FileSystemView) */ public FileSystemView getFileSystemView() { @@ -1081,7 +1389,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Approves the selection. An {@link ActionEvent} is sent to all registered + * listeners. */ public void approveSelection() { @@ -1090,7 +1399,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Cancels the selection. An {@link ActionEvent} is sent to all registered + * listeners. */ public void cancelSelection() { @@ -1099,9 +1409,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Adds an {@link ActionListener} to the file chooser. * - * @param l DOCUMENT ME! + * @param l the listener. */ public void addActionListener(ActionListener l) { @@ -1109,9 +1419,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Removes an {@link ActionListener} from this file chooser. * - * @param l DOCUMENT ME! + * @param l the listener. */ public void removeActionListener(ActionListener l) { @@ -1126,9 +1436,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the action listeners registered with this file chooser. * - * @return DOCUMENT ME! + * @return An array of listeners. */ public ActionListener[] getActionListeners() { @@ -1136,9 +1446,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sends an @link {ActionEvent} to all registered listeners. * - * @param command DOCUMENT ME! + * @param command the action command. */ protected void fireActionPerformed(String command) { @@ -1151,7 +1461,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Installs the UI delegate for the current look and feel. */ public void updateUI() { @@ -1160,9 +1470,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the UI delegate class identifier. * - * @return DOCUMENT ME! + * @return <code>FileChooserUI</code>. */ public String getUIClassID() { @@ -1170,9 +1480,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the UI delegate for the component. * - * @return DOCUMENT ME! + * @return The UI delegate. */ public FileChooserUI getUI() { @@ -1190,12 +1500,29 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the accessible context. * - * @return DOCUMENT ME! + * @return The accessible context. */ public AccessibleContext getAccessibleContext() { - return null; + return new AccessibleJFileChooser(); + } + + /** + * Accessibility support for JFileChooser + */ + protected class AccessibleJFileChooser + extends JComponent.AccessibleJComponent + { + protected AccessibleJFileChooser() + { + // Nothing to do here. + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FILE_CHOOSER; + } } } diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java index 7081f5980e4..8d4dcb53b3c 100644 --- a/libjava/classpath/javax/swing/JFrame.java +++ b/libjava/classpath/javax/swing/JFrame.java @@ -50,6 +50,7 @@ import java.awt.LayoutManager; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; /** @@ -65,8 +66,31 @@ import javax.accessibility.AccessibleContext; * @author Ronald Veldema (rveldema@cs.vu.nl) */ public class JFrame extends Frame - implements WindowConstants, RootPaneContainer + implements WindowConstants, RootPaneContainer, Accessible { + /** + * Provides accessibility support for <code>JFrame</code>s. + */ + protected class AccessibleJFrame extends Frame.AccessibleAWTFrame + { + /** + * Creates a new instance of <code>AccessibleJFrame</code>. + */ + public AccessibleJFrame() + { + super(); + // Nothing to do here. + } + } + + /** + * A flag for {@link #setDefaultCloseOperation(int)}, indicating that the + * application should be exited, when this <code>JFrame</code> is closed. + * + * @since 1.3 + */ + public static final int EXIT_ON_CLOSE = 3; + private static final long serialVersionUID = -3362141868504252139L; private static boolean defaultLookAndFeelDecorated; private int close_action = HIDE_ON_CLOSE; @@ -78,13 +102,6 @@ public class JFrame extends Frame */ protected boolean rootPaneCheckingEnabled = false; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - public JFrame() { super("JFrame"); @@ -134,7 +151,7 @@ public class JFrame extends Frame enableEvents(AWTEvent.WINDOW_EVENT_MASK); getRootPane(); // will do set/create // We're now done the init stage. - initStageDone = true; + setRootPaneCheckingEnabled(true); } public Dimension getPreferredSize() @@ -156,13 +173,8 @@ public class JFrame extends Frame { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -222,15 +234,10 @@ public class JFrame extends Frame { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp,constraints,index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("rootPaneChecking is enabled - adding components " - + "disallowed."); - getContentPane().add(comp,constraints,index); - } + super.addImpl(comp, constraints, index); } public void remove(Component comp) @@ -275,6 +282,8 @@ public class JFrame extends Frame public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) + accessibleContext = new AccessibleJFrame(); return accessibleContext; } diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index b504aaaa5e3..479294b1377 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/javax/swing/JInternalFrame.java @@ -437,13 +437,6 @@ public class JInternalFrame extends JComponent implements Accessible, */ protected boolean rootPaneCheckingEnabled = false; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - /** Whether the JInternalFrame is resizable. */ protected boolean resizable; @@ -567,7 +560,7 @@ public class JInternalFrame extends JComponent implements Accessible, storedBounds = new Rectangle(); setRootPane(createRootPane()); updateUI(); - initStageDone = true; // Done the init stage, now adds go to content pane. + setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane. } /** @@ -587,15 +580,10 @@ public class JInternalFrame extends JComponent implements Accessible, // If we're in the initialization stage use super.add. Here we add the // rootPane as well as the title bar and other stuff. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp,constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JInternalFrame directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp,constraints, index); } /** @@ -1187,7 +1175,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ protected String paramString() { - return "JInternalFrame"; + return super.paramString(); } /** @@ -1227,8 +1215,7 @@ public class JInternalFrame extends JComponent implements Accessible, public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - invalidate(); - doLayout(); + revalidate(); } /** @@ -1489,13 +1476,8 @@ public class JInternalFrame extends JComponent implements Accessible, { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -1678,7 +1660,12 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setUI(InternalFrameUI ui) { + // We must temporarily go into init mode so that the UI can directly + // manipulate the JInternalFrame. + boolean old = isRootPaneCheckingEnabled(); + setRootPaneCheckingEnabled(false); super.setUI(ui); + setRootPaneCheckingEnabled(old); } /** @@ -1704,7 +1691,13 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void updateUI() { + // We must go into the init stage when updating the UI, so the UI can + // set layout and components directly on the internal frame, not its + // content pane. + boolean old = isRootPaneCheckingEnabled(); + setRootPaneCheckingEnabled(false); setUI((InternalFrameUI) UIManager.getUI(this)); + setRootPaneCheckingEnabled(old); } /** diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java index 2e7ad98ddae..a9adc96b2f4 100644 --- a/libjava/classpath/javax/swing/JLabel.java +++ b/libjava/classpath/javax/swing/JLabel.java @@ -331,9 +331,6 @@ public class JLabel extends JComponent implements Accessible, SwingConstants /** The gap between the icon and the text. */ private transient int iconTextGap = 4; - /** The accessible context for this JLabel. */ - private AccessibleJLabel accessibleContext; - /** * Creates a new vertically centered, horizontally on the leading edge * JLabel object with text and no icon. @@ -403,6 +400,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants this.text = text; this.icon = icon; this.horizontalAlignment = horizontalAlignment; + setAlignmentX(0.0F); updateUI(); } @@ -477,12 +475,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants { if (text != newText) { - String oldText = text; - text = newText; - firePropertyChange("text", oldText, newText); - - if (text != null && text.length() <= displayedMnemonicIndex) - setDisplayedMnemonicIndex(text.length() - 1); + String oldText = text; + text = newText; + firePropertyChange("text", oldText, newText); + + if (text != null && text.length() <= displayedMnemonicIndex) + setDisplayedMnemonicIndex(text.length() - 1); + revalidate(); + repaint(); } } diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index 1ea39dc5007..346570d95b1 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/javax/swing/JLayeredPane.java @@ -38,14 +38,20 @@ exception statement from your version. */ package javax.swing; +import java.awt.Color; import java.awt.Component; import java.awt.Container; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; /** * A container that adds depth to the usual <code>Container</code> semantics. @@ -116,6 +122,30 @@ import javax.accessibility.Accessible; */ public class JLayeredPane extends JComponent implements Accessible { + + /** + * Provides accessibility support for <code>JLayeredPane</code>. + */ + protected class AccessibleJLayeredPane extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJLayeredPane</code>. + */ + public AccessibleJLayeredPane() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of <code>JLayeredPane</code>, + * {@link AccessibleRole#LAYERED_PANE}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LAYERED_PANE; + } + } + private static final long serialVersionUID = 5534920399324590459L; public static final String LAYER_PROPERTY = "layeredContainerLayer"; @@ -131,13 +161,15 @@ public class JLayeredPane extends JComponent implements Accessible TreeMap layers; // Layer Number (Integer) -> Layer Size (Integer) Hashtable componentToLayer; // Component -> Layer Number (Integer) + private transient Rectangle rectCache; + public JLayeredPane() { layers = new TreeMap (); componentToLayer = new Hashtable (); + setLayout(null); } - /** * Looks up the layer a child component is currently assigned to. * @@ -223,29 +255,37 @@ public class JLayeredPane extends JComponent implements Accessible ret[1] = getComponents ().length; Iterator i = layers.entrySet ().iterator (); while (i.hasNext()) - { + { Map.Entry pair = (Map.Entry) i.next(); Integer layerNum = (Integer) pair.getKey (); Integer layerSz = (Integer) pair.getValue (); - if (layerNum.intValue() == layer.intValue()) + int layerInt = layerNum.intValue(); + if (layerInt == layer.intValue()) { ret[0] = ret[1] - layerSz.intValue (); - return ret; + break; + } + // In the following case there exists no layer with the specified + // number, so we return an empty interval here with the index at which + // such a layer would be inserted + else if (layerInt > layer.intValue()) + { + ret[1] = ret[0]; + break; } else { ret[1] -= layerSz.intValue (); } - } - // should have found the layer during iteration - throw new IllegalArgumentException (); + } + return ret; } /** * Increments the recorded size of a given layer. * * @param layer the layer number to increment. - * @see #incrLayer() + * @see #incrLayer */ private void incrLayer(Integer layer) { @@ -259,7 +299,7 @@ public class JLayeredPane extends JComponent implements Accessible * Decrements the recorded size of a given layer. * * @param layer the layer number to decrement. - * @see #decrLayer() + * @see #incrLayer */ private void decrLayer(Integer layer) { @@ -546,26 +586,15 @@ public class JLayeredPane extends JComponent implements Accessible * * @param index the index of the child component to remove. */ - public void remove (int index) + public void remove(int index) { - Component c = getComponent (index); - int layer = getLayer (c); - decrLayer (new Integer(layer)); - componentToLayer.remove (c); - super.remove (index); + Component c = getComponent(index); + int layer = getLayer(c); + decrLayer(new Integer(layer)); + componentToLayer.remove(c); + super.remove(index); + // FIXME: Figure out if this call is correct. revalidate(); - repaint(); - } - - /** - * Removes a child from this container. The child is specified directly. - * After removal, the child no longer occupies a layer. - * - * @param comp the child to remove. - */ - public void remove (Component comp) - { - remove (getIndexOf (comp)); } /** @@ -613,7 +642,7 @@ public class JLayeredPane extends JComponent implements Accessible * @param index an ignored parameter, for compatibility. */ protected void addImpl(Component comp, Object layerConstraint, int index) - { + { Integer layer; if (layerConstraint != null && layerConstraint instanceof Integer) layer = (Integer) layerConstraint; @@ -627,10 +656,8 @@ public class JLayeredPane extends JComponent implements Accessible componentToLayer.put (comp, layer); incrLayer (layer); - super.addImpl(comp, null, newIdx); - revalidate(); - repaint(); - } + super.addImpl(comp, null, newIdx); + } /** * Sets the layer property for a JComponent. @@ -642,4 +669,50 @@ public class JLayeredPane extends JComponent implements Accessible { getLayeredPaneAbove(component).setLayer(component, layer); } + + /** + * Returns the accessible context for this <code>JLayeredPane</code>. + * + * @return the accessible context for this <code>JLayeredPane</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJLayeredPane(); + return accessibleContext; + } + + /** + * This method is overridden order to provide a reasonable painting + * mechanism for <code>JLayeredPane</code>. This is necessary since + * <code>JLayeredPane</code>'s do not have an own UI delegate. + * + * Basically this method clears the background for the + * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>. + * + * @param g the graphics context to use + */ + public void paint(Graphics g) + { + if (isOpaque()) + { + Color oldColor = g.getColor(); + Rectangle clip = g.getClipBounds(); + g.setColor(getBackground()); + g.fillRect(clip.x, clip.y, clip.width, clip.height); + g.setColor(oldColor); + } + super.paint(g); + } + + /** + * Overridden to return <code>false</code>, since <code>JLayeredPane</code> + * cannot guarantee that its children don't overlap. + * + * @return <code>false</code> + */ + public boolean isOptimizedDrawingEnabled() + { + return false; + } } diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java index 92fe1ccfa67..4f5d3cc72c5 100644 --- a/libjava/classpath/javax/swing/JList.java +++ b/libjava/classpath/javax/swing/JList.java @@ -41,13 +41,25 @@ package javax.swing; import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; +import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Locale; import java.util.Vector; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; @@ -108,6 +120,736 @@ import javax.swing.text.Position; public class JList extends JComponent implements Accessible, Scrollable { + + /** + * Provides accessibility support for <code>JList</code>. + */ + protected class AccessibleJList extends AccessibleJComponent + implements AccessibleSelection, PropertyChangeListener, + ListSelectionListener, ListDataListener + { + + /** + * Provides accessibility support for list elements in <code>JList</code>s. + */ + protected class AccessibleJListChild extends AccessibleContext + implements Accessible, AccessibleComponent + { + + /** + * The parent list. + */ + JList parent; + + /** + * The index in the list for that child. + */ + int listIndex; + + /** + * The cursor for this list child. + */ + // TODO: Testcases show that this class somehow stores state about the + // cursor. I cannot make up though how that could affect + // the actual list. + Cursor cursor = Cursor.getDefaultCursor(); + + /** + * Creates a new instance of <code>AccessibleJListChild</code>. + * + * @param list the list of which this is an accessible child + * @param index the list index for this child + */ + public AccessibleJListChild(JList list, int index) + { + parent = list; + listIndex = index; + } + + /** + * Returns the accessible context of this object. Returns + * <code>this</code> since <code>AccessibleJListChild</code>s are their + * own accessible contexts. + * + * @return the accessible context of this object, <code>this</code> + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color for this list child. This returns the + * background of the <code>JList</code> itself since the background + * cannot be set on list children individually + * + * @return the background color for this list child + */ + public Color getBackground() + { + return parent.getBackground(); + } + + /** + * Calling this method has no effect, since the background color cannot be + * set on list children individually. + * + * @param color not used here. + */ + public void setBackground(Color color) + { + // Calling this method has no effect, since the background color cannot + // be set on list children individually. + } + + /** + * Returns the foreground color for this list child. This returns the + * background of the <code>JList</code> itself since the foreground + * cannot be set on list children individually. + * + * @return the background color for this list child + */ + public Color getForeground() + { + return parent.getForeground(); + } + + /** + * Calling this method has no effect, since the foreground color cannot be + * set on list children individually. + * + * @param color not used here. + */ + public void setForeground(Color color) + { + // Calling this method has no effect, since the foreground color cannot + // be set on list children individually. + } + + /** + * Returns the cursor for this list child. + * + * @return the cursor for this list child + */ + public Cursor getCursor() + { + // TODO: Testcases show that this method returns the cursor that has + // been set by setCursor. I cannot make up though how that could affect + // the actual list. + return cursor; + } + + /** + * Sets the cursor for this list child. + */ + public void setCursor(Cursor cursor) + { + this.cursor = cursor; + // TODO: Testcases show that this method returns the cursor that has + // been set by setCursor. I cannot make up though how that could affect + // the actual list. + } + + /** + * Returns the font of the <code>JList</code> since it is not possible to + * set fonts for list children individually. + * + * @return the font of the <code>JList</code> + */ + public Font getFont() + { + return parent.getFont(); + } + + /** + * Does nothing since it is not possible to set the font on list children + * individually. + * + * @param font not used here + */ + public void setFont(Font font) + { + // Does nothing since it is not possible to set the font on list + // children individually. + } + + /** + * Returns the font metrics for the specified font. This method forwards + * to the parent <code>JList</code>. + * + * @param font the font for which the font metrics is queried + * + * @return the font metrics for the specified font + */ + public FontMetrics getFontMetrics(Font font) + { + return parent.getFontMetrics(font); + } + + /** + * Returns <code>true</code> if the parent <code>JList</code> is enabled, + * <code>false</code> otherwise. The list children cannot have an enabled + * flag set individually. + * + * @return <code>true</code> if the parent <code>JList</code> is enabled, + * <code>false</code> otherwise + */ + public boolean isEnabled() + { + return parent.isEnabled(); + } + + /** + * Does nothing since the enabled flag cannot be set for list children + * individually. + * + * @param b not used here + */ + public void setEnabled(boolean b) + { + // Does nothing since the enabled flag cannot be set for list children + // individually. + } + + /** + * Returns <code>true</code> if this list child is visible, + * <code>false</code> otherwise. The value of this property depends + * on {@link JList#getFirstVisibleIndex()} and + * {@link JList#getLastVisibleIndex()}. + * + * @return <code>true</code> if this list child is visible, + * <code>false</code> otherwise + */ + public boolean isVisible() + { + return listIndex >= parent.getFirstVisibleIndex() + && listIndex <= parent.getLastVisibleIndex(); + } + + /** + * The value of the visible property cannot be modified, so this method + * does nothing. + * + * @param b not used here + */ + public void setVisible(boolean b) + { + // The value of the visible property cannot be modified, so this method + // does nothing. + } + + /** + * Returns <code>true</code> if this list child is currently showing on + * screen and <code>false</code> otherwise. The list child is showing if + * it is visible and if it's parent JList is currently showing. + * + * @return <code>true</code> if this list child is currently showing on + * screen and <code>false</code> otherwise + */ + public boolean isShowing() + { + return isVisible() && parent.isShowing(); + } + + /** + * Returns <code>true</code> if this list child covers the screen location + * <code>point</code> (relative to the <code>JList</code> coordinate + * system, <code>false</code> otherwise. + * + * @return <code>true</code> if this list child covers the screen location + * <code>point</code> , <code>false</code> otherwise + */ + public boolean contains(Point point) + { + return getBounds().contains(point); + } + + /** + * Returns the absolute screen location of this list child. + * + * @return the absolute screen location of this list child + */ + public Point getLocationOnScreen() + { + Point loc = getLocation(); + SwingUtilities.convertPointToScreen(loc, parent); + return loc; + } + + /** + * Returns the screen location of this list child relative to it's parent. + * + * @return the location of this list child relative to it's parent + * + * @see JList#indexToLocation(int) + */ + public Point getLocation() + { + return parent.indexToLocation(listIndex); + } + + /** + * Does nothing since the screen location cannot be set on list children + * explictitly. + * + * @param point not used here + */ + public void setLocation(Point point) + { + // Does nothing since the screen location cannot be set on list children + // explictitly. + } + + /** + * Returns the bounds of this list child. + * + * @return the bounds of this list child + * + * @see JList#getCellBounds(int, int) + */ + public Rectangle getBounds() + { + return parent.getCellBounds(listIndex, listIndex); + } + + /** + * Does nothing since the bounds cannot be set on list children + * individually. + * + * @param rectangle not used here + */ + public void setBounds(Rectangle rectangle) + { + // Does nothing since the bounds cannot be set on list children + // individually. + } + + /** + * Returns the size of this list child. + * + * @return the size of this list child + */ + public Dimension getSize() + { + Rectangle b = getBounds(); + return b.getSize(); + } + + /** + * Does nothing since the size cannot be set on list children + * individually. + * + * @param dimension not used here + */ + public void setSize(Dimension dimension) + { + // Does nothing since the size cannot be set on list children + // individually. + } + + /** + * Returns <code>null</code> because list children do not have children + * themselves + * + * @return <code>null</code> + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns <code>true</code> since list children are focus traversable. + * + * @return true + */ + public boolean isFocusTraversable() + { + // TODO: Is this 100% ok? + return true; + } + + /** + * Requests focus on the parent list. List children cannot request focus + * individually. + */ + public void requestFocus() + { + // TODO: Is this 100% ok? + parent.requestFocus(); + } + + /** + * Adds a focus listener to the parent list. List children do not have + * their own focus management. + * + * @param listener the focus listener to add + */ + public void addFocusListener(FocusListener listener) + { + // TODO: Is this 100% ok? + parent.addFocusListener(listener); + } + + /** + * Removes a focus listener from the parent list. List children do not + * have their own focus management. + * + * @param listener the focus listener to remove + */ + public void removeFocusListener(FocusListener listener) + { + // TODO: Is this 100% + parent.removeFocusListener(listener); + } + + /** + * Returns the accessible role of this list item, which is + * {@link AccessibleRole#LABEL}. + * + * @return {@link AccessibleRole#LABEL} + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LABEL; + } + + /** + * Returns the accessible state set of this list item. + * + * @return the accessible state set of this list item + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = new AccessibleStateSet(); + if (isVisible()) + states.add(AccessibleState.VISIBLE); + if (isShowing()) + states.add(AccessibleState.SHOWING); + if (isFocusTraversable()) + states.add(AccessibleState.FOCUSABLE); + // TODO: How should the active state be handled? The API docs + // suggest that this state is set on the activated list child, + // that is the one that is drawn with a box. However, I don't know how + // to implement this. + + // TODO: We set the selectable state here because list children are + // selectable. Is there a way to disable single children? + if (parent.isEnabled()) + states.add(AccessibleState.SELECTABLE); + + if (parent.isSelectedIndex(listIndex)) + states.add(AccessibleState.SELECTED); + + // TODO: Handle more states here? + return states; + } + + /** + * Returns the index of this list child within it's parent list. + * + * @return the index of this list child within it's parent list + */ + public int getAccessibleIndexInParent() + { + return listIndex; + } + + /** + * Returns <code>0</code> since list children don't have children + * themselves. + * + * @return <code>0</code> + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns <code>null</code> since list children don't have children + * themselves. + * + * @return <code>null</code> + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale of this component. This call is forwarded to the + * parent list since list children don't have a separate locale setting. + * + * @return the locale of this component + */ + public Locale getLocale() + { + return parent.getLocale(); + } + + /** + * This method does + * nothing, list children are transient accessible objects which means + * that they don't fire property change events. + * + * @param l not used here + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + // Do nothing here. + } + + /** + * This method does + * nothing, list children are transient accessible objects which means + * that they don't fire property change events. + * + * @param l not used here + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + // Do nothing here. + } + + // TODO: Implement the remaining methods of this class. + } + + /** + * Create a new AccessibleJList. + */ + public AccessibleJList() + { + // Nothing to do here. + } + + /** + * Returns the number of selected accessible children. + * + * @return the number of selected accessible children + */ + public int getAccessibleSelectionCount() + { + return getSelectedIndices().length; + } + + /** + * Returns the n-th selected accessible child. + * + * @param n the index of the selected child to return + * + * @return the n-th selected accessible child + */ + public Accessible getAccessibleSelection(int n) + { + return new AccessibleJListChild(JList.this, getSelectedIndices()[n]); + } + + /** + * Returns <code>true</code> if the n-th child is selected, + * <code>false</code> otherwise. + * + * @param n the index of the child of which the selected state is queried + * + * @return <code>true</code> if the n-th child is selected, + * <code>false</code> otherwise + */ + public boolean isAccessibleChildSelected(int n) + { + return isSelectedIndex(n); + } + + /** + * Adds the accessible item with the specified index to the selected items. + * If multiple selections are supported, the item is added to the selection, + * otherwise the item replaces the current selection. + * + * @param i the index of the item to add to the selection + */ + public void addAccessibleSelection(int i) + { + addSelectionInterval(i, i); + } + + /** + * Removes the accessible item with the specified index to the selection. + * + * @param i the index of the item to be removed from the selection + */ + public void removeAccessibleSelection(int i) + { + removeSelectionInterval(i, i); + } + + /** + * Remove all selection items from the selection. + */ + public void clearAccessibleSelection() + { + clearSelection(); + } + + /** + * Selects all items if multiple selections are supported. + * Otherwise do nothing. + */ + public void selectAllAccessibleSelection() + { + addSelectionInterval(0, getModel().getSize()); + } + + /** + * Receices notification when the list selection is changed. This method + * fires two property change events, the first with + * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second + * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}. + * + * @param event the list selection event + */ + public void valueChanged(ListSelectionEvent event) + { + firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, + Boolean.TRUE); + firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE, + Boolean.TRUE); + } + + /** + * Receives notification when items have changed in the + * <code>JList</code>. This method fires a property change event with + * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. + * + * @param event the list data event + */ + public void contentsChanged(ListDataEvent event) + { + firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, + Boolean.TRUE); + } + + /** + * Receives notification when items are inserted into the + * <code>JList</code>. This method fires a property change event with + * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. + * + * @param event the list data event + */ + public void intervalAdded(ListDataEvent event) + { + firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, + Boolean.TRUE); + } + + /** + * Receives notification when items are removed from the + * <code>JList</code>. This method fires a property change event with + * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. + * + * @param event the list data event + */ + public void intervalRemoved(ListDataEvent event) + { + firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, + Boolean.TRUE); + } + + + /** + * Receives notification about changes of the <code>JList</code>'s + * properties. This is used to re-register this object as listener to + * the data model and selection model when the data model or selection model + * changes. + * + * @param e the property change event + */ + public void propertyChange(PropertyChangeEvent e) + { + String propertyName = e.getPropertyName(); + if (propertyName.equals("model")) + { + ListModel oldModel = (ListModel) e.getOldValue(); + oldModel.removeListDataListener(this); + ListModel newModel = (ListModel) e.getNewValue(); + newModel.addListDataListener(this); + } + else if (propertyName.equals("selectionModel")) + { + ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); + oldModel.removeListSelectionListener(this); + ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); + oldModel.addListSelectionListener(this); + } + } + + /** + * Return the state set of the <code>JList</code>. + * + * @return the state set of the <code>JList</code> + */ + public AccessibleStateSet getAccessibleStateSet() + { + // TODO: Figure out if there is possibly more state that must be + // handled here. + AccessibleStateSet s = super.getAccessibleStateSet(); + if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) + s.add(AccessibleState.MULTISELECTABLE); + return s; + } + + /** + * Returns the accessible role for <code>JList</code>, + * {@link AccessibleRole#LIST}. + * + * @return the accessible role for <code>JList</code> + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + /** + * Returns the accessible child at the visual location <code>p</code> + * (relative to the upper left corner of the <code>JList</code>). If there + * is no child at that location, this returns <code>null</code>. + * + * @param p the screen location for which to return the accessible child + * + * @return the accessible child at the specified location, or + * <code>null</code> if there is no child at that location + */ + public Accessible getAccessibleAt(Point p) + { + int childIndex = locationToIndex(p); + return getAccessibleChild(childIndex); + } + + /** + * Returns the number of accessible children in the <code>JList</code>. + * + * @return the number of accessible children in the <code>JList</code> + */ + public int getAccessibleChildrenCount() + { + return getModel().getSize(); + } + + /** + * Returns the n-th accessible child of this <code>JList</code>. This will + * be an instance of {@link AccessibleJListChild}. If there is no child + * at that index, <code>null</code> is returned. + * + * @param n the index of the child to return + * + * @return the n-th accessible child of this <code>JList</code> + */ + public Accessible getAccessibleChild(int n) + { + if (getModel().getSize() <= n) + return null; + return new AccessibleJListChild(JList.this, n); + } + } + private static final long serialVersionUID = 4406629526391098046L; /** @@ -181,7 +923,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property specifies a foreground color for the selected cells in - * the list. When {@link ListCellRenderer.getListCellRendererComponent} + * the list. When {@link ListCellRenderer#getListCellRendererComponent} * is called with a selected cell object, the component returned will * have its "foreground" set to this color. */ @@ -189,7 +931,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property specifies a background color for the selected cells in - * the list. When {@link ListCellRenderer.getListCellRendererComponent} + * the list. When {@link ListCellRenderer#getListCellRendererComponent} * is called with a selected cell object, the component returned will * have its "background" property set to this color. */ @@ -216,9 +958,9 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property indicates a <em>preference</em> for the number of rows * displayed in the list, and will scale the - * {@link #preferredScrollableViewportSize} property accordingly. The actual + * {@link #getPreferredScrollableViewportSize} property accordingly. The actual * number of displayed rows, when the list is placed in a real {@link - * Viewport} or other component, may be greater or less than this number. + * JViewport} or other component, may be greater or less than this number. */ int visibleRowCount; @@ -270,7 +1012,7 @@ public class JList extends JComponent implements Accessible, Scrollable event.getValueIsAdjusting()); JList.this.repaint(); } - }; + } /** * Shared ListListener instance, subscribed to both the current {@link @@ -437,7 +1179,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * Sets the value of the {@link #visibleRowCount} property. * - * @param visibleRowCount The new property value + * @param vc The new property value */ public void setVisibleRowCount(int vc) { @@ -563,8 +1305,8 @@ public class JList extends JComponent implements Accessible, Scrollable /** * Returns the list index of the upper left or upper right corner of the - * {@link #visibleRect} property, depending on the {@link - * #componentOrientation} property. + * visible rectangle of this list, depending on the {@link + * Component#getComponentOrientation} property. * * @return The index of the first visible list cell, or <code>-1</code> * if none is visible. @@ -585,7 +1327,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return index of the cell to which specified location is closest to. */ - public int locationToIndex(Point location) { + public int locationToIndex(Point location) + { return getUI().locationToIndex(this, location); } @@ -595,14 +1338,15 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return location of the cell located at the specified index in the list. */ - public Point indexToLocation(int index){ - return getCellBounds(index, index).getLocation(); + public Point indexToLocation(int index) + { + return getUI().indexToLocation(this, index); } /** * Returns the list index of the lower right or lower left corner of the - * {@link #visibleRect} property, depending on the {@link - * #componentOrientation} property. + * visible rectangle of this list, depending on the {@link + * Component#getComponentOrientation} property. * * @return The index of the last visible list cell, or <code>-1</code> * if none is visible. @@ -625,7 +1369,7 @@ public class JList extends JComponent implements Accessible, Scrollable * selected. * * @return An array of model indices, each of which is selected according - * to the {@link #selection} property + * to the {@link #getSelectedValues} property */ public int[] getSelectedIndices() { @@ -640,7 +1384,7 @@ public class JList extends JComponent implements Accessible, Scrollable n++; int [] v = new int[n]; j = 0; - for (i = lo; i < hi; ++i) + for (i = lo; i <= hi; ++i) if (selectionModel.isSelectedIndex(i)) v[j++] = i; return v; @@ -670,7 +1414,7 @@ public class JList extends JComponent implements Accessible, Scrollable * @return The first selected element, or <code>null</code> if no element * is selected. * - * @see getSelectedValues + * @see #getSelectedValues */ public Object getSelectedValue() { @@ -686,7 +1430,7 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return An array containing all the selected values * - * @see getSelectedValue + * @see #setSelectedValue */ public Object[] getSelectedValues() { @@ -845,7 +1589,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Sets the value of the {@link #celLRenderer} property. + * Sets the value of the {@link #getCellRenderer} property. * * @param renderer The new property value */ @@ -876,10 +1620,15 @@ public class JList extends JComponent implements Accessible, Scrollable * #listListener} is unsubscribed from the existing model, if it exists, * and re-subscribed to the new model. * - * @param model The new property value + * @param model the new model (<code>null</code> not permitted). + * + * @throws IllegalArgumentException if <code>model</code> is + * <code>null</code>. */ public void setModel(ListModel model) { + if (model == null) + throw new IllegalArgumentException("Null 'model' argument."); if (this.model == model) return; @@ -1019,14 +1768,14 @@ public class JList extends JComponent implements Accessible, Scrollable public AccessibleContext getAccessibleContext() { - return null; + return new AccessibleJList(); } /** * Returns a size indicating how much space this list would like to * consume, when contained in a scrollable viewport. This is part of the * {@link Scrollable} interface, which interacts with {@link - * ScrollPaneLayout} and {@link Viewport} to define scrollable objects. + * ScrollPaneLayout} and {@link JViewport} to define scrollable objects. * * @return The preferred size */ @@ -1036,36 +1785,43 @@ public class JList extends JComponent implements Accessible, Scrollable //return the value from getPreferredSize. The current ListUI is //expected to override getPreferredSize to return an appropriate value. if (getLayoutOrientation() != VERTICAL) - return getPreferredSize(); + return getPreferredSize(); + + int size = getModel().getSize(); + // Trivial case: if fixedCellWidth and fixedCellHeight were set + // just use them if (fixedCellHeight != -1 && fixedCellWidth != -1) - return new Dimension(fixedCellWidth, getModel().getSize() * - fixedCellHeight); + return new Dimension(fixedCellWidth, size * fixedCellHeight); + + // If the model is empty we use 16 * the number of visible rows + // for the height and either fixedCellWidth (if set) or 256 + // for the width + if (size == 0) + { + if (fixedCellWidth == -1) + return new Dimension(256, 16 * getVisibleRowCount()); + else + return new Dimension(fixedCellWidth, 16 * getVisibleRowCount()); + } - int prefWidth, prefHeight; + // Calculate the width: if fixedCellWidth was set use that, otherwise + // use the preferredWidth + int prefWidth; if (fixedCellWidth != -1) prefWidth = fixedCellWidth; else - { - prefWidth = 0; - int size = getModel().getSize(); - for (int i = 0; i < size; i++) - if (getCellBounds(i, i).width > prefWidth) - prefWidth = getCellBounds(i, i).width; - } - - if (getModel().getSize() == 0 && fixedCellWidth == -1) - return new Dimension(256, 16 * getVisibleRowCount()); - else if (getModel().getSize() == 0) - return new Dimension (fixedCellWidth, 16 * getVisibleRowCount()); - + prefWidth = getPreferredSize().width; + + // Calculate the height: if fixedCellHeight was set use that, otherwise + // use the height of the first row multiplied by the number of visible + // rows + int prefHeight; if (fixedCellHeight != -1) prefHeight = fixedCellHeight; else - { - prefHeight = getVisibleRowCount() * getCellBounds - (getFirstVisibleIndex(), getFirstVisibleIndex()).height; - } + prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height; + return new Dimension (prefWidth, prefHeight); } @@ -1196,7 +1952,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #scrollableTracksViewportWidth} property. + * Gets the value of the <code>scrollableTracksViewportWidth</code> property. * * @return <code>true</code> if the viewport is larger (horizontally) * than the list and the list should be expanded to fit the viewport; @@ -1221,7 +1977,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #scrollableTracksViewportWidth} property. + * Gets the value of the </code>scrollableTracksViewportWidth</code> property. * * @return <code>true</code> if the viewport is larger (vertically) * than the list and the list should be expanded to fit the viewport; @@ -1373,7 +2129,7 @@ public class JList extends JComponent implements Accessible, Scrollable */ public Rectangle getCellBounds(int index0, int index1) { - return ((ListUI) ui).getCellBounds(this, index0, index1); + return getUI().getCellBounds(this, index0, index1); } /** @@ -1383,8 +2139,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @param prefix the prefix to search for in the cell values * @param startIndex the index where to start searching from - * @param bias the search direction, either {@link Position.Bias.Forward} - * or {@link Position.Bias.Backward} + * @param bias the search direction, either {@link Position.Bias#Forward} + * or {@link Position.Bias#Backward} * * @return the index of the found element or -1 if no such element has * been found diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java index 8dcad8b77cd..9734eb8732f 100644 --- a/libjava/classpath/javax/swing/JMenu.java +++ b/libjava/classpath/javax/swing/JMenu.java @@ -45,8 +45,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; @@ -137,10 +135,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement this(text); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Adds specified menu item to this menu * @@ -768,6 +762,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ protected void processKeyEvent(KeyEvent event) { + // TODO: Implement this properly. } /** @@ -812,6 +807,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement return accessibleContext; } + // FIXME: This inner class is a complete stub and needs to be implemented. protected class AccessibleJMenu extends AccessibleJMenuItem implements AccessibleSelection { @@ -819,6 +815,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement protected AccessibleJMenu() { + // Nothing to do here. } public int getAccessibleChildrenCount() @@ -858,32 +855,48 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public void addAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void removeAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void clearAccessibleSelection() { + // TODO: Implement this properly. } public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } protected class WinListener extends WindowAdapter implements Serializable { - JPopupMenu popupMenu; private static final long serialVersionUID = -6415815570638474823L; + /** + * Creates a new <code>WinListener</code>. + * + * @param popup the popup menu which is observed + */ public WinListener(JPopupMenu popup) { + // TODO: What should we do with the popup argument? } + /** + * Receives notification when the popup menu is closing and deselects + * the menu. + * + * @param event the window event + */ public void windowClosing(WindowEvent event) { + setSelected(false); } } diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java index eebb1a050be..f018daabf80 100644 --- a/libjava/classpath/javax/swing/JMenuBar.java +++ b/libjava/classpath/javax/swing/JMenuBar.java @@ -46,6 +46,9 @@ import java.awt.event.MouseEvent; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; import javax.swing.plaf.MenuBarUI; /** @@ -59,6 +62,137 @@ import javax.swing.plaf.MenuBarUI; */ public class JMenuBar extends JComponent implements Accessible, MenuElement { + /** + * Provides accessibility support for <code>JMenuBar</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJMenuBar extends AccessibleJComponent + implements AccessibleSelection + { + + /** + * Returns the number of selected items in the menu bar. Possible values + * are <code>0</code> if nothing is selected, or <code>1</code> if one + * item is selected. + * + * @return the number of selected items in the menu bar + */ + public int getAccessibleSelectionCount() + { + int count = 0; + if (getSelectionModel().getSelectedIndex() != -1) + count = 1; + return count; + } + + /** + * Returns the selected with index <code>i</code> menu, or + * <code>null</code> if the specified menu is not selected. + * + * @param i the index of the menu to return + * + * @return the selected with index <code>i</code> menu, or + * <code>null</code> if the specified menu is not selected + */ + public Accessible getAccessibleSelection(int i) + { + if (getSelectionModel().getSelectedIndex() != i) + return null; + return getMenu(i); + } + + /** + * Returns <code>true</code> if the specified menu is selected, + * <code>false</code> otherwise. + * + * @param i the index of the menu to check + * + *@return <code>true</code> if the specified menu is selected, + * <code>false</code> otherwise + */ + public boolean isAccessibleChildSelected(int i) + { + return getSelectionModel().getSelectedIndex() == i; + } + + /** + * Selects the menu with index <code>i</code>. If another menu is already + * selected, this will be deselected. + * + * @param i the menu to be selected + */ + public void addAccessibleSelection(int i) + { + getSelectionModel().setSelectedIndex(i); + } + + /** + * Deselects the menu with index <code>i</code>. + * + * @param i the menu index to be deselected + */ + public void removeAccessibleSelection(int i) + { + if (getSelectionModel().getSelectedIndex() == i) + getSelectionModel().clearSelection(); + } + + /** + * Deselects all possibly selected menus. + */ + public void clearAccessibleSelection() + { + getSelectionModel().clearSelection(); + } + + /** + * In menu bars it is not possible to select all items, so this method + * does nothing. + */ + public void selectAllAccessibleSelection() + { + // In menu bars it is not possible to select all items, so this method + // does nothing. + } + + /** + * Returns the accessible role of <code>JMenuBar</code>, which is + * {@link AccessibleRole#MENU_BAR}. + * + * @return the accessible role of <code>JMenuBar</code>, which is + * {@link AccessibleRole#MENU_BAR} + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_BAR; + } + + /** + * Returns the <code>AccessibleSelection</code> for this object. This + * method returns <code>this</code>, since the + * <code>AccessibleJMenuBar</code> manages its selection itself. + * + * @return the <code>AccessibleSelection</code> for this object + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Returns the state of this <code>AccessibleJMenuBar</code>. + * + * @return the state of this <code>AccessibleJMenuBar</code>. + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet stateSet = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added to the super state set. + return stateSet; + } + } + private static final long serialVersionUID = -8191026883931977036L; /** JMenuBar's model. It keeps track of selected menu's index */ @@ -100,13 +234,15 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement */ public void addNotify() { - // FIXME: Should register this menu bar with the keyboard manager super.addNotify(); + KeyboardManager.getManager().registerJMenuBar(this); } public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJMenuBar(); + return accessibleContext; } /** @@ -253,7 +389,7 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement * This method returns a name to identify which look and feel class will be * the UI delegate for the menu bar. * - * @return The Look and Feel classID. "MenuItemUI" + * @return The Look and Feel classID. "MenuBarUI" */ public String getUIClassID() { @@ -338,6 +474,63 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement } /** + * This method overrides JComponent.processKeyBinding to allow the + * JMenuBar to check all the child components (recursiveley) to see + * if they'll consume the event. + * + * @param ks the KeyStroke for the event + * @param e the KeyEvent for the event + * @param condition the focus condition for the binding + * @param pressed true if the key is pressed + */ + protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, + boolean pressed) + { + // See if the regular JComponent behavior consumes the event + if (super.processKeyBinding(ks, e, condition, pressed)) + return true; + + // If not, have to recursively check all the child menu elements to see + // if they want it + MenuElement[] children = getSubElements(); + for (int i = 0; i < children.length; i++) + if (processKeyBindingHelper(children[i], ks, e, condition, pressed)) + return true; + return false; + } + + /** + * This is a helper method to recursively check the children of this + * JMenuBar to see if they will consume a key event via key bindings. + * This is used for menu accelerators. + * @param menuElement the menuElement to check (and check all its children) + * @param ks the KeyStroke for the event + * @param e the KeyEvent that may be consumed + * @param condition the focus condition for the binding + * @param pressed true if the key was pressed + * @return true <code>menuElement</code> or one of its children consume + * the event (processKeyBinding returns true for menuElement or one of + * its children). + */ + static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks, + KeyEvent e, int condition, + boolean pressed) + { + // First check the menuElement itself, if it's a JComponent + if (menuElement instanceof JComponent + && ((JComponent) menuElement).processKeyBinding(ks, e, condition, + pressed)) + return true; + + // If that didn't consume it, check all the children recursively + MenuElement[] children = menuElement.getSubElements(); + for (int i = 0; i < children.length; i++) + if (processKeyBindingHelper(children[i], ks, e, condition, pressed)) + return true; + return false; + } + + /** * Process mouse events forwarded from MenuSelectionManager. This method * doesn't do anything. It is here to conform to the MenuElement interface. * @@ -358,7 +551,7 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement */ public void removeNotify() { - // Must unregister this menu bar with the current keyboard manager. + KeyboardManager.getManager().unregisterJMenuBar(this); super.removeNotify(); } @@ -384,9 +577,14 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement * Sets help menu for this menu bar * * @param menu help menu + * + * @specnote The specification states that this method is not yet implemented + * and should throw an exception. */ public void setHelpMenu(JMenu menu) { + // We throw an Error here, just as Sun's JDK does. + throw new Error("setHelpMenu() not yet implemented."); } /** diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java index 069b7bc86f8..c87a4dc2b20 100644 --- a/libjava/classpath/javax/swing/JMenuItem.java +++ b/libjava/classpath/javax/swing/JMenuItem.java @@ -44,9 +44,6 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.EventListener; import javax.accessibility.Accessible; @@ -84,6 +81,7 @@ public class JMenuItem extends AbstractButton implements Accessible, public JMenuItem() { super(); + init(null, null); } /** @@ -118,6 +116,7 @@ public class JMenuItem extends AbstractButton implements Accessible, { super(); super.setAction(action); + init(null, null); } /** @@ -147,15 +146,6 @@ public class JMenuItem extends AbstractButton implements Accessible, setMnemonic(mnemonic); } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { - } - - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Initializes this menu item * @@ -176,7 +166,7 @@ public class JMenuItem extends AbstractButton implements Accessible, //borderPainted = false; focusPainted = false; horizontalAlignment = JButton.LEFT; - horizontalTextPosition = JButton.LEFT; + horizontalTextPosition = JButton.TRAILING; } /** @@ -189,7 +179,7 @@ public class JMenuItem extends AbstractButton implements Accessible, { super.setUI(ui); } - + /** * This method sets this menuItem's UI to the UIManager's default for the * current look and feel. @@ -255,13 +245,18 @@ public class JMenuItem extends AbstractButton implements Accessible, } /** - * Sets accelerator for this menu item. - * + * Sets the key combination which invokes the menu item's action + * listeners without navigating the menu hierarchy. Note that when the + * keyboard accelerator is typed, it will work whether or not the + * menu is currently displayed. + * * @param keystroke accelerator for this menu item. */ public void setAccelerator(KeyStroke keystroke) { + KeyStroke old = this.accelerator; this.accelerator = keystroke; + firePropertyChange ("accelerator", old, keystroke); } /** @@ -276,7 +271,11 @@ public class JMenuItem extends AbstractButton implements Accessible, super.configurePropertiesFromAction(action); if (! (this instanceof JMenu) && action != null) - setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY))); + { + setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY))); + super.registerKeyboardAction(action, accelerator, + JComponent.WHEN_IN_FOCUSED_WINDOW); + } } /** @@ -667,6 +666,7 @@ public class JMenuItem extends AbstractButton implements Accessible, public void stateChanged(ChangeEvent event) { + // TODO: What should be done here, if anything? } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java index ad0772ab8d3..057326cd209 100644 --- a/libjava/classpath/javax/swing/JOptionPane.java +++ b/libjava/classpath/javax/swing/JOptionPane.java @@ -1,5 +1,5 @@ /* JOptionPane.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,7 +39,6 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.awt.Dimension; import java.awt.Frame; import javax.accessibility.Accessible; @@ -60,16 +59,19 @@ public class JOptionPane extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJOptionPane extends JComponent.AccessibleJComponent { /** DOCUMENT ME! */ private static final long serialVersionUID = 686071432213084821L; - + /** * Creates a new AccessibleJOptionPane object. */ protected AccessibleJOptionPane() { + // Nothing to do here. } /** @@ -343,8 +345,6 @@ public class JOptionPane extends JComponent implements Accessible setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); updateUI(); - invalidate(); - repaint(); } /** @@ -369,17 +369,12 @@ public class JOptionPane extends JComponent implements Accessible inputValue = UNINITIALIZED_VALUE; value = UNINITIALIZED_VALUE; - // FIXME: This dialog should be centered on the parent - // or at the center of the screen (if the parent is null) - // Need getGraphicsConfiguration to return non-null in - // order for that to work so we know how large the - // screen is. dialog.getContentPane().add(this); dialog.setModal(true); dialog.setResizable(false); - dialog.invalidate(); - dialog.repaint(); - + dialog.pack(); + dialog.setLocationRelativeTo(parentComponent); + return dialog; } @@ -513,6 +508,8 @@ public class JOptionPane extends JComponent implements Accessible */ public Object getInputValue() { + if (getValue().equals(new Integer(CANCEL_OPTION))) + setInputValue(null); return inputValue; } @@ -858,13 +855,13 @@ public class JOptionPane extends JComponent implements Accessible */ public static int showConfirmDialog(Component parentComponent, Object message) { - JOptionPane pane = new JOptionPane(message); + JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); JDialog dialog = pane.createDialog(parentComponent, "Select an Option"); - - dialog.pack(); dialog.show(); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -886,10 +883,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -912,10 +910,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType, optionType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -940,10 +939,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType, optionType, icon); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -964,9 +964,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -991,9 +990,8 @@ public class JOptionPane extends JComponent implements Accessible pane.setInitialSelectionValue(initialSelectionValue); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1017,9 +1015,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, messageType); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1050,10 +1047,9 @@ public class JOptionPane extends JComponent implements Accessible pane.setSelectionValues(selectionValues); pane.setInitialSelectionValue(initialSelectionValue); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - - return (String) pane.getInputValue(); + + return pane.getInputValue(); } /** @@ -1071,9 +1067,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); pane.setWantsInput(true); JDialog dialog = pane.createDialog(null, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1096,9 +1091,8 @@ public class JOptionPane extends JComponent implements Accessible pane.setWantsInput(true); pane.setInitialSelectionValue(initialSelectionValue); JDialog dialog = pane.createDialog(null, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1120,8 +1114,10 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, null); startModal(frame); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1146,7 +1142,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1172,7 +1170,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1200,7 +1200,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1222,7 +1224,7 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, null); startModal(frame); - + return (String) pane.getInputValue(); } @@ -1248,7 +1250,7 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - + return (String) pane.getInputValue(); } @@ -1283,8 +1285,8 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - - return (String) pane.getInputValue(); + + return pane.getInputValue(); } /** @@ -1376,8 +1378,10 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1391,8 +1395,7 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); - dialog.show(); + dialog.show(); } /** @@ -1410,7 +1413,6 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); } @@ -1431,7 +1433,6 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, messageType); pane.setIcon(icon); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); } @@ -1461,10 +1462,11 @@ public class JOptionPane extends JComponent implements Accessible options, initialValue); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1524,34 +1526,34 @@ public class JOptionPane extends JComponent implements Accessible * JInternalFrame's preferred size. * * @param f The JInternalFrame to make modal. - * @param pane The JOptionPane to add to the JInternalFrame. */ private static void startModal(JInternalFrame f) { synchronized (f) - { - final JInternalFrame tmp = f; - tmp.toFront(); - - f.addInternalFrameListener(new InternalFrameAdapter() - { - public void internalFrameClosed(InternalFrameEvent e) - { - synchronized (tmp) - { - tmp.removeInternalFrameListener(this); - tmp.notifyAll(); - } - } - }); - try - { - while (! f.isClosed()) - f.wait(); - } - catch (InterruptedException ignored) - { - } - } + { + final JInternalFrame tmp = f; + tmp.toFront(); + + f.addInternalFrameListener(new InternalFrameAdapter() + { + public void internalFrameClosed(InternalFrameEvent e) + { + synchronized (tmp) + { + tmp.removeInternalFrameListener(this); + tmp.notifyAll(); + } + } + }); + try + { + while (! f.isClosed()) + f.wait(); + } + catch (InterruptedException ignored) + { + // Ignore this Exception. + } + } } } diff --git a/libjava/classpath/javax/swing/JPanel.java b/libjava/classpath/javax/swing/JPanel.java index c7f7c448331..7805e92b6e9 100644 --- a/libjava/classpath/javax/swing/JPanel.java +++ b/libjava/classpath/javax/swing/JPanel.java @@ -43,6 +43,7 @@ import java.awt.LayoutManager; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.plaf.PanelUI; /** @@ -52,63 +53,90 @@ import javax.swing.plaf.PanelUI; */ public class JPanel extends JComponent implements Accessible { - public JPanel() + /** + * Provides accessibility support for <code>JPanel</code>. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJPanel extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJPanel</code>. + */ + public AccessibleJPanel() { - this(new FlowLayout(), - true); + // Nothing to do here. } - - public JPanel(boolean double_buffered) - { - this(new FlowLayout(), - double_buffered); - } - - public JPanel(LayoutManager layout) - { - this(layout, - true); - } - - - public JPanel(LayoutManager layout, - boolean isDoubleBuffered) - { - if (layout == null) - { - System.err.println("NO LAYOUT SET !!!"); - layout = new FlowLayout(); - } - setLayout(layout); - setOpaque(true); - - updateUI(); - } - - public String getUIClassID() - { return "PanelUI"; } - - public void setUI(PanelUI ui) { - super.setUI(ui); - } - - public PanelUI getUI() { - return (PanelUI)ui; - } - - public void updateUI() { - setUI((PanelUI)UIManager.getUI(this)); - } - - - public AccessibleContext getAccessibleContext() + /** + * Returns the accessible role for <code>JPanel</code>, which is + * {@link AccessibleRole#PANEL}. + * + * @return the accessible role for <code>JPanel</code> + */ + public AccessibleRole getAccessibleRole() { - return null; + return AccessibleRole.PANEL; } + } + + public JPanel() + { + this(new FlowLayout(), true); + } + + public JPanel(boolean double_buffered) + { + this(new FlowLayout(), double_buffered); + } + + public JPanel(LayoutManager layout) + { + this(layout, true); + } + + public JPanel(LayoutManager layout, boolean isDoubleBuffered) + { + if (layout == null) + { + // TODO: Is this correct? Or should we throw a NPE? + layout = new FlowLayout(); + } + setLayout(layout); + setOpaque(true); + + updateUI(); + } + + public String getUIClassID() + { + return "PanelUI"; + } + + public void setUI(PanelUI ui) + { + super.setUI(ui); + } + + public PanelUI getUI() + { + return (PanelUI) ui; + } + + public void updateUI() + { + setUI((PanelUI) UIManager.getUI(this)); + } + + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJPanel(); + return accessibleContext; + } - protected String paramString() - { + protected String paramString() + { return "JPanel"; - } + } } diff --git a/libjava/classpath/javax/swing/JPasswordField.java b/libjava/classpath/javax/swing/JPasswordField.java index 151d2484a82..11e45e8a58a 100644 --- a/libjava/classpath/javax/swing/JPasswordField.java +++ b/libjava/classpath/javax/swing/JPasswordField.java @@ -67,6 +67,7 @@ public class JPasswordField extends JTextField */ protected AccessibleJPasswordField() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java index c4ee5fe7346..1f2282e2326 100644 --- a/libjava/classpath/javax/swing/JPopupMenu.java +++ b/libjava/classpath/javax/swing/JPopupMenu.java @@ -39,19 +39,13 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; -import java.awt.GridBagConstraints; import java.awt.Insets; -import java.awt.Panel; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.EventListener; @@ -102,11 +96,11 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement 1. if DefaultLightWeightPopupEnabled true (i) use lightweight container if popup feets inside top-level window - (ii) only use heavyweight container (JWindow) if popup doesn't fit. + (ii) only use heavyweight container (JDialog) if popup doesn't fit. 2. if DefaultLightWeightPopupEnabled false (i) if popup fits, use awt.Panel (mediumWeight) - (ii) if popup doesn't fit, use JWindow (heavyWeight) + (ii) if popup doesn't fit, use JDialog (heavyWeight) */ private static boolean DefaultLightWeightPopupEnabled = true; @@ -130,8 +124,15 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement /* Popup that is used to display JPopupMenu */ private transient Popup popup; - /* Location of the popup */ - private Point popupLocation; + /** + * Location of the popup, X coordinate. + */ + private int popupLocationX; + + /** + * Location of the popup, Y coordinate. + */ + private int popupLocationY; /* Field indicating if popup menu is visible or not */ private boolean visible = false; @@ -158,15 +159,6 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement updateUI(); } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { - } - - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Adds given menu item to the popup menu * @@ -220,19 +212,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement public void remove(int index) { super.remove(index); - - GridBagConstraints constraints = new GridBagConstraints(); - constraints.fill = GridBagConstraints.BOTH; - constraints.weightx = 100.0; - constraints.weighty = 100.0; - - Component[] items = getComponents(); - for (int i = index; i < items.length; i++) - { - constraints.gridy = i; - super.add(items[i], constraints, i); - } - this.setSize(this.getPreferredSize()); + revalidate(); } /** @@ -257,27 +237,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void insert(Component component, int index) { - GridBagConstraints constraints = new GridBagConstraints(); - constraints.fill = GridBagConstraints.BOTH; - constraints.weightx = 100.0; - constraints.weighty = 100.0; - - constraints.gridy = index; - super.add(component, constraints, index); - - // need to change constraints for the components that were moved by 1 - // due to the insertion - if (index != -1) - { - Component[] items = getComponents(); - - for (int i = index + 1; i < items.length; i++) - { - constraints.gridy = i; - super.add(items[i], constraints, i); - } - } - this.setSize(this.getPreferredSize()); + super.add(component, index); } /** @@ -527,7 +487,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void pack() { - super.setSize(null); + // Hook up this call so that it gets executed on the event thread in order + // to avoid synchronization problems when calling the layout manager. + if (! SwingUtilities.isEventDispatchThread()) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + show(); + } + }); + } + + setSize(getPreferredSize()); } /** @@ -547,8 +520,21 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement * * @param visible true if popup menu will become visible and false otherwise. */ - public void setVisible(boolean visible) + public void setVisible(final boolean visible) { + // Hook up this call so that it gets executed on the event thread in order + // to avoid synchronization problems when calling the layout manager. + if (! SwingUtilities.isEventDispatchThread()) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + setVisible(visible); + } + }); + } + if (visible == isVisible()) return; @@ -556,58 +542,21 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement this.visible = visible; if (old != isVisible()) { - firePropertyChange("visible", old, isVisible()); - if (visible) - { - firePopupMenuWillBecomeVisible(); - Container rootContainer = (Container) SwingUtilities.getRoot(invoker); - - boolean fit = true; - Dimension size; - - // Determine the size of the popup menu - if (this.getSize().width == 0 && this.getSize().width == 0) - size = this.getPreferredSize(); - else - size = this.getSize(); - - if ((size.width > (rootContainer.getWidth() - popupLocation.x)) - || (size.height > (rootContainer.getHeight() - popupLocation.y))) - fit = false; - if (lightWeightPopupEnabled && fit) - popup = new LightWeightPopup(this); - else - { - if (fit) - popup = new MediumWeightPopup(this); - else - popup = new HeavyWeightPopup(this); - } - if (popup instanceof LightWeightPopup - || popup instanceof MediumWeightPopup) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker) - .getLayeredPane(); - Point p = new Point(popupLocation.x, popupLocation.y); - SwingUtilities.convertPointFromScreen(p, layeredPane); - popup.show(p.x, p.y, size.width, size.height); - } - else - { - // Subtract insets of the top-level container if popup menu's - // top-left corner is inside it. - Insets insets = rootContainer.getInsets(); - popup.show(popupLocation.x - insets.left, - popupLocation.y - insets.top, size.width, - size.height); - } - } - else - { - firePopupMenuWillBecomeInvisible(); - popup.hide(); - } + firePropertyChange("visible", old, isVisible()); + if (visible) + { + firePopupMenuWillBecomeVisible(); + + PopupFactory pf = PopupFactory.getSharedInstance(); + pack(); + popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY); + popup.show(); + } + else + { + firePopupMenuWillBecomeInvisible(); + popup.hide(); + } } } @@ -619,11 +568,11 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void setLocation(int x, int y) { - if (popupLocation == null) - popupLocation = new Point(); - - popupLocation.x = x; - popupLocation.y = y; + popupLocationX = x; + popupLocationY = y; + // Handle the case when the popup is already showing. In this case we need + // to fetch a new popup from PopupFactory and use this. See the general + // contract of the PopupFactory. } /** @@ -657,11 +606,14 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void show(Component component, int x, int y) { - setInvoker(component); - Point p = new Point(x, y); - SwingUtilities.convertPointToScreen(p, component); - setLocation(p.x, p.y); - setVisible(true); + if (component.isShowing()) + { + setInvoker(component); + Point p = new Point(x, y); + SwingUtilities.convertPointToScreen(p, component); + setLocation(p.x, p.y); + setVisible(true); + } } /** @@ -882,162 +834,13 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement } /** - * This interface is used to display menu items of the JPopupMenu - */ - private interface Popup - { - /** - * Displays container on the screen - * - * @param x x-coordinate of popup menu's location on the screen - * @param y y-coordinate of popup menu's location on the screen - * @param width width of the container that is used to display menu - * item's for popup menu - * @param height height of the container that is used to display menu - * item's for popup menu - */ - void show(int x, int y, int width, int height); - - /** - * Hides container used to display popup menu item's from the screen - */ - void hide(); - } - - /** - * This class represents Popup menu that uses light weight container - * to display its contents. - */ - private class LightWeightPopup extends Container implements Popup - { - private Component c; - - /** - * Creates a new LightWeightPopup menu - * - * @param c Container containing menu items - */ - public LightWeightPopup(Container c) - { - this.c = c; - } - - /** - * Displayes lightweight container with menu items to the screen - * - * @param x x-coordinate of lightweight container on the screen - * @param y y-coordinate of lightweight container on the screen - * @param width width of the lightweight container - * @param height height of the lightweight container - */ - public void show(int x, int y, int width, int height) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - c.setBounds(x, y, width, height); - layeredPane.add(c, JLayeredPane.POPUP_LAYER, 0); - } - - /** - * Hides lightweight container from the screen - */ - public void hide() - { - // FIXME: Right now the lightweight container is removed from JLayered - // pane. It is probably would be better in order to improve performance - // to make the container invisible instead of removing it everytime. - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - int index = layeredPane.getIndexOf(c); - layeredPane.remove(index); - } - } - - /** - * MediumWeightPopup is an AWT Panel with JPopupMenu's menu items. - * It is used to display JPopupMenu's menu items on the screen - */ - private class MediumWeightPopup extends Panel implements Popup - { - /** - * Creates a new MediumWeightPopup object. - * - * @param c Container with JPopupMenu's menu items - */ - public MediumWeightPopup(Container c) - { - this.add(c); - } - - /** - * Displays AWT Panel with its components on the screen - * - * @param x x-coordinate of the upper-left corner of the panel's - * @param y y-coordinate of the upper-left corner of the panel's - * @param width width of the panel - * @param height height of the panel - */ - public void show(int x, int y, int width, int height) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - layeredPane.add(this, JLayeredPane.POPUP_LAYER, 0); - this.setBounds(x, y, width, height); - } - - /** - * Hides This panel from the screen - */ - public void hide() - { - // FIXME: Right now the lightweight container is removed from JLayered - // pane. It is probably would be better in order to improve performance - // to make the container invisible instead of removing it everytime. - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - int index = layeredPane.getIndexOf(this); - layeredPane.remove(index); - } - } - - /** - * HeavyWeightPopup is JWindow that is used to display JPopupMenu menu item's - * on the screen - */ - private class HeavyWeightPopup extends JWindow implements Popup - { - /** - * Creates a new HeavyWeightPopup object. - * - * @param c Container containing menu items - */ - public HeavyWeightPopup(Container c) - { - this.setContentPane(c); - } - - /** - * Displays JWindow container JPopupMenu's menu items to the screen - * - * @param x x-coordinate of JWindow containing menu items - * @param y y-coordinate of JWindow containing menu items - * @param width width of the JWindow - * @param height height of the JWindow - */ - public void show(int x, int y, int width, int height) - { - this.setBounds(x, y, width, height); - this.show(); - } - } - - /** * This is the separator that can be used in popup menu. */ public static class Separator extends JSeparator { public Separator() { + super(); } public String getUIClassID() @@ -1052,6 +855,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement protected AccessibleJPopupMenu() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -1066,8 +870,9 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement { public void propertyChange(PropertyChangeEvent evt) { - JPopupMenu.this.revalidate(); - JPopupMenu.this.repaint(); + // We used to have a revalidate() and repaint() call here. However I think + // this is not needed. Instead, a new Popup has to be fetched from the + // PopupFactory and used here. } } } diff --git a/libjava/classpath/javax/swing/JProgressBar.java b/libjava/classpath/javax/swing/JProgressBar.java index 1b8fcea4672..0de9115dc7d 100644 --- a/libjava/classpath/javax/swing/JProgressBar.java +++ b/libjava/classpath/javax/swing/JProgressBar.java @@ -81,6 +81,8 @@ public class JProgressBar extends JComponent implements SwingConstants, /** * AccessibleJProgressBar */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJProgressBar extends AccessibleJComponent implements AccessibleValue { @@ -91,6 +93,7 @@ public class JProgressBar extends JComponent implements SwingConstants, */ protected AccessibleJProgressBar() { + // Nothing to do here. } /** @@ -243,7 +246,7 @@ public class JProgressBar extends JComponent implements SwingConstants, model = new DefaultBoundedRangeModel(minimum, 0, minimum, maximum); if (orientation != HORIZONTAL && orientation != VERTICAL) throw new IllegalArgumentException(orientation + " is not a legal orientation"); - this.orientation = orientation; + setOrientation(orientation); changeListener = createChangeListener(); model.addChangeListener(changeListener); updateUI(); diff --git a/libjava/classpath/javax/swing/JRadioButton.java b/libjava/classpath/javax/swing/JRadioButton.java index 66f5902e899..e0593f3a501 100644 --- a/libjava/classpath/javax/swing/JRadioButton.java +++ b/libjava/classpath/javax/swing/JRadioButton.java @@ -192,8 +192,8 @@ public class JRadioButton extends JToggleButton public JRadioButton(String text, Icon icon, boolean selected) { super(text, icon, selected); - borderPainted = false; - contentAreaFilled = false; + setBorderPainted(false); + setHorizontalAlignment(LEADING); } /** diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java index 76a8fef640a..61a8dbab300 100644 --- a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java +++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java @@ -38,9 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectOutputStream; - import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; @@ -149,10 +146,6 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible model.setSelected(selected); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. @@ -202,6 +195,7 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible */ protected AccessibleJRadioButtonMenuItem() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java index cb0bafd84e0..dea4ee4b195 100644 --- a/libjava/classpath/javax/swing/JRootPane.java +++ b/libjava/classpath/javax/swing/JRootPane.java @@ -42,10 +42,14 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.IllegalComponentStateException; +import java.awt.Insets; import java.awt.LayoutManager; import java.awt.LayoutManager2; +import java.awt.Rectangle; import java.io.Serializable; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleRole; import javax.swing.plaf.RootPaneUI; @@ -59,10 +63,10 @@ import javax.swing.plaf.RootPaneUI; * * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public class JRootPane extends JComponent +public class JRootPane extends JComponent implements Accessible { // The class used to obtain the accessible role for this object. - protected static class AccessibleJRootPane + protected class AccessibleJRootPane extends AccessibleJComponent { /** * For compatability with Sun's JDK @@ -74,6 +78,7 @@ public class JRootPane extends JComponent */ protected AccessibleJRootPane() { + // Nothing to do here. } /** @@ -95,10 +100,36 @@ public class JRootPane extends JComponent private static final long serialVersionUID = -4100116998559815027L; /** + * The cached layout info for the glass pane. + */ + private Rectangle glassPaneBounds; + + /** + * The cached layout info for the layered pane. + */ + private Rectangle layeredPaneBounds; + + /** + * The cached layout info for the content pane. + */ + private Rectangle contentPaneBounds; + + /** + * The cached layout info for the menu bar. + */ + private Rectangle menuBarBounds; + + /** + * The cached preferred size. + */ + private Dimension prefSize; + + /** * Creates a new <code>RootLayout</code> object. */ protected RootLayout() { + // Nothing to do here. } /** @@ -109,6 +140,7 @@ public class JRootPane extends JComponent */ public void addLayoutComponent(Component comp, Object constraints) { + // Nothing to do here. } /** @@ -119,6 +151,7 @@ public class JRootPane extends JComponent */ public void addLayoutComponent(String name, Component comp) { + // Nothing to do here. } /** @@ -130,7 +163,7 @@ public class JRootPane extends JComponent */ public float getLayoutAlignmentX(Container target) { - return target.getAlignmentX(); + return 0.0F; } /** @@ -142,7 +175,7 @@ public class JRootPane extends JComponent */ public float getLayoutAlignmentY(Container target) { - return target.getAlignmentY(); + return 0.0F; } /** @@ -152,6 +185,14 @@ public class JRootPane extends JComponent */ public void invalidateLayout(Container target) { + synchronized (this) + { + glassPaneBounds = null; + layeredPaneBounds = null; + contentPaneBounds = null; + menuBarBounds = null; + prefSize = null; + } } /** @@ -161,81 +202,56 @@ public class JRootPane extends JComponent */ public void layoutContainer(Container c) { - Dimension menuBarSize; - Dimension containerSize = c.getSize(null); - Dimension contentPaneSize = contentPane.getPreferredSize(); - - /* - if size of top-level window wasn't set then just set - contentPane and menuBar to its preferred sizes. - Otherwise, if the size of top-level window was specified then - set menuBar to its preferred size and make content pane - to fit into the remaining space - - - +-------------------------------+ - | JLayeredPane | - | +--------------------------+ | - | | menuBar | | - | +--------------------------+ | - | +--------------------------+ | - | |contentPane | | - | | | | - | | | | - | | | | - | +--------------------------+ | - +-------------------------------+ - - */ - if (containerSize.width == 0 && containerSize.height == 0) + if (glassPaneBounds == null || layeredPaneBounds == null + || contentPaneBounds == null || menuBarBounds == null) { + Insets i = getInsets(); + int containerWidth = c.getBounds().width - i.left - i.right; + int containerHeight = c.getBounds().height - i.top - i.bottom; + + // 1. the glassPane fills entire viewable region (bounds - insets). + // 2. the layeredPane filles entire viewable region. + // 3. the menuBar is positioned at the upper edge of layeredPane. + // 4. the contentPane fills viewable region minus menuBar, if present. + + + // +-------------------------------+ + // | JLayeredPane | + // | +--------------------------+ | + // | | menuBar | | + // | +--------------------------+ | + // | +--------------------------+ | + // | |contentPane | | + // | | | | + // | | | | + // | | | | + // | +--------------------------+ | + // +-------------------------------+ + if (menuBar != null) { - int maxWidth; - menuBarSize = menuBar.getPreferredSize(); - maxWidth = Math.max(menuBarSize.width, contentPaneSize.width); - menuBar.setBounds(0, 0, maxWidth, menuBarSize.height); - glassPane.setBounds(0, menuBarSize.height, maxWidth, - contentPaneSize.height); - contentPane.setBounds(0, menuBarSize.height, maxWidth, - contentPaneSize.height); - layeredPane.setSize(maxWidth, - menuBarSize.height + contentPaneSize.height); + Dimension menuBarSize = menuBar.getPreferredSize(); + if (menuBarSize.height > containerHeight) + menuBarSize.height = containerHeight; + menuBarBounds = new Rectangle(0, 0, containerWidth, + menuBarSize.height); + contentPaneBounds = new Rectangle(0, menuBarSize.height, + containerWidth, + containerHeight - menuBarSize.height); } else - { - glassPane.setBounds(0, 0, contentPaneSize.width, - contentPaneSize.height); - contentPane.setBounds(0, 0, contentPaneSize.width, - contentPaneSize.height); - layeredPane.setSize(contentPaneSize.width, contentPaneSize.height); - } + contentPaneBounds = new Rectangle(0, 0, containerWidth, + containerHeight); + + glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight); + layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight); } - else - { - if (menuBar != null) - { - menuBarSize = menuBar.getPreferredSize(); - if (menuBarSize.height > containerSize.height) - menuBarSize.height = containerSize.height; - menuBar.setBounds(0, 0, containerSize.width, menuBarSize.height); - int remainingHeight = containerSize.height - menuBarSize.height; - glassPane.setBounds(0, menuBarSize.height, containerSize.width, - containerSize.height - menuBarSize.height); - contentPane.setBounds(0, menuBarSize.height, - containerSize.width, - (containerSize.height - menuBarSize.height)); - } - else - { - glassPane.setBounds(0, 0, containerSize.width, - containerSize.height); - contentPane.setBounds(0, 0, containerSize.width, - containerSize.height); - } - layeredPane.setSize(containerSize.width, containerSize.height); - } + glassPane.setBounds(glassPaneBounds); + layeredPane.setBounds(layeredPaneBounds); + if (menuBar != null) + menuBar.setBounds(menuBarBounds); + contentPane.setBounds(contentPaneBounds); } /** @@ -271,30 +287,29 @@ public class JRootPane extends JComponent */ public Dimension preferredLayoutSize(Container c) { - Dimension menuBarSize; - Dimension prefSize; - - Dimension containerSize = c.getSize(); - Dimension contentPaneSize = contentPane.getPreferredSize(); - - if (containerSize.width == 0 && containerSize.height == 0) + // We must synchronize here, otherwise we cannot guarantee that the + // prefSize is still non-null when returning. + synchronized (this) { - if (menuBar != null) + if (prefSize == null) { - int maxWidth; - menuBarSize = menuBar.getPreferredSize(); - maxWidth = Math.max(menuBarSize.width, contentPaneSize.width); - prefSize = new Dimension(maxWidth, - contentPaneSize.height - + menuBarSize.height); + Insets i = getInsets(); + prefSize = new Dimension(i.left + i.right, i.top + i.bottom); + Dimension contentPrefSize = contentPane.getPreferredSize(); + prefSize.width += contentPrefSize.width; + prefSize.height += contentPrefSize.height; + if (menuBar != null) + { + Dimension menuBarSize = menuBar.getPreferredSize(); + if (menuBarSize.width > contentPrefSize.width) + prefSize.width += menuBarSize.width - contentPrefSize.width; + prefSize.height += menuBarSize.height; + } } - else - prefSize = contentPaneSize; - } - else - prefSize = c.getSize(); - - return prefSize; + // Return a copy here so the cached value won't get trashed by some + // other component. + return new Dimension(prefSize); + } } /** @@ -304,6 +319,7 @@ public class JRootPane extends JComponent */ public void removeLayoutComponent(Component comp) { + // Nothing to do here. } } @@ -335,6 +351,32 @@ public class JRootPane extends JComponent protected JButton defaultButton; /** + * This field is unused since JDK1.3. To override the default action you + * should modify the JRootPane's ActionMap. + * + * @deprecated since JDK1.3 + * + * @specnote the specs indicate that the type of this field is + * a package private inner class + * javax.swing.JRootPane.DefaultAction. I assume that the closest + * public superclass is javax.swing.Action. + */ + protected Action defaultPressAction; + + /** + * This field is unused since JDK1.3. To override the default action you + * should modify the JRootPane's ActionMap. + * + * @deprecated since JDK1.3 + * + * @specnote the specs indicate that the type of this field is + * a package private inner class + * javax.swing.JRootPane.DefaultAction. I assume that the closest + * public superclass is javax.swing.Action. + */ + protected Action defaultReleaseAction; + + /** * @since 1.4 */ private int windowDecorationStyle = NONE; @@ -403,14 +445,25 @@ public class JRootPane extends JComponent } /** - * DOCUMENT ME! + * Sets the JRootPane's content pane. The content pane should typically be + * opaque for painting to work properly. This method also + * removes the old content pane from the layered pane. * - * @param p DOCUMENT ME! + * @param p the Container that will be the content pane + * @throws IllegalComponentStateException if p is null */ public void setContentPane(Container p) { - contentPane = p; - getLayeredPane().add(contentPane, JLayeredPane.FRAME_CONTENT_LAYER); + if (p == null) + throw new IllegalComponentStateException ("cannot " + + "have a null content pane"); + else + { + if (contentPane != null && contentPane.getParent() == layeredPane) + layeredPane.remove(contentPane); + contentPane = p; + getLayeredPane().add(contentPane, JLayeredPane.FRAME_CONTENT_LAYER); + } } /** @@ -488,7 +541,6 @@ public class JRootPane extends JComponent getGlassPane(); getLayeredPane(); getContentPane(); - setDoubleBuffered(true); updateUI(); } @@ -524,7 +576,6 @@ public class JRootPane extends JComponent { JPanel p = new JPanel(); p.setName(this.getName() + ".glassPane"); - p.setLayout(new BorderLayout()); p.setVisible(false); p.setOpaque(false); return p; @@ -615,7 +666,8 @@ public class JRootPane extends JComponent && style != COLOR_CHOOSER_DIALOG && style != FILE_CHOOSER_DIALOG && style != QUESTION_DIALOG - && style != WARNING_DIALOG) + && style != WARNING_DIALOG + && style != PLAIN_DIALOG) throw new IllegalArgumentException("invalid style"); int oldStyle = windowDecorationStyle; diff --git a/libjava/classpath/javax/swing/JScrollPane.java b/libjava/classpath/javax/swing/JScrollPane.java index e83513f07de..45dfbf50619 100644 --- a/libjava/classpath/javax/swing/JScrollPane.java +++ b/libjava/classpath/javax/swing/JScrollPane.java @@ -40,13 +40,14 @@ package javax.swing; import java.awt.Component; import java.awt.ComponentOrientation; -import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; -import java.awt.Point; import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -77,10 +78,73 @@ import javax.swing.plaf.UIResource; * <tr><td>wheelScrollingEnabled </td><td>scrollPane </td><td>yes </td></tr> * </table> */ -public class JScrollPane - extends JComponent +public class JScrollPane extends JComponent implements Accessible, ScrollPaneConstants { + /** + * Provides accessibility support for the <code>JScrollPane</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJScrollPane extends AccessibleJComponent + implements ChangeListener, PropertyChangeListener + { + + /** + * The viewport of the underlying scrollpane. + */ + protected JViewport viewPort; + + /** + * Creates a new <code>AccessibleJScrollPane</code> object. This + * initializes the <code>viewport</code> field with the current viewport + * from the scrollpane associated with this + * <code>AccessibleJScrollPane</code>. + */ + public AccessibleJScrollPane() + { + viewPort = getViewport(); + viewPort.addChangeListener(this); + viewPort.addPropertyChangeListener(this); + } + + /** + * Receives notification when the state of the viewport changes. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + // TODO: Figure out what should be done here, if anything. + } + + /** + * Receives notification if any of the viewport's bound properties changes. + * + * @param e the propery change event + */ + public void propertyChange(PropertyChangeEvent e) + { + // TODO: Figure out what should be done here, if anything. + } + + /** + * Resets the <code>viewPort</code> field when the scrollpane's viewport + * changes. This method is called by + * {@link JScrollPane#setViewport(JViewport)} in order to update the + * <code>viewPort</code> field and set up the listeners on this viewport + * correctly. + */ + public void resetViewPort() + { + viewPort.removeChangeListener(this); + viewPort.removePropertyChangeListener(this); + viewPort = getViewport(); + viewPort.addChangeListener(this); + viewPort.addPropertyChangeListener(this); + } + } + private static final long serialVersionUID = 5203525440012340014L; protected JViewport columnHeader; @@ -100,7 +164,6 @@ public class JScrollPane Border viewportBorder; boolean wheelScrollingEnabled; - ChangeListener scrollListener; public JViewport getColumnHeader() { @@ -228,10 +291,10 @@ public class JScrollPane remove(c); } - private void addNonNull(Component c) + private void addNonNull(Component c, Object constraints) { if (c != null) - add(c); + add(c, constraints); } public void setComponentOrientation(ComponentOrientation co) @@ -250,7 +313,7 @@ public class JScrollPane JViewport old = columnHeader; removeNonNull(old); columnHeader = h; - addNonNull(h); + addNonNull(h, JScrollPane.COLUMN_HEADER); firePropertyChange("columnHeader", old, h); sync(); } @@ -294,25 +357,25 @@ public class JScrollPane { removeNonNull(lowerRight); lowerRight = c; - addNonNull(c); + addNonNull(c, JScrollPane.LOWER_RIGHT_CORNER); } else if (key == UPPER_RIGHT_CORNER) { removeNonNull(upperRight); upperRight = c; - addNonNull(c); + addNonNull(c, JScrollPane.UPPER_RIGHT_CORNER); } else if (key == LOWER_LEFT_CORNER) { removeNonNull(lowerLeft); lowerLeft = c; - addNonNull(c); + addNonNull(c, JScrollPane.LOWER_LEFT_CORNER); } else if (key == UPPER_LEFT_CORNER) { removeNonNull(upperLeft); upperLeft = c; - addNonNull(c); + addNonNull(c, JScrollPane.UPPER_LEFT_CORNER); } else throw new IllegalArgumentException("unknown corner " + key); @@ -327,22 +390,10 @@ public class JScrollPane JScrollBar old = horizontalScrollBar; removeNonNull(old); horizontalScrollBar = h; - addNonNull(h); + addNonNull(h, JScrollPane.HORIZONTAL_SCROLLBAR); firePropertyChange("horizontalScrollBar", old, h); sync(); - if (old != null) - { - BoundedRangeModel model = old.getModel(); - if (model != null) - model.removeChangeListener(scrollListener); - } - if (h != null) - { - BoundedRangeModel model = h.getModel(); - if (model != null) - model.addChangeListener(scrollListener); - } } public void setHorizontalScrollBarPolicy(int h) @@ -359,6 +410,7 @@ public class JScrollPane horizontalScrollBarPolicy = h; firePropertyChange("horizontalScrollBarPolicy", old, h); sync(); + revalidate(); } public void setLayout(LayoutManager l) @@ -379,7 +431,7 @@ public class JScrollPane JViewport old = rowHeader; removeNonNull(old); rowHeader = v; - addNonNull(v); + addNonNull(v, JScrollPane.ROW_HEADER); firePropertyChange("rowHeader", old, v); sync(); } @@ -400,22 +452,9 @@ public class JScrollPane JScrollBar old = verticalScrollBar; removeNonNull(old); verticalScrollBar = v; - addNonNull(v); + addNonNull(v, JScrollPane.VERTICAL_SCROLLBAR); firePropertyChange("verticalScrollBar", old, v); sync(); - - if (old != null) - { - BoundedRangeModel model = old.getModel(); - if (model != null) - model.removeChangeListener(scrollListener); - } - if (v != null) - { - BoundedRangeModel model = v.getModel(); - if (model != null) - model.addChangeListener(scrollListener); - } } public void setVerticalScrollBarPolicy(int v) @@ -432,6 +471,7 @@ public class JScrollPane verticalScrollBarPolicy = v; firePropertyChange("verticalScrollBarPolicy", old, v); sync(); + revalidate(); } public void setWheelScrollingEnabled(boolean b) @@ -452,16 +492,17 @@ public class JScrollPane JViewport old = viewport; removeNonNull(old); - if (old != null) - old.removeChangeListener(scrollListener); viewport = v; - if (v != null) - v.addChangeListener(scrollListener); - addNonNull(v); + addNonNull(v, JScrollPane.VIEWPORT); revalidate(); repaint(); firePropertyChange("viewport", old, v); sync(); + if (accessibleContext != null) + { + AccessibleJScrollPane asp = (AccessibleJScrollPane) accessibleContext; + asp.resetViewPort(); + } } public void setViewportBorder(Border b) @@ -494,79 +535,6 @@ public class JScrollPane return true; } - ChangeListener createScrollListener() - { - return new ChangeListener() - { - - public void stateChanged(ChangeEvent event) - { - JScrollBar vsb = JScrollPane.this.getVerticalScrollBar(); - JScrollBar hsb = JScrollPane.this.getHorizontalScrollBar(); - JViewport vp = JScrollPane.this.getViewport(); - - if (vp != null && event.getSource() == vp) - { - // if the viewport changed, we should update the VSB / HSB - // models according to the new vertical and horizontal sizes - - Rectangle vr = vp.getViewRect(); - Dimension vs = vp.getViewSize(); - if (vsb != null - && (vsb.getMinimum() != 0 - || vsb.getMaximum() != vs.height - || vsb.getValue() != vr.y - || vsb.getVisibleAmount() != vr.height)) - vsb.setValues(vr.y, vr.height, 0, vs.height); - - if (hsb != null - && (hsb.getMinimum() != 0 - || hsb.getMaximum() != vs.width - || hsb.getValue() != vr.width - || hsb.getVisibleAmount() != vr.height)) - hsb.setValues(vr.x, vr.width, 0, vs.width); - } - else - { - // otherwise we got a change update from either the VSB or - // HSB model, and we need to update the viewport positions of - // both the main viewport and any row or column headers to - // match. - - int xpos = 0; - int ypos = 0; - - if (vsb != null) - ypos = vsb.getValue(); - - if (hsb != null) - xpos = hsb.getValue(); - - Point pt = new Point(xpos, ypos); - - if (vp != null - && vp.getViewPosition() != pt) - vp.setViewPosition(pt); - - pt.x = 0; - - if (rowHeader != null - && rowHeader.getViewPosition() != pt) - rowHeader.setViewPosition(pt); - - pt.x = xpos; - pt.y = 0; - - if (columnHeader != null - && columnHeader.getViewPosition() != pt) - columnHeader.setViewPosition(pt); - - } - } - }; - } - - /** * Creates a new <code>JScrollPane</code> without a view. The scrollbar * policy is set to {@link #VERTICAL_SCROLLBAR_AS_NEEDED} and @@ -627,7 +595,6 @@ public class JScrollPane */ public JScrollPane(Component view, int vsbPolicy, int hsbPolicy) { - scrollListener = createScrollListener(); setVerticalScrollBarPolicy(vsbPolicy); setVerticalScrollBar(createVerticalScrollBar()); setHorizontalScrollBarPolicy(hsbPolicy); @@ -635,7 +602,6 @@ public class JScrollPane viewport = createViewport(); if (view != null) getViewport().setView(view); - viewport.addChangeListener(scrollListener); add(viewport,0); setLayout(new ScrollPaneLayout()); setOpaque(false); @@ -728,4 +694,18 @@ public class JScrollPane } } } + + /** + * Returns the accessible context associated with this + * <code>JScrollPane</code>. + * + * @return the accessible context associated with this + * <code>JScrollPane</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJScrollPane(); + return accessibleContext; + } } diff --git a/libjava/classpath/javax/swing/JSeparator.java b/libjava/classpath/javax/swing/JSeparator.java index 6a3b97d35c6..602af6a380c 100644 --- a/libjava/classpath/javax/swing/JSeparator.java +++ b/libjava/classpath/javax/swing/JSeparator.java @@ -62,6 +62,7 @@ public class JSeparator extends JComponent implements SwingConstants, */ protected AccessibleJSeparator() { + // Nothing to do here. } /** @@ -131,7 +132,6 @@ public class JSeparator extends JComponent implements SwingConstants, public void updateUI() { setUI((SeparatorUI) UIManager.getUI(this)); - invalidate(); } /** diff --git a/libjava/classpath/javax/swing/JSlider.java b/libjava/classpath/javax/swing/JSlider.java index 2caf509a1bb..b28b06abad7 100644 --- a/libjava/classpath/javax/swing/JSlider.java +++ b/libjava/classpath/javax/swing/JSlider.java @@ -118,6 +118,8 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJSlider extends JComponent.AccessibleJComponent implements AccessibleValue { @@ -128,6 +130,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, */ protected AccessibleJSlider() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java index fc2b13e81a9..af34d9cf67d 100644 --- a/libjava/classpath/javax/swing/JSpinner.java +++ b/libjava/classpath/javax/swing/JSpinner.java @@ -108,10 +108,10 @@ public class JSpinner extends JComponent /** * DOCUMENT ME! */ - public void commitEdit() - throws ParseException + public void commitEdit() throws ParseException { - } /* TODO */ + // TODO: Implement this properly. + } /** * DOCUMENT ME! @@ -184,7 +184,8 @@ public class JSpinner extends JComponent */ public void propertyChange(PropertyChangeEvent event) { - } /* TODO */ + // TODO: Implement this properly. + } /** * DOCUMENT ME! @@ -193,11 +194,12 @@ public class JSpinner extends JComponent */ public void stateChanged(ChangeEvent event) { - } /* TODO */ + // TODO: Implement this properly. + } - /* no-ops */ public void removeLayoutComponent(Component child) { + // Nothing to do here. } /** @@ -208,6 +210,7 @@ public class JSpinner extends JComponent */ public void addLayoutComponent(String name, Component child) { + // Nothing to do here. } } @@ -258,6 +261,31 @@ public class JSpinner extends JComponent } /** + * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}. + * This editor uses a <code>JFormattedTextField</code> to edit the values + * of the spinner. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public static class ListEditor extends DefaultEditor + { + /** + * Creates a new instance of <code>ListEditor</code>. + * + * @param spinner the spinner for which this editor is used + */ + public ListEditor(JSpinner spinner) + { + super(spinner); + } + + public SpinnerListModel getModel() + { + return (SpinnerListModel) getSpinner().getModel(); + } + } + + /** * An editor class for a <code>JSpinner</code> that is used * for displaying and editing dates (e.g. that uses * <code>SpinnerDateModel</code> as model). @@ -307,7 +335,7 @@ public class JSpinner extends JComponent /** * Initializes the JFormattedTextField for this editor. * - * @param the date format to use in the formatted text field + * @param format the date format to use in the formatted text field */ private void init(SimpleDateFormat format) { diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java index cea5afef20a..cdab7bb6c4e 100644 --- a/libjava/classpath/javax/swing/JSplitPane.java +++ b/libjava/classpath/javax/swing/JSplitPane.java @@ -59,6 +59,8 @@ public class JSplitPane extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJSplitPane extends JComponent.AccessibleJComponent implements AccessibleValue { @@ -69,6 +71,7 @@ public class JSplitPane extends JComponent implements Accessible */ protected AccessibleJSplitPane() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index 828a69a24dc..27ba7bb82fc 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -56,8 +56,9 @@ import javax.swing.plaf.TabbedPaneUI; import javax.swing.plaf.UIResource; /** - * This is a container for components. One component is displayed at a time. - * Users can switch between components by clicking on tabs. + * This is a container for components where only one component is displayed at + * a given time and the displayed component can be switched by clicking on + * tabs. * * <p> * Tabs can be oriented in several ways. They can be above, below, left and @@ -72,12 +73,16 @@ public class JTabbedPane extends JComponent implements Serializable, SwingConstants { /** - * DOCUMENT ME! + * Accessibility support for <code>JTabbedPane</code>. */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent implements AccessibleSelection, ChangeListener { - /** DOCUMENT ME! */ + /** + * The serialization UID. + */ private static final long serialVersionUID = 7610530885966830483L; /** @@ -89,18 +94,21 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Receives notification when the selection state of the + * <code>JTabbedPane</code> changes. * - * @param e DOCUMENT ME! + * @param e the change event describing the change */ public void stateChanged(ChangeEvent e) { + // Implement this properly. } /** - * DOCUMENT ME! + * Returns the accessible role of the <code>JTabbedPane</code>, which is + * {@link AccessibleRole#PAGE_TAB_LIST}. * - * @return DOCUMENT ME! + * @return the accessible role of the <code>JTabbedPane</code> */ public AccessibleRole getAccessibleRole() { @@ -108,9 +116,11 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the number of accessible child components of the + * <code>JTabbedPane</code>. * - * @return DOCUMENT ME! + * @return the number of accessible child components of the + * <code>JTabbedPane</code> */ public int getAccessibleChildrenCount() { @@ -118,11 +128,11 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the accessible child component at the specified index. * - * @param i DOCUMENT ME! + * @param i the index of the child component to fetch * - * @return DOCUMENT ME! + * @return the accessible child component at the specified index */ public Accessible getAccessibleChild(int i) { @@ -130,9 +140,10 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the current selection state of the <code>JTabbedPane</code> + * as AccessibleSelection object. * - * @return DOCUMENT ME! + * @return the current selection state of the <code>JTabbedPane</code> */ public AccessibleSelection getAccessibleSelection() { @@ -140,11 +151,15 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the accessible child component at the specified coordinates. + * If there is no child component at this location, then return the + * currently selected tab. * - * @param p DOCUMENT ME! + * @param p the coordinates at which to look up the child component * - * @return DOCUMENT ME! + * @return the accessible child component at the specified coordinates or + * the currently selected tab if there is no child component at + * this location */ public Accessible getAccessibleAt(Point p) { @@ -152,9 +167,13 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * The number of selected child components of the + * <code>JTabbedPane</code>. This will be <code>0</code> if the + * <code>JTabbedPane</code> has no children, or <code>1</code> otherwise, + * since there is always exactly one tab selected. * - * @return DOCUMENT ME! + * @return number of selected child components of the + * <code>JTabbedPane</code> */ public int getAccessibleSelectionCount() { @@ -192,6 +211,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void addAccessibleSelection(int i) { + // TODO: Implement this properly. } /** @@ -201,6 +221,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void removeAccessibleSelection(int i) { + // TODO: Implement this properly. } /** @@ -208,6 +229,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void clearAccessibleSelection() { + // TODO: Implement this properly. } /** @@ -215,6 +237,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } @@ -231,6 +254,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ protected ModelListener() { + // Nothing to do here. } /** @@ -313,9 +337,10 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void setComponent(Component c) { - remove(component); - this.component = c; - add(c); + int i = indexOfComponent(component); + insertTab(title, icon, c, tip, i); + component = c; + removeTabAt(i); } /** @@ -596,7 +621,7 @@ public class JTabbedPane extends JComponent implements Serializable, throw new IllegalArgumentException("tabLayoutPolicy is not valid."); this.tabPlacement = tabPlacement; layoutPolicy = tabLayoutPolicy; - + changeEvent = new ChangeEvent(this); changeListener = createChangeListener(); @@ -863,15 +888,17 @@ public class JTabbedPane extends JComponent implements Serializable, * This method inserts tabs into JTabbedPane. This includes adding the * component to the JTabbedPane and hiding it. * - * @param title The title of the tab. - * @param icon The tab's icon. - * @param component The component associated with the tab. - * @param tip The tooltip for the tab. - * @param index The index to insert the tab at. + * @param title the title of the tab; may be <code>null</code> + * @param icon the tab's icon; may be <code>null</code> + * @param component the component associated with the tab + * @param tip the tooltip for the tab + * @param index the index to insert the tab at */ public void insertTab(String title, Icon icon, Component component, String tip, int index) { + if (title == null) + title = ""; Page p = new Page(title, icon, component, tip); tabs.insertElementAt(p, index); @@ -893,10 +920,10 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param icon The icon for the tab. - * @param component The associated component. - * @param tip The associated tooltip. + * @param title the title of the tab; may be <code>null</code> + * @param icon the icon for the tab; may be <code>null</code> + * @param component the associated component + * @param tip the associated tooltip */ public void addTab(String title, Icon icon, Component component, String tip) { @@ -906,9 +933,9 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param icon The icon for the tab. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param icon the icon for the tab; may be <code>null</code> + * @param component the associated component */ public void addTab(String title, Icon icon, Component component) { @@ -918,8 +945,8 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param component the associated component */ public void addTab(String title, Component component) { @@ -942,6 +969,7 @@ public class JTabbedPane extends JComponent implements Serializable, super.add(component); else insertTab(component.getName(), null, component, null, tabs.size()); + return component; } @@ -950,8 +978,8 @@ public class JTabbedPane extends JComponent implements Serializable, * instance of UIResource, it doesn't add the tab and instead add the * component directly to the JTabbedPane. * - * @param title The title of the tab. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param component the associated component * * @return The Component that was added. */ @@ -1025,45 +1053,37 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * The tab and it's associated component are removed. After the component - * has been removed from the JTabbedPane, it's set visible to ensure that - * it can be seen. + * Removes the tab at index. After the component associated with + * index is removed, its visibility is reset to true to ensure it + * will be visible if added to other containers. * * @param index The index of the tab to remove. */ public void removeTabAt(int index) { checkIndex(index, 0, tabs.size()); - Component c = getComponentAt(index); - super.remove(index); - c.show(); tabs.remove(index); + getComponentAt(index).show(); } /** - * This method removes the component from the JTabbedPane. After the - * component has been removed from the JTabbedPane, it's set visible to - * ensure that it can be seen. + * Removes the specified Component from the JTabbedPane. * * @param component The Component to remove. */ public void remove(Component component) { - // This simply removes the component. - int index = indexOfComponent(component); super.remove(component); - component.show(); - setComponentAt(index, null); } /** - * This method removes the tab and component from the JTabbedPane. It simply - * calls removeTabAt(int index). + * Removes the tab and component which corresponds to the specified index. * * @param index The index of the tab to remove. */ public void remove(int index) { + remove(getComponentAt(index)); removeTabAt(index); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 21680d567e2..69a865df9c0 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/javax/swing/JTable.java @@ -40,22 +40,34 @@ package javax.swing; import java.awt.Color; import java.awt.Component; +import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.text.DateFormat; import java.text.NumberFormat; import java.util.Date; import java.util.EventObject; import java.util.Hashtable; +import java.util.Locale; import java.util.Vector; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleExtendedTable; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleTable; +import javax.accessibility.AccessibleTableModelChange; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; @@ -76,10 +88,874 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.text.Caret; -public class JTable extends JComponent +public class JTable + extends JComponent implements TableModelListener, Scrollable, TableColumnModelListener, ListSelectionListener, CellEditorListener, Accessible { + /** + * Provides accessibility support for <code>JTable</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTable + extends AccessibleJComponent + implements AccessibleSelection, ListSelectionListener, TableModelListener, + TableColumnModelListener, CellEditorListener, PropertyChangeListener, + AccessibleExtendedTable + { + + /** + * Provides accessibility support for table cells. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTableCell + extends AccessibleContext + implements Accessible, AccessibleComponent + { + + /** + * The table of this cell. + */ + private JTable table; + + /** + * The row index of this cell. + */ + private int row; + + /** + * The column index of this cell. + */ + private int column; + + /** + * The index of this cell inside the AccessibleJTable parent. + */ + private int index; + + /** + * Creates a new <code>AccessibleJTableCell</code>. + * + * @param t the table + * @param r the row + * @param c the column + * @param i the index of this cell inside the accessible table parent + */ + public AccessibleJTableCell(JTable t, int r, int c, int i) + { + table = t; + row = r; + column = c; + index = i; + } + + /** + * Returns the accessible row for the table cell. + * + * @return the accessible row for the table cell + */ + public AccessibleRole getAccessibleRole() + { + // TODO: What is the role of the table cell? + return AccessibleRole.UNKNOWN; + } + + /** + * Returns the accessible state set of this accessible table cell. + * + * @return the accessible state set of this accessible table cell + */ + public AccessibleStateSet getAccessibleStateSet() + { + // TODO: What state shoiuld be returned here? + return new AccessibleStateSet(); + } + + /** + * Returns the index of this cell in the parent object. + * + * @return the index of this cell in the parent object + */ + public int getAccessibleIndexInParent() + { + return index; + } + + /** + * Returns the number of children of this object. Table cells cannot have + * children, so we return <code>0</code> here. + * + * @return <code>0</code> + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the accessible child at index <code>i</code>. Table cells + * don't have children, so we return <code>null</code> here. + * + * @return <code>null</code> + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale setting for this accessible table cell. + * + * @return the locale setting for this accessible table cell + */ + public Locale getLocale() + { + // TODO: For now, we return english here. This must be fixed as soon + // as we have a localized Swing. + return Locale.ENGLISH; + } + + /** + * Returns the accessible context of this table cell. Since accessible + * table cells are their own accessible context, we return + * <code>this</code>. + * + * @return the accessible context of this table cell + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color of this cell. + * + * @return the background color of this cell + */ + public Color getBackground() + { + return table.getBackground(); + } + + /** + * Sets the background of the cell. Since table cells cannot have + * individual background colors, this method does nothing. Set the + * background directly on the table instead. + * + * @param color not used + */ + public void setBackground(Color color) + { + // This method does nothing. See API comments. + } + + /** + * Returns the foreground color of the table cell. + * + * @return the foreground color of the table cell + */ + public Color getForeground() + { + return table.getForeground(); + } + + /** + * Sets the foreground of the cell. Since table cells cannot have + * individual foreground colors, this method does nothing. Set the + * foreground directly on the table instead. + * + * @param color not used + */ + public void setForeground(Color color) + { + // This method does nothing. See API comments. + } + + /** + * Returns the cursor for this table cell. + * + * @return the cursor for this table cell + */ + public Cursor getCursor() + { + return table.getCursor(); + } + + /** + * Sets the cursor of the cell. Since table cells cannot have + * individual cursors, this method does nothing. Set the + * cursor directly on the table instead. + * + * @param cursor not used + */ + public void setCursor(Cursor cursor) + { + // This method does nothing. See API comments. + } + + /** + * Returns the font of the table cell. + * + * @return the font of the table cell + */ + public Font getFont() + { + return table.getFont(); + } + + /** + * Sets the font of the cell. Since table cells cannot have + * individual fonts, this method does nothing. Set the + * font directly on the table instead. + * + * @param font not used + */ + public void setFont(Font font) + { + // This method does nothing. See API comments. + } + + /** + * Returns the font metrics for a specified font. + * + * @param font the font for which we return the metrics + * + * @return the font metrics for a specified font + */ + public FontMetrics getFontMetrics(Font font) + { + return table.getFontMetrics(font); + } + + /** + * Returns <code>true</code> if this table cell is enabled, + * <code>false</code> otherwise. + * + * @return <code>true</code> if this table cell is enabled, + * <code>false</code> otherwise + */ + public boolean isEnabled() + { + return table.isEnabled(); + } + + /** + * Table cells cannot be disabled or enabled individually, so this method + * does nothing. Set the enabled flag on the table itself. + * + * @param b not used here + */ + public void setEnabled(boolean b) + { + // This method does nothing. See API comments. + } + + /** + * Returns <code>true</code> if this cell is visible, <code>false</code> + * otherwise. + * + * @return <code>true</code> if this cell is visible, <code>false</code> + * otherwise + */ + public boolean isVisible() + { + return table.isVisible(); + } + + /** + * The visibility cannot be set on individual table cells, so this method + * does nothing. Set the visibility on the table itself. + * + * @param b not used + */ + public void setVisible(boolean b) + { + // This method does nothing. See API comments. + } + + /** + * Returns <code>true</code> if this table cell is currently showing on + * screen. + * + * @return <code>true</code> if this table cell is currently showing on + * screen + */ + public boolean isShowing() + { + return table.isShowing(); + } + + /** + * Returns <code>true</code> if this table cell contains the location + * at <code>point</code>, <code>false</code> otherwise. + * <code>point</code> is interpreted as relative to the coordinate system + * of the table cell. + * + * @return <code>true</code> if this table cell contains the location + * at <code>point</code>, <code>false</code> otherwise + */ + public boolean contains(Point point) + { + Rectangle cellRect = table.getCellRect(row, column, true); + cellRect.x = 0; + cellRect.y = 0; + return cellRect.contains(point); + } + + /** + * Returns the screen location of the table cell. + * + * @return the screen location of the table cell + */ + public Point getLocationOnScreen() + { + Point tableLoc = table.getLocationOnScreen(); + Rectangle cellRect = table.getCellRect(row, column, true); + tableLoc.x += cellRect.x; + tableLoc.y += cellRect.y; + return tableLoc; + } + + /** + * Returns the location of this cell relative to the table's bounds. + * + * @return the location of this cell relative to the table's bounds + */ + public Point getLocation() + { + Rectangle cellRect = table.getCellRect(row, column, true); + return new Point(cellRect.x, cellRect.y); + } + + /** + * The location of the table cells cannot be manipulated directly, so + * this method does nothing. + * + * @param point not used + */ + public void setLocation(Point point) + { + // This method does nothing. See API comments. + } + + /** + * Returns the bounds of the cell relative to its table. + * + * @return the bounds of the cell relative to its table + */ + public Rectangle getBounds() + { + return table.getCellRect(row, column, true); + } + + /** + * The bounds of the table cells cannot be manipulated directly, so + * this method does nothing. + * + * @param rectangle not used + */ + public void setBounds(Rectangle rectangle) + { + // This method does nothing. See API comments. + } + + /** + * Returns the size of the table cell. + * + * @return the size of the table cell + */ + public Dimension getSize() + { + Rectangle cellRect = table.getCellRect(row, column, true); + return new Dimension(cellRect.width, cellRect.height); + } + + /** + * The size cannot be set on table cells directly, so this method does + * nothing. + * + * @param dimension not used + */ + public void setSize(Dimension dimension) + { + // This method does nothing. See API comments. + } + + /** + * Table cells have no children, so we return <code>null</code> here. + * + * @return <code>null</code> + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns <code>true</code> if this table cell is focus traversable, + * <code>false</code> otherwise. + * + * @return <code>true</code> if this table cell is focus traversable, + * <code>false</code> otherwise + */ + public boolean isFocusTraversable() + { + return table.isFocusable(); + } + + /** + * Requests that this table cell gets the keyboard focus. + */ + public void requestFocus() + { + // We first set the selection models' lead selection to this cell. + table.getColumnModel().getSelectionModel() + .setLeadSelectionIndex(column); + table.getSelectionModel().setLeadSelectionIndex(row); + // Now we request that the table receives focus. + table.requestFocus(); + } + + /** + * Adds a focus listener to this cell. The focus listener is really + * added to the table, so there is no way to find out when an individual + * cell changes the focus. + * + * @param listener the focus listener to add + */ + public void addFocusListener(FocusListener listener) + { + table.addFocusListener(listener); + } + + /** + * Removes a focus listener from the cell. The focus listener is really + * removed from the table. + * + * @param listener the listener to remove + */ + public void removeFocusListener(FocusListener listener) + { + table.removeFocusListener(listener); + } + + } + + protected class AccessibleJTableModelChange + implements AccessibleTableModelChange + { + protected int type; + protected int firstRow; + protected int lastRow; + protected int firstColumn; + protected int lastColumn; + + protected AccessibleJTableModelChange(int type, int firstRow, + int lastRow, int firstColumn, + int lastColumn) + { + this.type = type; + this.firstRow = firstRow; + this.lastRow = lastRow; + this.firstColumn = firstColumn; + this.lastColumn = lastColumn; + } + + public int getType() + { + return type; + } + + public int getFirstRow() + { + return firstRow; + } + + public int getLastRow() + { + return lastRow; + } + + public int getFirstColumn() + { + return firstColumn; + } + + public int getLastColumn() + { + return lastColumn; + } + } + + /** + * Creates a new <code>AccessibleJTable</code>. + * + * @since JDK1.5 + */ + protected AccessibleJTable() + { + getModel().addTableModelListener(this); + getSelectionModel().addListSelectionListener(this); + getColumnModel().addColumnModelListener(this); + getCellEditor().addCellEditorListener(this); + } + + /** + * Returns the number of selected items in this table. + */ + public int getAccessibleSelectionCount() + { + return getSelectedColumnCount(); + } + + public Accessible getAccessibleSelection(int i) + { + // TODO Auto-generated method stub + return null; + } + + public boolean isAccessibleChildSelected(int i) + { + // TODO Auto-generated method stub + return false; + } + + public void addAccessibleSelection(int i) + { + // TODO Auto-generated method stub + + } + + public void removeAccessibleSelection(int i) + { + // TODO Auto-generated method stub + + } + + public void clearAccessibleSelection() + { + // TODO Auto-generated method stub + + } + + public void selectAllAccessibleSelection() + { + // TODO Auto-generated method stub + + } + + public void valueChanged(ListSelectionEvent event) + { + // TODO Auto-generated method stub + + } + + /** + * Receives notification when the table model changes. Depending on the + * type of change, this method calls {@link #tableRowsInserted} or + * {@link #tableRowsDeleted}. + * + * @param event the table model event + */ + public void tableChanged(TableModelEvent event) + { + switch (event.getType()) + { + case TableModelEvent.INSERT: + tableRowsInserted(event); + break; + case TableModelEvent.DELETE: + tableRowsDeleted(event); + break; + } + } + + /** + * Receives notification when one or more rows have been inserted into the + * table. + * + * @param event the table model event + */ + public void tableRowsInserted(TableModelEvent event) + { + // TODO: What to do here, if anything? This might be a hook method for + // subclasses... + } + + /** + * Receives notification when one or more rows have been deleted from the + * table. + * + * @param event the table model event + */ + public void tableRowsDeleted(TableModelEvent event) + { + // TODO: What to do here, if anything? This might be a hook method for + // subclasses... + } + + public void columnAdded(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnMarginChanged(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnMoved(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnRemoved(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnSelectionChanged(ListSelectionEvent event) + { + // TODO Auto-generated method stub + + } + + public void editingCanceled(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + public void editingStopped(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + /** + * Receives notification when any of the JTable's properties changes. This + * is used to replace the listeners on the table's model, selection model, + * column model and cell editor. + * + * @param e the property change event + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("tableModel")) + { + TableModel oldModel = (TableModel) e.getOldValue(); + oldModel.removeTableModelListener(this); + TableModel newModel = (TableModel) e.getNewValue(); + newModel.addTableModelListener(this); + } + else if (propName.equals("columnModel")) + { + TableColumnModel oldModel = (TableColumnModel) e.getOldValue(); + oldModel.removeColumnModelListener(this); + TableColumnModel newModel = (TableColumnModel) e.getNewValue(); + newModel.addColumnModelListener(this); + } + else if (propName.equals("selectionModel")) + { + ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); + oldModel.removeListSelectionListener(this); + ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); + newModel.addListSelectionListener(this); + } + else if (propName.equals("cellEditor")) + { + CellEditor oldEd = (CellEditor) e.getOldValue(); + oldEd.removeCellEditorListener(this); + CellEditor newEd = (CellEditor) e.getNewValue(); + newEd.addCellEditorListener(this); + } + } + + public int getAccessibleRow(int index) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumn(int index) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleIndex(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public Accessible getAccessibleCaption() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleCaption(Accessible caption) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleSummary() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleSummary(Accessible summary) + { + // TODO Auto-generated method stub + + } + + public int getAccessibleRowCount() + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumnCount() + { + // TODO Auto-generated method stub + return 0; + } + + public Accessible getAccessibleAt(int r, int c) + { + // TODO Auto-generated method stub + return null; + } + + public int getAccessibleRowExtentAt(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumnExtentAt(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public AccessibleTable getAccessibleRowHeader() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleRowHeader(AccessibleTable header) + { + // TODO Auto-generated method stub + + } + + public AccessibleTable getAccessibleColumnHeader() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleColumnHeader(AccessibleTable header) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleRowDescription(int r) + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleRowDescription(int r, Accessible description) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleColumnDescription(int c) + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleColumnDescription(int c, Accessible description) + { + // TODO Auto-generated method stub + + } + + public boolean isAccessibleSelected(int r, int c) + { + // TODO Auto-generated method stub + return false; + } + + public boolean isAccessibleRowSelected(int r) + { + // TODO Auto-generated method stub + return false; + } + + public boolean isAccessibleColumnSelected(int c) + { + // TODO Auto-generated method stub + return false; + } + + public int[] getSelectedAccessibleRows() + { + // TODO Auto-generated method stub + return null; + } + + public int[] getSelectedAccessibleColumns() + { + // TODO Auto-generated method stub + return null; + } + + } + /** + * Handles property changes from the <code>TableColumn</code>s of this + * <code>JTable</code>. + * + * More specifically, this triggers a {@link #revalidate()} call if the + * preferredWidth of one of the observed columns changes. + */ + class TableColumnPropertyChangeHandler implements PropertyChangeListener + { + /** + * Receives notification that a property of the observed TableColumns + * has changed. + * + * @param ev the property change event + */ + public void propertyChange(PropertyChangeEvent ev) + { + if (ev.getPropertyName().equals("preferredWidth")) + { + JTableHeader header = getTableHeader(); + TableColumn col = (TableColumn) ev.getSource(); + header.setResizingColumn(col); + doLayout(); + header.setResizingColumn(null); + } + } + } /** * A cell renderer for boolean values. @@ -364,7 +1240,7 @@ public class JTable extends JComponent * property when the {@link #dataModel} property is changed. * * @see #setModel(TableModel) - * @see #createColumnsFromModel() + * @see #createDefaultColumnsFromModel() * @see #setColumnModel(TableColumnModel) * @see #setAutoCreateColumnsFromModel(boolean) * @see #getAutoCreateColumnsFromModel() @@ -480,11 +1356,6 @@ public class JTable extends JComponent protected ListSelectionModel selectionModel; /** - * The accessibleContext property. - */ - protected AccessibleContext accessibleContext; - - /** * The current cell editor. */ protected TableCellEditor cellEditor; @@ -492,7 +1363,7 @@ public class JTable extends JComponent /** * Whether or not drag-and-drop is enabled on this table. * - * @see #setDragEnabled() + * @see #setDragEnabled(boolean) * @see #getDragEnabled() */ private boolean dragEnabled; @@ -584,6 +1455,12 @@ public class JTable extends JComponent Object oldCellValue; /** + * The property handler for this table's columns. + */ + TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler = + new TableColumnPropertyChangeHandler(); + + /** * Creates a new <code>JTable</code> instance. */ public JTable () @@ -643,15 +1520,28 @@ public class JTable extends JComponent */ public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm) { - setModel(dm == null ? createDefaultDataModel() : dm); + boolean autoCreate = false; + if (cm != null) + setColumnModel(cm); + else + { + setColumnModel(createDefaultColumnModel()); + autoCreate = true; + } setSelectionModel(sm == null ? createDefaultSelectionModel() : sm); - - this.columnModel = cm; + setModel(dm == null ? createDefaultDataModel() : dm); + setAutoCreateColumnsFromModel(autoCreate); initializeLocalVars(); - // The next two lines are for compliance with the JDK which starts - // the JLists associated with a JTable with both lead selection - // indices at 0, rather than -1 as in regular JLists + // The following four lines properly set the lead selection indices. + // After this, the UI will handle the lead selection indices. + // FIXME: this should probably not be necessary, if the UI is installed + // before the TableModel is set then the UI will handle things on its + // own, but certain variables need to be set before the UI can be installed + // so we must get the correct order for all the method calls in this + // constructor. + selectionModel.setAnchorSelectionIndex(0); selectionModel.setLeadSelectionIndex(0); + columnModel.getSelectionModel().setAnchorSelectionIndex(0); columnModel.getSelectionModel().setLeadSelectionIndex(0); updateUI(); } @@ -659,12 +1549,8 @@ public class JTable extends JComponent protected void initializeLocalVars() { setTableHeader(createDefaultTableHeader()); - this.autoCreateColumnsFromModel = false; - if (columnModel == null) - { - this.autoCreateColumnsFromModel = true; - createColumnsFromModel(); - } + if (autoCreateColumnsFromModel) + createDefaultColumnsFromModel(); this.columnModel.addColumnModelListener(this); this.defaultRenderersByColumnClass = new Hashtable(); @@ -754,6 +1640,7 @@ public class JTable extends JComponent } columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); } protected void createDefaultEditors() @@ -799,20 +1686,6 @@ public class JTable extends JComponent return new JTableHeader(columnModel); } - private void createColumnsFromModel() - { - if (dataModel == null) - return; - - TableColumnModel cm = createDefaultColumnModel(); - - for (int i = 0; i < dataModel.getColumnCount(); ++i) - { - cm.addColumn(new TableColumn(i)); - } - this.setColumnModel(cm); - } - // listener support public void columnAdded (TableColumnModelEvent event) @@ -890,8 +1763,19 @@ public class JTable extends JComponent if ((event.getFirstRow() ==TableModelEvent.HEADER_ROW) && autoCreateColumnsFromModel) - createColumnsFromModel(); + createDefaultColumnsFromModel(); + // If the structure changes, we need to revalidate, since that might + // affect the size parameters of the JTable. Otherwise we only need + // to perform a repaint to update the view. + if (event.getType() == TableModelEvent.INSERT) + revalidate(); + else if (event.getType() == TableModelEvent.DELETE) + { + if (dataModel.getRowCount() == 0) + clearSelection(); + revalidate(); + } repaint(); } @@ -944,8 +1828,7 @@ public class JTable extends JComponent { int y0 = getLocation().y; int nrows = getRowCount(); - Dimension gap = getIntercellSpacing(); - int height = getRowHeight() + (gap == null ? 0 : gap.height); + int height = getRowHeight(); int y = point.y; for (int i = 0; i < nrows; ++i) @@ -990,10 +1873,7 @@ public class JTable extends JComponent int y = (height + y_gap) * row; for (int i = 0; i < column; ++i) - { - x += columnModel.getColumn(i).getWidth(); - x += x_gap; - } + x += columnModel.getColumn(i).getWidth(); if (includeSpacing) return new Rectangle(x, y, width, height); @@ -1015,7 +1895,7 @@ public class JTable extends JComponent * @return The current value of the selectedRow property */ public int getSelectedRow () - { + { return selectionModel.getMinSelectionIndex(); } @@ -1069,13 +1949,14 @@ public class JTable extends JComponent // scroll direction. if (orientation == SwingConstants.VERTICAL) - return rowHeight; + return direction * rowHeight; else { int sum = 0; for (int i = 0; i < getColumnCount(); ++i) sum += columnModel.getColumn(0).getWidth(); - return getColumnCount() == 0 ? 10 : sum / getColumnCount(); + int inc = getColumnCount() == 0 ? 10 : sum / getColumnCount(); + return direction * inc; } } @@ -1656,24 +2537,29 @@ public class JTable extends JComponent // Don't do anything if setting the current model again. if (dataModel == m) return; - + + TableModel oldModel = dataModel; + // Remove table as TableModelListener from old model. if (dataModel != null) dataModel.removeTableModelListener(this); if (m != null) { - // Set property. + // Set property. dataModel = m; - // Add table as TableModelListener to new model. - dataModel.addTableModelListener(this); + // Add table as TableModelListener to new model. + dataModel.addTableModelListener(this); - // Automatically create columns. - if (autoCreateColumnsFromModel) - createColumnsFromModel(); + // Automatically create columns. + if (autoCreateColumnsFromModel) + createDefaultColumnsFromModel(); } - + + // This property is bound, so we fire a property change event. + firePropertyChange("model", oldModel, dataModel); + // Repaint table. revalidate(); repaint(); @@ -1959,7 +2845,8 @@ public class JTable extends JComponent int average = spill / cols.length; for (int i = 0; i < cols.length; i++) { - cols[i].setWidth(cols[i].getWidth() + average); + if (cols[i] != null) + cols[i].setWidth(cols[i].getWidth() + average); } } @@ -2023,6 +2910,8 @@ public class JTable extends JComponent case AUTO_RESIZE_OFF: default: + int prefWidth = resizingColumn.getPreferredWidth(); + resizingColumn.setWidth(prefWidth); } } else @@ -2258,6 +3147,8 @@ public class JTable extends JComponent */ public void createDefaultColumnsFromModel() { + assert columnModel != null : "The columnModel must not be null."; + // remove existing columns int columnIndex = columnModel.getColumnCount() - 1; while (columnIndex >= 0) @@ -2272,7 +3163,9 @@ public class JTable extends JComponent { TableColumn column = new TableColumn(c); column.setIdentifier(dataModel.getColumnName(c)); + column.setHeaderValue(dataModel.getColumnName(c)); columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); } } @@ -2372,4 +3265,13 @@ public class JTable extends JComponent return editor.getTableCellEditorComponent (this, getValueAt(row, column), isCellSelected(row, column), row, column); } + + /** + * This revalidates the <code>JTable</code> and queues a repaint. + */ + protected void resizeAndRepaint() + { + revalidate(); + repaint(); + } } diff --git a/libjava/classpath/javax/swing/JTextArea.java b/libjava/classpath/javax/swing/JTextArea.java index 53591ffcc3a..2fa185b6207 100644 --- a/libjava/classpath/javax/swing/JTextArea.java +++ b/libjava/classpath/javax/swing/JTextArea.java @@ -42,6 +42,8 @@ import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Rectangle; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleStateSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; @@ -92,6 +94,35 @@ import javax.swing.text.View; public class JTextArea extends JTextComponent { /** + * Provides accessibility support for <code>JTextArea</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTextArea extends AccessibleJTextComponent + { + + /** + * Creates a new <code>AccessibleJTextArea</code> object. + */ + protected AccessibleJTextArea() + { + super(); + } + + /** + * Returns the accessible state of this <code>AccessibleJTextArea</code>. + * + * @return the accessible state of this <code>AccessibleJTextArea</code> + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet state = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added here to the super's state. + return state; + } + } + + /** * Compatible with Sun's JDK */ private static final long serialVersionUID = -6141680179310439825L; @@ -208,6 +239,8 @@ public class JTextArea extends JTextComponent /* This shouldn't happen in theory -- but, if it does... */ throw new RuntimeException("Unexpected exception occurred.", exception); } + if (toAppend != null && toAppend.length() > 0) + revalidate(); } /** @@ -312,8 +345,12 @@ public class JTextArea extends JTextComponent { if (columns < 0) throw new IllegalArgumentException(); - - this.columns = columns; + + if (columns != this.columns) + { + this.columns = columns; + revalidate(); + } } /** @@ -337,8 +374,12 @@ public class JTextArea extends JTextComponent { if (rows < 0) throw new IllegalArgumentException(); - - this.rows = rows; + + if (rows != this.rows) + { + this.rows = rows; + revalidate(); + } } /** @@ -547,4 +588,16 @@ public class JTextArea extends JTextComponent return new Dimension(Math.max(reqWidth, neededWidth), Math.max(reqHeight, neededHeight)); } + + /** + * Returns the accessible context associated with the <code>JTextArea</code>. + * + * @return the accessible context associated with the <code>JTextArea</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJTextArea(); + return accessibleContext; + } } diff --git a/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java index 5ae9c9f1a82..8dc2f256914 100644 --- a/libjava/classpath/javax/swing/JTextField.java +++ b/libjava/classpath/javax/swing/JTextField.java @@ -46,9 +46,8 @@ import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleStateSet; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; @@ -69,15 +68,19 @@ public class JTextField extends JTextComponent */ protected AccessibleJTextField() { + super(); } /** - * getAccessibleStateSet - * @return AccessibleStateSet + * Returns the accessible state of this <code>AccessibleJTextField</code>. + * + * @return the accessible state of this <code>AccessibleJTextField</code> */ public AccessibleStateSet getAccessibleStateSet() { - return null; + AccessibleStateSet state = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added here to the super's state. + return state; } } @@ -92,17 +95,17 @@ public class JTextField extends JTextComponent public static final String notifyAction = "notify-field-accept"; static - { - actions = new Action[1]; - actions[0] = new TextAction(notifyAction) + { + actions = new Action[1]; + actions[0] = new TextAction(notifyAction) { - public void actionPerformed(ActionEvent event) - { - JTextField textField = (JTextField) event.getSource(); - textField.fireActionPerformed(); - } + public void actionPerformed(ActionEvent event) + { + JTextField textField = (JTextField) event.getSource(); + textField.fireActionPerformed(); + } }; - } + } private int columns; private int align; @@ -117,6 +120,11 @@ public class JTextField extends JTextComponent private PropertyChangeListener actionPropertyChangeListener; /** + * The horizontal visibility of the textfield. + */ + private BoundedRangeModel horizontalVisibility; + + /** * Creates a new instance of <code>JTextField</code>. */ public JTextField() @@ -172,9 +180,9 @@ public class JTextField extends JTextComponent { if (columns < 0) throw new IllegalArgumentException(); - + this.columns = columns; - + setDocument(doc == null ? createDefaultModel() : doc); if (text != null) @@ -182,6 +190,9 @@ public class JTextField extends JTextComponent // default value for alignment align = LEADING; + + // Initialize the horizontal visibility model. + horizontalVisibility = new DefaultBoundedRangeModel(); } /** @@ -192,15 +203,9 @@ public class JTextField extends JTextComponent */ protected Document createDefaultModel() { - // subclassed to swallow newlines - return new PlainDocument() { - public void insertString(int offset, String str, AttributeSet a) - throws BadLocationException - { - if (str != null && str.indexOf('\n') == -1) - super.insertString(offset, str, a); - } - }; + PlainDocument doc = new PlainDocument(); + doc.putProperty("filterNewlines", Boolean.TRUE); + return doc; } /** @@ -268,6 +273,11 @@ public class JTextField extends JTextComponent return columns; } + /** + * Sets the number of columns and then invalidates the layout. + * @param columns the number of columns + * @throws IllegalArgumentException if columns < 0 + */ public void setColumns(int columns) { if (columns < 0) @@ -275,16 +285,31 @@ public class JTextField extends JTextComponent this.columns = columns; invalidate(); + //FIXME: do we need this repaint call? repaint(); } + /** + * Returns the horizontal alignment, which is one of: JTextField.LEFT, + * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING, + * JTextField.TRAILING. + * @return the horizontal alignment + */ public int getHorizontalAlignment() { return align; } + /** + * Sets the horizontal alignment of the text. Calls invalidate and repaint + * and fires a property change event. + * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER, + * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING. + * @throws IllegalArgumentException if newAlign is not one of the above. + */ public void setHorizontalAlignment(int newAlign) { + //FIXME: should throw an IllegalArgumentException if newAlign is invalid if (align == newAlign) return; @@ -295,12 +320,20 @@ public class JTextField extends JTextComponent repaint(); } + /** + * Sets the current font and revalidates so the font will take effect. + */ public void setFont(Font newFont) { super.setFont(newFont); revalidate(); } + /** + * Returns the preferred size. If there is a non-zero number of columns, + * this is the number of columns multiplied by the column width, otherwise + * it returns super.getPreferredSize(). + */ public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); @@ -318,6 +351,7 @@ public class JTextField extends JTextComponent */ public int getScrollOffset() { + //FIXME: this should return horizontalVisibility's value return scrollOffset; } @@ -328,9 +362,15 @@ public class JTextField extends JTextComponent */ public void setScrollOffset(int offset) { + //FIXME: this should actualy scroll the field if needed scrollOffset = offset; } + /** + * Returns the set of Actions that are commands for the editor. + * This is the actions supported by this editor plus the actions + * of the UI (returned by JTextComponent.getActions()). + */ public Action[] getActions() { return TextAction.augmentList(super.getActions(), actions); @@ -364,26 +404,27 @@ public class JTextField extends JTextComponent if (action != null) { - removeActionListener(action); - action.removePropertyChangeListener(actionPropertyChangeListener); - actionPropertyChangeListener = null; + removeActionListener(action); + action.removePropertyChangeListener(actionPropertyChangeListener); + actionPropertyChangeListener = null; } - + Action oldAction = action; action = newAction; if (action != null) { - addActionListener(action); - actionPropertyChangeListener = - createActionPropertyChangeListener(action); - action.addPropertyChangeListener(actionPropertyChangeListener); + addActionListener(action); + actionPropertyChangeListener = createActionPropertyChangeListener(action); + action.addPropertyChangeListener(actionPropertyChangeListener); } - + + //FIXME: is this a hack? The horizontal alignment hasn't changed firePropertyChange("horizontalAlignment", oldAction, newAction); } /** + * Sets the command string used in action events. * @since 1.3 */ public void setActionCommand(String command) @@ -397,45 +438,79 @@ public class JTextField extends JTextComponent protected PropertyChangeListener createActionPropertyChangeListener(Action action) { return new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent event) { - public void propertyChange(PropertyChangeEvent event) - { - // Update properties "action" and "horizontalAlignment". - String name = event.getPropertyName(); - - if (name.equals("enabled")) - { - boolean enabled = ((Boolean) event.getNewValue()).booleanValue(); - JTextField.this.setEnabled(enabled); - } - else if (name.equals(Action.SHORT_DESCRIPTION)) - { - JTextField.this.setToolTipText((String) event.getNewValue()); - } - } - }; + // Update properties "action" and "horizontalAlignment". + String name = event.getPropertyName(); + + if (name.equals("enabled")) + { + boolean enabled = ((Boolean) event.getNewValue()).booleanValue(); + JTextField.this.setEnabled(enabled); + } + else if (name.equals(Action.SHORT_DESCRIPTION)) + { + JTextField.this.setToolTipText((String) event.getNewValue()); + } + } + }; } /** + * * @since 1.3 */ protected void configurePropertiesFromAction(Action action) { if (action != null) { - setEnabled(action.isEnabled()); - setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION)); + setEnabled(action.isEnabled()); + setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION)); } else { - setEnabled(true); - setToolTipText(null); + setEnabled(true); + setToolTipText(null); } } + /** + * Returns the column width, which is the width of the character m + * for the font in use. + * @return the width of the character m for the font in use. + */ protected int getColumnWidth() { FontMetrics metrics = getToolkit().getFontMetrics(getFont()); return metrics.charWidth('m'); } + + /** + * Returns the accessible context associated with the <code>JTextField</code>. + * + * @return the accessible context associated with the <code>JTextField</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJTextField(); + return accessibleContext; + } + + /** + * Returns the bounded range model that describes the horizontal visibility + * of the text field in the case when the text does not fit into the + * available space. The actual values of this model are managed by the look + * and feel implementation. + * + * @return the bounded range model that describes the horizontal visibility + */ + public BoundedRangeModel getHorizontalVisibility() + { + // TODO: The real implementation of this property is still missing. + // However, this is not done in JTextField but must instead be handled in + // javax.swing.text.FieldView. + return horizontalVisibility; + } } diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java index 80632fff38e..1f5b99e43c5 100644 --- a/libjava/classpath/javax/swing/JTextPane.java +++ b/libjava/classpath/javax/swing/JTextPane.java @@ -39,8 +39,6 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.io.IOException; -import java.io.ObjectOutputStream; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -49,7 +47,6 @@ import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.Element; import javax.swing.text.MutableAttributeSet; -import javax.swing.text.SimpleAttributeSet; import javax.swing.text.Style; import javax.swing.text.StyledDocument; import javax.swing.text.StyledEditorKit; @@ -106,7 +103,7 @@ public class JTextPane * @throws IllegalArgumentException if <code>document</code> is not an * instance of <code>StyledDocument</code> * - * @see {@link #setStyledDocument} + * @see #setStyledDocument */ public void setDocument(Document document) { @@ -120,7 +117,7 @@ public class JTextPane /** * Returns the {@link StyledDocument} that is the content model for * this <code>JTextPane</code>. This is a typed wrapper for - * {@link #getDocument}. + * {@link #getDocument()}. * * @return the content model of this <code>JTextPane</code> */ @@ -179,8 +176,6 @@ public class JTextPane doc.setCharacterAttributes(start, contentLength, getInputAttributes(), true); - // Set dot to new position. - setCaretPosition(start + contentLength); } catch (BadLocationException e) { @@ -300,7 +295,7 @@ public class JTextPane * @param replace if <code>true</code>, the attributes of the current * selection are overridden, otherwise they are merged * - * @see {@link #getInputAttributes} + * @see #getInputAttributes */ public void setCharacterAttributes(AttributeSet attribute, boolean replace) diff --git a/libjava/classpath/javax/swing/JToggleButton.java b/libjava/classpath/javax/swing/JToggleButton.java index 25d67f59e4f..077e5adb887 100644 --- a/libjava/classpath/javax/swing/JToggleButton.java +++ b/libjava/classpath/javax/swing/JToggleButton.java @@ -129,7 +129,7 @@ public class JToggleButton extends AbstractButton implements Accessible * Compatible with Sun's JDK. */ private static final long serialVersionUID = -1589950750899943974L; - + /** * Sets the pressed state of the button. The selected state * of the button also changes follwing the button being pressed. @@ -174,8 +174,27 @@ public class JToggleButton extends AbstractButton implements Accessible ActionEvent.ACTION_PERFORMED, actionCommand)); } - } + + /** + * Checks if the button is selected. + * + * @returns true if the button is selected + */ + public boolean isSelected() + { + return super.isSelected(); + } + + /** + * Sets the selected state of the button. + * + * @param b true if button is selected + */ + public void setSelected(boolean b) + { + super.setSelected(b); + } } /** @@ -276,6 +295,7 @@ public class JToggleButton extends AbstractButton implements Accessible setModel(new ToggleButtonModel()); model.setSelected(selected); + setAlignmentX(LEFT_ALIGNMENT); } /** diff --git a/libjava/classpath/javax/swing/JToolBar.java b/libjava/classpath/javax/swing/JToolBar.java index 649919e0618..a508ee6d8e7 100644 --- a/libjava/classpath/javax/swing/JToolBar.java +++ b/libjava/classpath/javax/swing/JToolBar.java @@ -68,6 +68,8 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible /** * AccessibleJToolBar */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJToolBar extends AccessibleJComponent { /** DOCUMENT ME! */ @@ -78,6 +80,7 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible */ protected AccessibleJToolBar() { + // Nothing to do here. } /** @@ -749,7 +752,6 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible { AbstractButton b = (AbstractButton) component; b.setRolloverEnabled(rollover); - b.updateUI(); } } // addImpl() diff --git a/libjava/classpath/javax/swing/JToolTip.java b/libjava/classpath/javax/swing/JToolTip.java index 8d774782780..6bc3e3fa287 100644 --- a/libjava/classpath/javax/swing/JToolTip.java +++ b/libjava/classpath/javax/swing/JToolTip.java @@ -58,6 +58,8 @@ public class JToolTip extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJToolTip extends AccessibleJComponent { private static final long serialVersionUID = -6222548177795408476L; @@ -67,6 +69,7 @@ public class JToolTip extends JComponent implements Accessible */ protected AccessibleJToolTip() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index bb24c7a459d..4422a193396 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/javax/swing/JTree.java @@ -37,16 +37,32 @@ exception statement from your version. */ package javax.swing; +import java.awt.Color; +import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; +import java.util.Locale; import java.util.Vector; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleComponent; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; +import javax.accessibility.AccessibleValue; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeModelEvent; @@ -57,7 +73,6 @@ import javax.swing.event.TreeWillExpandListener; import javax.swing.plaf.TreeUI; import javax.swing.text.Position; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.ExpandVetoException; @@ -68,1754 +83,2830 @@ import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -public class JTree - extends JComponent - implements Scrollable, Accessible +public class JTree extends JComponent implements Scrollable, Accessible { - /** - * Listens to the model of the JTree and updates the property - * <code>expandedState</code> if nodes are removed or changed. - */ - protected class TreeModelHandler - implements - TreeModelListener - { - - /** - * Creates a new instance of TreeModelHandler. - */ - protected TreeModelHandler() - { - } - - /** - * Notifies when a node has changed in some ways. This does not include - * that a node has changed its location or changed it's children. It - * only means that some attributes of the node have changed that might - * affect its presentation. - * - * This method is called after the actual change occured. - * - * @param ev the TreeModelEvent describing the change - */ - public void treeNodesChanged(TreeModelEvent ev) - { - // nothing to do here - } - - /** - * Notifies when a node is inserted into the tree. - * - * This method is called after the actual change occured. - * - * @param ev the TreeModelEvent describing the change - */ - public void treeNodesInserted(TreeModelEvent ev) - { - // nothing to do here - } - - /** - * Notifies when a node is removed from the tree. - * - * This method is called after the actual change occured. - * - * @param ev the TreeModelEvent describing the change - */ - public void treeNodesRemoved(TreeModelEvent ev) - { - // TODO: The API docs suggest that this method should do something - // but I cannot really see what has to be done here ... - } - - /** - * Notifies when the structure of the tree is changed. - * - * This method is called after the actual change occured. - * - * @param ev the TreeModelEvent describing the change - */ - public void treeStructureChanged(TreeModelEvent ev) - { - // set state of new path - TreePath path = ev.getTreePath(); - setExpandedState(path, isExpanded(path)); - } - } // TreeModelHandler - - /** - * This redirects TreeSelectionEvents and rewrites the source of it to be - * this JTree. This is typically done when the tree model generates an - * event, but the JTree object associated with that model should be listed - * as the actual source of the event. - */ - protected class TreeSelectionRedirector - implements - TreeSelectionListener, - Serializable - { - /** The serial version UID. */ - private static final long serialVersionUID = -3505069663646241664L; - - /** - * Creates a new instance of TreeSelectionRedirector - */ - protected TreeSelectionRedirector() - { - } - - /** - * Notifies when the tree selection changes. - * - * @param ev the TreeSelectionEvent that describes the change - */ - public void valueChanged(TreeSelectionEvent ev) - { - TreeSelectionEvent rewritten = (TreeSelectionEvent) ev - .cloneWithSource(JTree.this); - fireValueChanged(rewritten); - JTree.this.repaint(); - } - } // TreeSelectionRedirector - - /** - * A TreeModel that does not allow anything to be selected. + + /** + * This class implements accessibility support for the JTree class. It + * provides an implementation of the Java Accessibility API appropriate + * to tree user-interface elements. + */ + protected class AccessibleJTree extends JComponent.AccessibleJComponent + implements AccessibleSelection, TreeSelectionListener, TreeModelListener, + TreeExpansionListener + { + + /** + * This class implements accessibility support for the JTree child. It provides + * an implementation of the Java Accessibility API appropriate to tree nodes. + */ + protected class AccessibleJTreeNode extends AccessibleContext + implements Accessible, AccessibleComponent, AccessibleSelection, + AccessibleAction + { + + private JTree tree; + private TreePath tp; + private Accessible acc; + private AccessibleStateSet states; + private Vector selectionList; + private Vector actionList; + private TreeModel mod; + private Cursor cursor; + + /** + * Constructs an AccessibleJTreeNode + * + * @param t - the current tree + * @param p - the current path to be dealt with + * @param ap - the accessible object to use + */ + public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) + { + states = new AccessibleStateSet(); + selectionList = new Vector(); + actionList = new Vector(); + mod = tree.getModel(); + cursor = JTree.this.getCursor(); + + tree = t; + tp = p; + acc = ap; + + // Add all the children of this path that may already be + // selected to the selection list. + TreePath[] selected = tree.getSelectionPaths(); + for (int i = 0; i < selected.length; i++) + { + TreePath sel = selected[i]; + if ((sel.getParentPath()).equals(tp)) + selectionList.add(sel); + } + + // Add all the actions available for a node to + // the action list. + actionList.add("EXPAND"); + actionList.add("COLLAPSE"); + actionList.add("EDIT"); + actionList.add("SELECT"); + actionList.add("DESELECT"); + } + + /** + * Adds the specified selected item in the object to the object's + * selection. + * + * @param i - the i-th child of this node. + */ + public void addAccessibleSelection(int i) + { + if (mod != null) + { + Object child = mod.getChild(tp.getLastPathComponent(), i); + if (child != null) + { + if (!states.contains(AccessibleState.MULTISELECTABLE)) + clearAccessibleSelection(); + selectionList.add(child); + tree.addSelectionPath(tp.pathByAddingChild(child)); + } + } + } + + /** + * Adds the specified focus listener to receive focus events + * from this component. + * + * @param l - the new focus listener + */ + public void addFocusListener(FocusListener l) + { + tree.addFocusListener(l); + } + + /** + * Add a PropertyChangeListener to the listener list. + * + * @param l - the new property change listener + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + // Nothing to do here. + } + + /** + * Clears the selection in the object, so that nothing in the + * object is selected. + */ + public void clearAccessibleSelection() + { + selectionList.clear(); + } + + /** + * Checks whether the specified point is within this object's + * bounds, where the point's x and y coordinates are defined to be + * relative to the coordinate system of the object. + * + * @param p - the point to check + * @return true if p is in the bounds + */ + public boolean contains(Point p) + { + return getBounds().contains(p); + } + + /** + * Perform the specified Action on the tree node. + * + * @param i - the i-th action to perform + * @return true if the the action was performed; else false. + */ + public boolean doAccessibleAction(int i) + { + if (i >= actionList.size() || i < 0) + return false; + + if (actionList.get(i).equals("EXPAND")) + tree.expandPath(tp); + else if (actionList.get(i).equals("COLLAPSE")) + tree.collapsePath(tp); + else if (actionList.get(i).equals("SELECT")) + tree.addSelectionPath(tp); + else if (actionList.get(i).equals("DESELECT")) + tree.removeSelectionPath(tp); + else if (actionList.get(i).equals("EDIT")) + tree.startEditingAtPath(tp); + else + return false; + return true; + } + + /** + * Get the AccessibleAction associated with this object. + * + * @return the action + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns the number of accessible actions available in this tree node. + * + * @return the number of actions + */ + public int getAccessibleActionCount() + { + return actionList.size(); + } + + /** + * Return a description of the specified action of the tree node. + * + * @param i - the i-th action's description + * @return a description of the action + */ + public String getAccessibleActionDescription(int i) + { + if (i < 0 || i >= actionList.size()) + return (actionList.get(i)).toString(); + return super.getAccessibleDescription(); + } + + /** + * Returns the Accessible child, if one exists, contained at the + * local coordinate Point. + * + * @param p - the point of the accessible + * @return the accessible at point p if it exists + */ + public Accessible getAccessibleAt(Point p) + { + TreePath acc = tree.getClosestPathForLocation(p.x, p.y); + if (acc != null) + return new AccessibleJTreeNode(tree, acc, this); + return null; + } + + /** + * Return the specified Accessible child of the object. + * + * @param i - the i-th child of the current path + * @return the child if it exists + */ + public Accessible getAccessibleChild(int i) + { + if (mod != null) + { + Object child = mod.getChild(tp.getLastPathComponent(), i); + if (child != null) + return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child), + acc); + } + return null; + } + + /** + * Returns the number of accessible children in the object. + * + * @return the number of children the current node has + */ + public int getAccessibleChildrenCount() + { + TreeModel mod = getModel(); + if (mod != null) + return mod.getChildCount(tp.getLastPathComponent()); + return 0; + } + + /** + * Get the AccessibleComponent associated with this object. + * + * @return the accessible component if it is supported. + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Get the AccessibleContext associated with this tree node. + * + * @return an instance of this class + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Get the accessible description of this object. + * + * @return the accessible description + */ + public String getAccessibleDescription() + { + return super.getAccessibleDescription(); + } + + /** + * Get the index of this object in its accessible parent. + * + * @return the index of this in the parent. + */ + public int getAccessibleIndexInParent() + { + AccessibleContext parent = getAccessibleParent().getAccessibleContext(); + if (parent != null) + for (int i = 0; i < parent.getAccessibleChildrenCount(); i++) + { + if ((parent.getAccessibleChild(i)).equals(this)) + return i; + } + return -1; + } + + /** + * Get the accessible name of this object. + * + * @return the accessible name + */ + public String getAccessibleName() + { + return super.getAccessibleName(); + } + + /** + * Get the Accessible parent of this object. + * + * @return the accessible parent if it exists. + */ + public Accessible getAccessibleParent() + { + return super.getAccessibleParent(); + } + + /** + * Get the role of this object. + * + * @return the accessible role + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleJTree.this.getAccessibleRole(); + } + + /** + * Get the AccessibleSelection associated with this object if one exists. + * + * @return the accessible selection for this. + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Returns an Accessible representing the specified selected item + * in the object. + * + * @return the accessible representing a certain selected item. + */ + public Accessible getAccessibleSelection(int i) + { + if (i > 0 && i < getAccessibleSelectionCount()) + return new AccessibleJTreeNode(tree, + tp.pathByAddingChild(selectionList.get(i)), acc); + return null; + } + + /** + * Returns the number of items currently selected. + * + * @return the number of items selected. + */ + public int getAccessibleSelectionCount() + { + return selectionList.size(); + } + + /** + * Get the state set of this object. + * + * @return the state set for this object + */ + public AccessibleStateSet getAccessibleStateSet() + { + if (isVisible()) + states.add(AccessibleState.VISIBLE); + if (tree.isCollapsed(tp)) + states.add(AccessibleState.COLLAPSED); + if (tree.isEditable()) + states.add(AccessibleState.EDITABLE); + if (mod != null && + !mod.isLeaf(tp.getLastPathComponent())) + states.add(AccessibleState.EXPANDABLE); + if (tree.isExpanded(tp)) + states.add(AccessibleState.EXPANDED); + if (isFocusable()) + states.add(AccessibleState.FOCUSABLE); + if (hasFocus()) + states.add(AccessibleState.FOCUSED); + if (tree.getSelectionModel().getSelectionMode() != + TreeSelectionModel.SINGLE_TREE_SELECTION) + states.add(AccessibleState.MULTISELECTABLE); + if (tree.isOpaque()) + states.add(AccessibleState.OPAQUE); + if (tree.isPathSelected(tp)) + states.add(AccessibleState.SELECTED); + if (isShowing()) + states.add(AccessibleState.SHOWING); + + states.add(AccessibleState.SELECTABLE); + return states; + } + + /** + * Get the AccessibleText associated with this object if one exists. + * + * @return the accessible text + */ + public AccessibleText getAccessibleText() + { + return super.getAccessibleText(); + } + + /** + * Get the AccessibleValue associated with this object if one exists. + * + * @return the accessible value if it exists + */ + public AccessibleValue getAccessibleValue() + { + return super.getAccessibleValue(); + } + + /** + * Get the background color of this object. + * + * @return the color of the background. + */ + public Color getBackground() + { + return tree.getBackground(); + } + + /** + * Gets the bounds of this object in the form of a Rectangle object. + * + * @return the bounds of the current node. + */ + public Rectangle getBounds() + { + return tree.getPathBounds(tp); + } + + /** + * Gets the Cursor of this object. + * + * @return the cursor for the current node + */ + public Cursor getCursor() + { + return cursor; + } + + /** + * Gets the Font of this object. + * + * @return the font for the current node + */ + public Font getFont() + { + return tree.getFont(); + } + + /** + * Gets the FontMetrics of this object. + * + * @param f - the current font. + * @return the font metrics for the given font. + */ + public FontMetrics getFontMetrics(Font f) + { + return tree.getFontMetrics(f); + } + + /** + * Get the foreground color of this object. + * + * @return the foreground for this object. + */ + public Color getForeground() + { + return tree.getForeground(); + } + + /** + * Gets the locale of the component. + * + * @return the locale of the component. + */ + public Locale getLocale() + { + return tree.getLocale(); + } + + /** + * Gets the location of the object relative to the + * parent in the form of a point specifying the object's + * top-left corner in the screen's coordinate space. + * + * @return the location of the current node. + */ + public Point getLocation() + { + return getLocationInJTree(); + } + + /** + * Returns the location in the tree. + * + * @return the location in the JTree. + */ + protected Point getLocationInJTree() + { + Rectangle bounds = tree.getPathBounds(tp); + return new Point(bounds.x, bounds.y); + } + + /** + * Returns the location of the object on the screen. + * + * @return the location of the object on the screen. + */ + public Point getLocationOnScreen() + { + Point loc = getLocation(); + SwingUtilities.convertPointToScreen(loc, tree); + return loc; + } + + /** + * Returns the size of this object in the form of a Dimension object. + * + * @return the size of the object + */ + public Dimension getSize() + { + Rectangle b = getBounds(); + return b.getSize(); + } + + /** + * Returns true if the current child of this object is selected. + * + * @param i - the child of the current node + * @return true if the child is selected. + */ + public boolean isAccessibleChildSelected(int i) + { + Object child = mod.getChild(tp.getLastPathComponent(), i); + if (child != null) + return tree.isPathSelected(tp.pathByAddingChild(child)); + return false; + } + + /** + * Determines if the object is enabled. + * + * @return true if the tree is enabled + */ + public boolean isEnabled() + { + return tree.isEnabled(); + } + + /** + * Returns whether this object can accept focus or not. + * + * @return true, it is always focus traversable + */ + public boolean isFocusTraversable() + { + return true; + } + + /** + * Determines if the object is showing. + * + * @return true if the object is visible and the + * parent is visible. + */ + public boolean isShowing() + { + return isVisible() && tree.isShowing(); + } + + /** + * Determines if the object is visible. + * + * @return true if the object is visible. + */ + public boolean isVisible() + { + return tree.isVisible(tp); + } + + /** + * Removes the specified selected item in the object from the + * object's selection. + * + * @param i - the specified item to remove + */ + public void removeAccessibleSelection(int i) + { + if (mod != null) + { + Object child = mod.getChild(tp.getLastPathComponent(), i); + if (child != null) + { + if (!states.contains(AccessibleState.MULTISELECTABLE)) + clearAccessibleSelection(); + if (selectionList.contains(child)) + { + selectionList.remove(child); + tree.removeSelectionPath(tp.pathByAddingChild(child)); + } + } + } + } + + /** + * Removes the specified focus listener so it no longer receives focus + * events from this component. + * + * @param l - the focus listener to remove + */ + public void removeFocusListener(FocusListener l) + { + tree.removeFocusListener(l); + } + + /** + * Remove a PropertyChangeListener from the listener list. + * + * @param l - the property change listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + // Nothing to do here. + } + + /** + * Requests focus for this object. + */ + public void requestFocus() + { + tree.requestFocus(); + } + + /** + * Causes every selected item in the object to be selected if the object + * supports multiple selections. + */ + public void selectAllAccessibleSelection() + { + Object parent = tp.getLastPathComponent(); + if (mod != null) + { + for (int i = 0; i < mod.getChildCount(parent); i++) + { + Object child = mod.getChild(parent, i); + if (child != null) + { + if (!states.contains(AccessibleState.MULTISELECTABLE)) + clearAccessibleSelection(); + if (selectionList.contains(child)) + { + selectionList.add(child); + tree.addSelectionPath(tp.pathByAddingChild(child)); + } + } + } + } + } + + /** + * Set the accessible description of this object. + * + * @param s - the string to set the accessible description to. + */ + public void setAccessibleDescription(String s) + { + super.setAccessibleDescription(s); + } + + /** + * Set the localized accessible name of this object. + * + * @param s - the string to set the accessible name to. + */ + public void setAccessibleName(String s) + { + super.setAccessibleName(s); + } + + /** + * Set the background color of this object. + * + * @param c - the color to set the background to. + */ + public void setBackground(Color c) + { + // Nothing to do here. + } + + /** + * Sets the bounds of this object in the form of a Rectangle object. + * + * @param r - the bounds to set the object o + */ + public void setBounds(Rectangle r) + { + // Nothing to do here. + } + + /** + * Sets the Cursor of this object. + * + * @param c - the new cursor + */ + public void setCursor(Cursor c) + { + cursor = c; + } + + /** + * Sets the enabled state of the object. + * + * @param b - boolean to enable or disable object + */ + public void setEnabled(boolean b) + { + // Nothing to do here. + } + + /** + * Sets the Font of this object. + * + * @param f - the new font. + */ + public void setFont(Font f) + { + // Nothing to do here. + } + + /** + * Sets the foreground color of this object. + * + * @param c - the new foreground color. + */ + public void setForeground(Color c) + { + // Nothing to do here. + } + + /** + * Sets the location of the object relative to the parent. + * + * @param p - the new location for the object. + */ + public void setLocation(Point p) + { + // Nothing to do here. + } + + /** + * Resizes this object so that it has width and height. + * + * @param d - the new size for the object. + */ + public void setSize(Dimension d) + { + // Nothing to do here. + } + + /** + * Sets the visible state of the object. + * + * @param b - sets the objects visibility. + */ + public void setVisible(boolean b) + { + // Nothing to do here. + } + } + + /** + * Constructor + */ + public AccessibleJTree() + { + // Nothing to do here. + } + + /** + * Adds the specified selected item in the object to the object's selection. + * + * @param i - the row to add to the tree's selection + */ + public void addAccessibleSelection(int i) + { + addSelectionInterval(i, i); + } + + /** + * Clears the selection in the object, so that nothing in the object is selected. + */ + public void clearAccessibleSelection() + { + clearSelection(); + } + + /** + * Fire a visible data property change notification. + */ + public void fireVisibleDataPropertyChange() + { + treeDidChange(); + } + + /** + * Returns the Accessible child, if one exists, contained at the local + * coordinate Point. + * + * @param p - the point of the accessible to get. + * @return the accessible at point p. + */ + public Accessible getAccessibleAt(Point p) + { + TreePath tp = getClosestPathForLocation(p.x, p.y); + if (tp != null) + return new AccessibleJTreeNode(JTree.this, tp, null); + return null; + } + + /** + * Return the nth Accessible child of the object. + * + * @param i - the accessible child to get + * @return the i-th child + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the number of top-level children nodes of this JTree. + * + * @return the number of top-level children + */ + public int getAccessibleChildrenCount() + { + TreeModel model = getModel(); + if (model != null) + return model.getChildCount(model.getRoot()); + return 0; + } + + /** + * Get the index of this object in its accessible parent. + * + * @return the index of this object. + */ + public int getAccessibleIndexInParent() + { + return 0; + } + + /** + * Get the role of this object. + * + * @return the role of this object + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TREE; + } + + /** + * Get the AccessibleSelection associated with this object. + * + * @return the accessible selection of the tree + */ + public AccessibleSelection getAccessibleSelection() + { + TreeModel mod = getModel(); + if (mod != null) + return (new AccessibleJTreeNode(JTree.this, + new TreePath(mod.getRoot()), null)).getAccessibleSelection(); + return null; + } + + /** + * Returns an Accessible representing the specified selected item in the object. + * + * @return the i-th accessible in the selection + */ + public Accessible getAccessibleSelection(int i) + { + TreeModel mod = getModel(); + if (mod != null) + return (new AccessibleJTreeNode(JTree.this, + new TreePath(mod.getRoot()), null)).getAccessibleSelection(i); + return null; + } + + /** + * Returns the number of items currently selected. + * + * @return the number of selected accessibles. + */ + public int getAccessibleSelectionCount() + { + return getSelectionCount(); + } + + /** + * Returns true if the current child of this object is selected. + * + * @param i - the child of this object + * @return true if the i-th child is selected. + */ + public boolean isAccessibleChildSelected(int i) + { + // Nothing to do here. + return false; + } + + /** + * Removes the specified selected item in the object from the object's + * selection. + * + * @param i - the i-th selected item to remove + */ + public void removeAccessibleSelection(int i) + { + removeSelectionInterval(i, i); + } + + /** + * Causes every selected item in the object to be selected if the object + * supports multiple selections. + */ + public void selectAllAccessibleSelection() + { + if (getSelectionModel().getSelectionMode() != + TreeSelectionModel.SINGLE_TREE_SELECTION) + addSelectionInterval(0, getVisibleRowCount()); + } + + /** + * Tree Collapsed notification + * + * @param e - the event + */ + public void treeCollapsed(TreeExpansionEvent e) + { + fireTreeCollapsed(e.getPath()); + } + + /** + * Tree Model Expansion notification. + * + * @param e - the event + */ + public void treeExpanded(TreeExpansionEvent e) + { + fireTreeExpanded(e.getPath()); + } + + /** + * Tree Model Node change notification. + * + * @param e - the event + */ + public void treeNodesChanged(TreeModelEvent e) + { + // Nothing to do here. + } + + /** + * Tree Model Node change notification. + * + * @param e - the event + */ + public void treeNodesInserted(TreeModelEvent e) + { + // Nothing to do here. + } + + /** + * Tree Model Node change notification. + * + * @param e - the event + */ + public void treeNodesRemoved(TreeModelEvent e) + { + // Nothing to do here. + } + + /** + * Tree Model structure change change notification. + * + * @param e - the event + */ + public void treeStructureChanged(TreeModelEvent e) + { + // Nothing to do here. + } + + /** + * Tree Selection Listener value change method. + * + * @param e - the event + */ + public void valueChanged(TreeSelectionEvent e) + { + fireValueChanged(e); + } + } + + public static class DynamicUtilTreeNode extends DefaultMutableTreeNode + { + protected Object childValue; + + protected boolean loadedChildren; + + /** + * Currently not set or used by this class. It might be set and used in + * later versions of this class. + */ + protected boolean hasChildren; + + public DynamicUtilTreeNode(Object value, Object children) + { + super(value); + childValue = children; + loadedChildren = false; + } + + public int getChildCount() + { + loadChildren(); + return super.getChildCount(); + } + + protected void loadChildren() + { + if (!loadedChildren) + { + createChildren(this, childValue); + loadedChildren = true; + } + } + + public Enumeration children() + { + loadChildren(); + return super.children(); + } + + /** + * Returns the child node at position <code>pos</code>. Subclassed + * here to load the children if necessary. + * + * @param pos the position of the child node to fetch + * + * @return the childnode at the specified position + */ + public TreeNode getChildAt(int pos) + { + loadChildren(); + return super.getChildAt(pos); + } + + public boolean isLeaf() + { + return (childValue == null || !(childValue instanceof Hashtable + || childValue instanceof Vector || childValue.getClass() + .isArray())); + } + + public static void createChildren(DefaultMutableTreeNode parent, + Object children) + { + if (children instanceof Hashtable) + { + Hashtable tab = (Hashtable) children; + Enumeration e = tab.keys(); + while (e.hasMoreElements()) + { + Object key = e.nextElement(); + Object val = tab.get(key); + parent.add(new DynamicUtilTreeNode(key, val)); + } + } + else if (children instanceof Vector) + { + Iterator i = ((Vector) children).iterator(); + while (i.hasNext()) + { + Object n = i.next(); + parent.add(new DynamicUtilTreeNode(n, n)); + } + } + else if (children != null && children.getClass().isArray()) + { + Object[] arr = (Object[]) children; + for (int i = 0; i < arr.length; ++i) + parent.add(new DynamicUtilTreeNode(arr[i], arr[i])); + } + } + } + + /** + * Listens to the model of the JTree and updates the property + * <code>expandedState</code> if nodes are removed or changed. + */ + protected class TreeModelHandler implements TreeModelListener + { + + /** + * Creates a new instance of TreeModelHandler. + */ + protected TreeModelHandler() + { + // Nothing to do here. + } + + /** + * Notifies when a node has changed in some ways. This does not include + * that a node has changed its location or changed it's children. It + * only means that some attributes of the node have changed that might + * affect its presentation. + * + * This method is called after the actual change occured. + * + * @param ev the TreeModelEvent describing the change + */ + public void treeNodesChanged(TreeModelEvent ev) + { + // Nothing to do here. + } + + /** + * Notifies when a node is inserted into the tree. + * + * This method is called after the actual change occured. + * + * @param ev the TreeModelEvent describing the change + */ + public void treeNodesInserted(TreeModelEvent ev) + { + // nothing to do here + } + + /** + * Notifies when a node is removed from the tree. + * + * This method is called after the actual change occured. + * + * @param ev the TreeModelEvent describing the change */ - protected static class EmptySelectionModel - extends - DefaultTreeSelectionModel - { - /** The serial version UID. */ - private static final long serialVersionUID = -5815023306225701477L; - - /** - * The shared instance of this model. - */ - protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel(); - - /** - * Creates a new instance of EmptySelectionModel. - */ - protected EmptySelectionModel() - { - } - - /** - * Returns the shared instance of EmptySelectionModel. - * - * @return the shared instance of EmptySelectionModel - */ - public static EmptySelectionModel sharedInstance() - { - return sharedInstance; - } - - /** - * This catches attempts to set a selection and sets nothing instead. - * - * @param paths not used here - */ - public void setSelectionPaths(TreePath[] paths) - { - // we don't allow selections in this class - } - - /** - * This catches attempts to add something to the selection. - * - * @param paths not used here - */ - public void addSelectionPaths(TreePath[] paths) - { - // we don't allow selections in this class - } - - /** - * This catches attempts to remove something from the selection. - * - * @param paths not used here - */ - public void removeSelectionPaths(TreePath[] paths) - { - // we don't allow selections in this class - } - }// EmptySelectionModel - - private static final long serialVersionUID = 7559816092864483649L; - public static final String CELL_EDITOR_PROPERTY = "cellEditor"; - public static final String CELL_RENDERER_PROPERTY = "cellRenderer"; - public static final String EDITABLE_PROPERTY = "editable"; - public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing"; - public static final String LARGE_MODEL_PROPERTY = "largeModel"; - public static final String ROOT_VISIBLE_PROPERTY = "rootVisible"; - public static final String ROW_HEIGHT_PROPERTY = "rowHeight"; - public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand"; - public static final String SELECTION_MODEL_PROPERTY = "selectionModel"; - public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles"; - public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount"; - public static final String TREE_MODEL_PROPERTY = "model"; - public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount"; + public void treeNodesRemoved(TreeModelEvent ev) + { + // TODO: The API docs suggest that this method should do something + // but I cannot really see what has to be done here ... + } - /** @since 1.3 */ - public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath"; + /** + * Notifies when the structure of the tree is changed. + * + * This method is called after the actual change occured. + * + * @param ev the TreeModelEvent describing the change + */ + public void treeStructureChanged(TreeModelEvent ev) + { + // Set state of new path. + TreePath path = ev.getTreePath(); + setExpandedState(path, isExpanded(path)); + } + } - /** @since 1.3 */ - public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; + /** + * This redirects TreeSelectionEvents and rewrites the source of it to be + * this JTree. This is typically done when the tree model generates an + * event, but the JTree object associated with that model should be listed + * as the actual source of the event. + */ + protected class TreeSelectionRedirector implements TreeSelectionListener, + Serializable + { + /** The serial version UID. */ + private static final long serialVersionUID = -3505069663646241664L; + + /** + * Creates a new instance of TreeSelectionRedirector + */ + protected TreeSelectionRedirector() + { + // Nothing to do here. + } + + /** + * Notifies when the tree selection changes. + * + * @param ev the TreeSelectionEvent that describes the change + */ + public void valueChanged(TreeSelectionEvent ev) + { + TreeSelectionEvent rewritten = + (TreeSelectionEvent) ev.cloneWithSource(JTree.this); + fireValueChanged(rewritten); + JTree.this.repaint(); + } + } + + /** + * A TreeModel that does not allow anything to be selected. + */ + protected static class EmptySelectionModel extends DefaultTreeSelectionModel + { + /** The serial version UID. */ + private static final long serialVersionUID = -5815023306225701477L; + + /** + * The shared instance of this model. + */ + protected static final EmptySelectionModel sharedInstance = + new EmptySelectionModel(); + + /** + * Creates a new instance of EmptySelectionModel. + */ + protected EmptySelectionModel() + { + // Nothing to do here. + } + + /** + * Returns the shared instance of EmptySelectionModel. + * + * @return the shared instance of EmptySelectionModel + */ + public static EmptySelectionModel sharedInstance() + { + return sharedInstance; + } + + /** + * This catches attempts to set a selection and sets nothing instead. + * + * @param paths not used here + */ + public void setSelectionPaths(TreePath[] paths) + { + // We don't allow selections in this class. + } + + /** + * This catches attempts to add something to the selection. + * + * @param paths not used here + */ + public void addSelectionPaths(TreePath[] paths) + { + // We don't allow selections in this class. + } + + /** + * This catches attempts to remove something from the selection. + * + * @param paths not used here + */ + public void removeSelectionPaths(TreePath[] paths) + { + // We don't allow selections in this class. + } + } + + private static final long serialVersionUID = 7559816092864483649L; + + public static final String CELL_EDITOR_PROPERTY = "cellEditor"; + + public static final String CELL_RENDERER_PROPERTY = "cellRenderer"; + + public static final String EDITABLE_PROPERTY = "editable"; + + public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = + "invokesStopCellEditing"; + + public static final String LARGE_MODEL_PROPERTY = "largeModel"; + + public static final String ROOT_VISIBLE_PROPERTY = "rootVisible"; + + public static final String ROW_HEIGHT_PROPERTY = "rowHeight"; + + public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand"; + + public static final String SELECTION_MODEL_PROPERTY = "selectionModel"; + + public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles"; + + public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount"; + + public static final String TREE_MODEL_PROPERTY = "model"; + + public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount"; + + /** @since 1.3 */ + public static final String ANCHOR_SELECTION_PATH_PROPERTY = + "anchorSelectionPath"; /** @since 1.3 */ - public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths"; - private static final Object EXPANDED = new Object(); - private static final Object COLLAPSED = new Object(); - private boolean dragEnabled; - private boolean expandsSelectedPaths; - private TreePath anchorSelectionPath; - private TreePath leadSelectionPath; - - /* - * This contains the state of all nodes in the tree. Al/ entries map the - * TreePath of a note to to its state. Valid states are EXPANDED and - * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED. - */ - private Hashtable nodeStates = new Hashtable(); - protected transient TreeCellEditor cellEditor; - protected transient TreeCellRenderer cellRenderer; - protected boolean editable; - protected boolean invokesStopCellEditing; - protected boolean largeModel; - protected boolean rootVisible; - protected int rowHeight; - protected boolean scrollsOnExpand; - protected transient TreeSelectionModel selectionModel; - protected boolean showsRootHandles; - protected int toggleClickCount; - protected transient TreeModel treeModel; - protected int visibleRowCount; - - /** - * Handles TreeModelEvents to update the expandedState. - */ - protected transient TreeModelListener treeModelListener; + public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; - /** - * Redirects TreeSelectionEvents so that the source is this JTree. - */ - protected TreeSelectionRedirector selectionRedirector = - new TreeSelectionRedirector(); + /** @since 1.3 */ + public static final String EXPANDS_SELECTED_PATHS_PROPERTY = + "expandsSelectedPaths"; - /** - * Creates a new <code>JTree</code> object. - */ - public JTree() - { - this(createTreeModel(null)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Hashtable value) - { - this(createTreeModel(value)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Object[] value) - { - this(createTreeModel(value)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param model the model to use - */ - public JTree(TreeModel model) - { - setModel(model); - setSelectionModel(EmptySelectionModel.sharedInstance()); - setCellRenderer(new DefaultTreeCellRenderer()); - updateUI(); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param root the root node - */ - public JTree(TreeNode root) - { - this(root, false); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param root the root node - * @param asksAllowChildren if false, all nodes without children are leaf - * nodes. If true, only nodes that do not allow children are leaf - * nodes. - */ - public JTree(TreeNode root, boolean asksAllowChildren) - { - this(new DefaultTreeModel(root, asksAllowChildren)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Vector value) - { - this(createTreeModel(value)); - } - - public static class DynamicUtilTreeNode - extends - DefaultMutableTreeNode - { - protected Object childValue; - protected boolean loadedChildren; - - /** - * Currently not set or used by this class. It might be set and used in - * later versions of this class. - */ - protected boolean hasChildren; - - public DynamicUtilTreeNode(Object value, Object children) - { - super(value); - childValue = children; - loadedChildren = false; - } - - public int getChildCount() - { - loadChildren(); - return super.getChildCount(); - } - - protected void loadChildren() - { - if (!loadedChildren) - { - createChildren(this, childValue); - loadedChildren = true; - } - } - - public Enumeration children() - { - loadChildren(); - return super.children(); - } - - /** - * Returns the child node at position <code>pos</code>. Subclassed - * here to load the children if necessary. - * - * @param pos the position of the child node to fetch - * - * @return the childnode at the specified position - */ - public TreeNode getChildAt(int pos) - { - loadChildren(); - return super.getChildAt(pos); - } - - public boolean isLeaf() - { - return (childValue == null || !(childValue instanceof Hashtable - || childValue instanceof Vector || childValue.getClass() - .isArray())); - } - - public static void createChildren(DefaultMutableTreeNode parent, - Object children) - { - if (children instanceof Hashtable) - { - Hashtable tab = (Hashtable) children; - Enumeration e = tab.keys(); - while (e.hasMoreElements()) - { - Object key = e.nextElement(); - Object val = tab.get(key); - parent.add(new DynamicUtilTreeNode(key, val)); - } - } else if (children instanceof Vector) - { - Iterator i = ((Vector) children).iterator(); - while (i.hasNext()) - { - Object n = i.next(); - parent.add(new DynamicUtilTreeNode(n, n)); - } - } else if (children != null && children.getClass().isArray()) - { - Object[] arr = (Object[]) children; - for (int i = 0; i < arr.length; ++i) - parent.add(new DynamicUtilTreeNode(arr[i], arr[i])); - } - } - } - - public int getRowForPath(TreePath path) - { - TreeUI ui = getUI(); - - if (ui != null) - return ui.getRowForPath(this, path); - - return -1; - } - - public TreePath getPathForRow(int row) - { - TreeUI ui = getUI(); - return ui != null ? ui.getPathForRow(this, row) : null; - } - - protected TreePath[] getPathBetweenRows(int index0, int index1) - { - TreeUI ui = getUI(); - - if (ui == null) - return null; - - int minIndex = Math.min(index0, index1); - int maxIndex = Math.max(index0, index1); - TreePath[] paths = new TreePath[maxIndex - minIndex + 1]; - - for (int i = minIndex; i <= maxIndex; ++i) - paths[i - minIndex] = ui.getPathForRow(this, i); - - return paths; - } - - /** - * Creates a new <code>TreeModel</code> object. - * - * @param value the values stored in the model - */ - protected static TreeModel createTreeModel(Object value) - { - return new DefaultTreeModel(new DynamicUtilTreeNode(value, value)); - } - - /** - * Return the UI associated with this <code>JTree</code> object. - * - * @return the associated <code>TreeUI</code> object - */ - public TreeUI getUI() - { - return (TreeUI) ui; - } - - /** - * Sets the UI associated with this <code>JTree</code> object. - * - * @param ui the <code>TreeUI</code> to associate - */ - public void setUI(TreeUI ui) - { - super.setUI(ui); - } + private static final Object EXPANDED = new Object(); - /** - * This method resets the UI used to the Look and Feel defaults.. - */ - public void updateUI() - { - setUI((TreeUI) UIManager.getUI(this)); - revalidate(); - repaint(); - } - - /** - * This method returns the String ID of the UI class of Separator. - * - * @return The UI class' String ID. - */ - public String getUIClassID() - { - return "TreeUI"; - } - - /** - * Gets the AccessibleContext associated with this - * <code>JToggleButton</code>. - * - * @return the associated context - */ - public AccessibleContext getAccessibleContext() - { - return null; - } - - /** - * Returns the preferred viewport size. - * - * @return the preferred size - */ - public Dimension getPreferredScrollableViewportSize() - { - return new Dimension (getPreferredSize().width, getVisibleRowCount()*getRowHeight()); - } - - public int getScrollableUnitIncrement(Rectangle visibleRect, - int orientation, int direction) - { - return 1; - } - - public int getScrollableBlockIncrement(Rectangle visibleRect, - int orientation, int direction) - { - return 1; - } + private static final Object COLLAPSED = new Object(); - public boolean getScrollableTracksViewportWidth() + private boolean dragEnabled; + + private boolean expandsSelectedPaths; + + private TreePath anchorSelectionPath; + + private TreePath leadSelectionPath; + + /** + * This contains the state of all nodes in the tree. Al/ entries map the + * TreePath of a note to to its state. Valid states are EXPANDED and + * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED. + */ + private Hashtable nodeStates = new Hashtable(); + + protected transient TreeCellEditor cellEditor; + + protected transient TreeCellRenderer cellRenderer; + + protected boolean editable; + + protected boolean invokesStopCellEditing; + + protected boolean largeModel; + + protected boolean rootVisible; + + protected int rowHeight; + + protected boolean scrollsOnExpand; + + protected transient TreeSelectionModel selectionModel; + + protected boolean showsRootHandles; + + protected int toggleClickCount; + + protected transient TreeModel treeModel; + + protected int visibleRowCount; + + /** + * Handles TreeModelEvents to update the expandedState. + */ + protected transient TreeModelListener treeModelListener; + + /** + * Redirects TreeSelectionEvents so that the source is this JTree. + */ + protected TreeSelectionRedirector selectionRedirector = + new TreeSelectionRedirector(); + + /** + * Creates a new <code>JTree</code> object. + */ + public JTree() + { + this(createTreeModel(null)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param value the initial nodes in the tree + */ + public JTree(Hashtable value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param value the initial nodes in the tree + */ + public JTree(Object[] value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param model the model to use + */ + public JTree(TreeModel model) + { + updateUI(); + setRootVisible(true); + setModel(model); + setSelectionModel(new EmptySelectionModel()); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param root the root node + */ + public JTree(TreeNode root) + { + this(root, false); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param root the root node + * @param asksAllowChildren if false, all nodes without children are leaf + * nodes. If true, only nodes that do not allow children are leaf + * nodes. + */ + public JTree(TreeNode root, boolean asksAllowChildren) + { + this(new DefaultTreeModel(root, asksAllowChildren)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param value the initial nodes in the tree + */ + public JTree(Vector value) + { + this(createTreeModel(value)); + } + + public int getRowForPath(TreePath path) + { + TreeUI ui = getUI(); + + if (ui != null) + return ui.getRowForPath(this, path); + + return -1; + } + + public TreePath getPathForRow(int row) + { + TreeUI ui = getUI(); + return ui != null ? ui.getPathForRow(this, row) : null; + } + + protected TreePath[] getPathBetweenRows(int index0, int index1) + { + TreeUI ui = getUI(); + + if (ui == null) + return null; + + int minIndex = Math.min(index0, index1); + int maxIndex = Math.max(index0, index1); + TreePath[] paths = new TreePath[maxIndex - minIndex + 1]; + + for (int i = minIndex; i <= maxIndex; ++i) + paths[i - minIndex] = ui.getPathForRow(this, i); + + return paths; + } + + /** + * Creates a new <code>TreeModel</code> object. + * + * @param value the values stored in the model + */ + protected static TreeModel createTreeModel(Object value) + { + return new DefaultTreeModel(new DynamicUtilTreeNode(value, value)); + } + + /** + * Return the UI associated with this <code>JTree</code> object. + * + * @return the associated <code>TreeUI</code> object + */ + public TreeUI getUI() + { + return (TreeUI) ui; + } + + /** + * Sets the UI associated with this <code>JTree</code> object. + * + * @param ui the <code>TreeUI</code> to associate + */ + public void setUI(TreeUI ui) + { + super.setUI(ui); + } + + /** + * This method resets the UI used to the Look and Feel defaults.. + */ + public void updateUI() + { + setUI((TreeUI) UIManager.getUI(this)); + } + + /** + * This method returns the String ID of the UI class of Separator. + * + * @return The UI class' String ID. + */ + public String getUIClassID() + { + return "TreeUI"; + } + + /** + * Gets the AccessibleContext associated with this + * <code>JTree</code>. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + return new AccessibleJTree(); + } + + /** + * Returns the preferred viewport size. + * + * @return the preferred size + */ + public Dimension getPreferredScrollableViewportSize() + { + return new Dimension (getPreferredSize().width, getVisibleRowCount()*getRowHeight()); + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, + int orientation, int direction) + { + return 1; + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, + int orientation, int direction) + { + return 1; + } + + public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) return ((JViewport) getParent()).getHeight() > getPreferredSize().height; return false; } - - public boolean getScrollableTracksViewportHeight() + + public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) return ((JViewport) getParent()).getWidth() > getPreferredSize().width; return false; } - /** - * Adds a <code>TreeExpansionListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeExpansionListener(TreeExpansionListener listener) - { - listenerList.add(TreeExpansionListener.class, listener); - } - - /** - * Removes a <code>TreeExpansionListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeExpansionListener(TreeExpansionListener listener) - { - listenerList.remove(TreeExpansionListener.class, listener); - } - - /** - * Returns all added <code>TreeExpansionListener</code> objects. - * - * @return an array of listeners - */ - public TreeExpansionListener[] getTreeExpansionListeners() - { - return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class); - } - - /** - * Notifies all listeners that the tree was collapsed. - * - * @param path the path to the node that was collapsed - */ - public void fireTreeCollapsed(TreePath path) - { - TreeExpansionEvent event = new TreeExpansionEvent(this, path); - TreeExpansionListener[] listeners = getTreeExpansionListeners(); - - for (int index = 0; index < listeners.length; ++index) - listeners[index].treeCollapsed(event); - } - - /** - * Notifies all listeners that the tree was expanded. - * - * @param path the path to the node that was expanded - */ - public void fireTreeExpanded(TreePath path) - { - TreeExpansionEvent event = new TreeExpansionEvent(this, path); - TreeExpansionListener[] listeners = getTreeExpansionListeners(); - - for (int index = 0; index < listeners.length; ++index) - listeners[index].treeExpanded(event); - } - - /** - * Adds a <code>TreeSelctionListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeSelectionListener(TreeSelectionListener listener) - { - listenerList.add(TreeSelectionListener.class, listener); - } - - /** - * Removes a <code>TreeSelectionListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeSelectionListener(TreeSelectionListener listener) - { - listenerList.remove(TreeSelectionListener.class, listener); - } - - /** - * Returns all added <code>TreeSelectionListener</code> objects. - * - * @return an array of listeners - */ - public TreeSelectionListener[] getTreeSelectionListeners() - { - return (TreeSelectionListener[]) - getListeners(TreeSelectionListener.class); - } - - /** - * Notifies all listeners when the selection of the tree changed. - * - * @param event the event to send - */ - protected void fireValueChanged(TreeSelectionEvent event) - { - TreeSelectionListener[] listeners = getTreeSelectionListeners(); - - for (int index = 0; index < listeners.length; ++index) - listeners[index].valueChanged(event); - } - - /** - * Adds a <code>TreeWillExpandListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeWillExpandListener(TreeWillExpandListener listener) - { - listenerList.add(TreeWillExpandListener.class, listener); - } - - /** - * Removes a <code>TreeWillExpandListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeWillExpandListener(TreeWillExpandListener listener) - { - listenerList.remove(TreeWillExpandListener.class, listener); - } - - /** - * Returns all added <code>TreeWillExpandListener</code> objects. - * - * @return an array of listeners - */ - public TreeWillExpandListener[] getTreeWillExpandListeners() - { - return (TreeWillExpandListener[]) - getListeners(TreeWillExpandListener.class); - } - - /** - * Notifies all listeners that the tree will collapse. - * - * @param path the path to the node that will collapse - */ - public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException - { - TreeExpansionEvent event = new TreeExpansionEvent(this, path); - TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); - - for (int index = 0; index < listeners.length; ++index) - listeners[index].treeWillCollapse(event); - } - - /** - * Notifies all listeners that the tree will expand. - * - * @param path the path to the node that will expand - */ - public void fireTreeWillExpand(TreePath path) throws ExpandVetoException - { - TreeExpansionEvent event = new TreeExpansionEvent(this, path); - TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); - - for (int index = 0; index < listeners.length; ++index) - listeners[index].treeWillExpand(event); - } - - /** - * Returns the model of this <code>JTree</code> object. - * - * @return the associated <code>TreeModel</code> - */ - public TreeModel getModel() - { - return treeModel; - } - - /** - * Sets the model to use in <code>JTree</code>. - * - * @param model the <code>TreeModel</code> to use - */ - public void setModel(TreeModel model) - { - if (treeModel == model) - return; - - // add treeModelListener to the new model - if (treeModelListener == null) - treeModelListener = createTreeModelListener(); - if (model != null) // as setModel(null) is allowed - model.addTreeModelListener(treeModelListener); - + /** + * Adds a <code>TreeExpansionListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.add(TreeExpansionListener.class, listener); + } + + /** + * Removes a <code>TreeExpansionListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.remove(TreeExpansionListener.class, listener); + } + + /** + * Returns all added <code>TreeExpansionListener</code> objects. + * + * @return an array of listeners + */ + public TreeExpansionListener[] getTreeExpansionListeners() + { + return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class); + } + + /** + * Notifies all listeners that the tree was collapsed. + * + * @param path the path to the node that was collapsed + */ + public void fireTreeCollapsed(TreePath path) + { + TreeExpansionEvent event = new TreeExpansionEvent(this, path); + TreeExpansionListener[] listeners = getTreeExpansionListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].treeCollapsed(event); + } + + /** + * Notifies all listeners that the tree was expanded. + * + * @param path the path to the node that was expanded + */ + public void fireTreeExpanded(TreePath path) + { + TreeExpansionEvent event = new TreeExpansionEvent(this, path); + TreeExpansionListener[] listeners = getTreeExpansionListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].treeExpanded(event); + } + + /** + * Adds a <code>TreeSelctionListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.add(TreeSelectionListener.class, listener); + } + + /** + * Removes a <code>TreeSelectionListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.remove(TreeSelectionListener.class, listener); + } + + /** + * Returns all added <code>TreeSelectionListener</code> objects. + * + * @return an array of listeners + */ + public TreeSelectionListener[] getTreeSelectionListeners() + { + return (TreeSelectionListener[]) + getListeners(TreeSelectionListener.class); + } + + /** + * Notifies all listeners when the selection of the tree changed. + * + * @param event the event to send + */ + protected void fireValueChanged(TreeSelectionEvent event) + { + TreeSelectionListener[] listeners = getTreeSelectionListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].valueChanged(event); + } + + /** + * Adds a <code>TreeWillExpandListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.add(TreeWillExpandListener.class, listener); + } + + /** + * Removes a <code>TreeWillExpandListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.remove(TreeWillExpandListener.class, listener); + } + + /** + * Returns all added <code>TreeWillExpandListener</code> objects. + * + * @return an array of listeners + */ + public TreeWillExpandListener[] getTreeWillExpandListeners() + { + return (TreeWillExpandListener[]) + getListeners(TreeWillExpandListener.class); + } + + /** + * Notifies all listeners that the tree will collapse. + * + * @param path the path to the node that will collapse + */ + public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException + { + TreeExpansionEvent event = new TreeExpansionEvent(this, path); + TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].treeWillCollapse(event); + } + + /** + * Notifies all listeners that the tree will expand. + * + * @param path the path to the node that will expand + */ + public void fireTreeWillExpand(TreePath path) throws ExpandVetoException + { + TreeExpansionEvent event = new TreeExpansionEvent(this, path); + TreeWillExpandListener[] listeners = getTreeWillExpandListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].treeWillExpand(event); + } + + /** + * Returns the model of this <code>JTree</code> object. + * + * @return the associated <code>TreeModel</code> + */ + public TreeModel getModel() + { + return treeModel; + } + + /** + * Sets the model to use in <code>JTree</code>. + * + * @param model the <code>TreeModel</code> to use + */ + public void setModel(TreeModel model) + { + if (treeModel == model) + return; + + // add treeModelListener to the new model + if (treeModelListener == null) + treeModelListener = createTreeModelListener(); + if (model != null) // as setModel(null) is allowed + model.addTreeModelListener(treeModelListener); + TreeModel oldValue = treeModel; treeModel = model; firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); - } + updateUI(); + } - /** - * Checks if this <code>JTree</code> object is editable. - * - * @return <code>true</code> if this tree object is editable, - * <code>false</code> otherwise - */ - public boolean isEditable() - { - return editable; - } - - /** - * Sets the <code>editable</code> property. - * - * @param flag <code>true</code> to make this tree object editable, - * <code>false</code> otherwise - */ - public void setEditable(boolean flag) - { - if (editable == flag) - return; - - boolean oldValue = editable; - editable = flag; - firePropertyChange(EDITABLE_PROPERTY, oldValue, editable); - } - - /** - * Checks if the root element is visible. - * - * @return <code>true</code> if the root element is visible, - * <code>false</code> otherwise - */ - public boolean isRootVisible() - { - return rootVisible; - } - - public void setRootVisible(boolean flag) - { - if (rootVisible == flag) - return; - - boolean oldValue = rootVisible; - rootVisible = flag; - firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); - } - - public boolean getShowsRootHandles() - { - return showsRootHandles; - } - - public void setShowsRootHandles(boolean flag) - { - if (showsRootHandles == flag) - return; - - boolean oldValue = showsRootHandles; - showsRootHandles = flag; - firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag); - } - - public TreeCellEditor getCellEditor() - { - - return cellEditor; - } - - public void setCellEditor(TreeCellEditor editor) - { - if (cellEditor == editor) - return; - - TreeCellEditor oldValue = cellEditor; - cellEditor = editor; - firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor); - } - - public TreeCellRenderer getCellRenderer() - { - return cellRenderer; - } - - public void setCellRenderer(TreeCellRenderer newRenderer) - { - if (cellRenderer == newRenderer) - return; - - TreeCellRenderer oldValue = cellRenderer; - cellRenderer = newRenderer; - firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer); - } - - public TreeSelectionModel getSelectionModel() - { - return selectionModel; - } - - public void setSelectionModel(TreeSelectionModel model) - { - if (selectionModel == model) - return; - - if (selectionModel != null) - selectionModel.removeTreeSelectionListener(selectionRedirector); - - TreeSelectionModel oldValue = selectionModel; - selectionModel = model; - - if (selectionModel != null) - selectionModel.addTreeSelectionListener(selectionRedirector); - - firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model); - revalidate(); - repaint(); - } - - public int getVisibleRowCount() - { - return visibleRowCount; - } - - public void setVisibleRowCount(int rows) - { - if (visibleRowCount == rows) - return; - - int oldValue = visibleRowCount; - visibleRowCount = rows; - firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows); - } - - public boolean isLargeModel() - { - return largeModel; - } - - public void setLargeModel(boolean large) - { - if (largeModel == large) - return; - - boolean oldValue = largeModel; - largeModel = large; - firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large); - } - - public int getRowHeight() - { - - return rowHeight; - } - - public void setRowHeight(int height) - { - if (rowHeight == height) - return; - - int oldValue = rowHeight; - rowHeight = height; - firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height); - } - - public boolean isFixedRowHeight() - { - return rowHeight > 0; - } - - public boolean getInvokesStopCellEditing() - { - return invokesStopCellEditing; - } - - public void setInvokesStopCellEditing(boolean invoke) - { - if (invokesStopCellEditing == invoke) - return; - - boolean oldValue = invokesStopCellEditing; - invokesStopCellEditing = invoke; - firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, - oldValue, invoke); - } - - /** - * @since 1.3 - */ - public int getToggleClickCount() - { - return toggleClickCount; - } + /** + * Checks if this <code>JTree</code> object is editable. + * + * @return <code>true</code> if this tree object is editable, + * <code>false</code> otherwise + */ + public boolean isEditable() + { + return editable; + } - /** - * @since 1.3 - */ - public void setToggleClickCount(int count) - { - if (toggleClickCount == count) - return; - - int oldValue = toggleClickCount; - toggleClickCount = count; - firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count); - } - - public void scrollPathToVisible(TreePath path) - { - if (path == null) - return; + /** + * Sets the <code>editable</code> property. + * + * @param flag <code>true</code> to make this tree object editable, + * <code>false</code> otherwise + */ + public void setEditable(boolean flag) + { + if (editable == flag) + return; - Rectangle rect = getPathBounds(path); - - if (rect == null) - return; - - scrollRectToVisible(rect); - } + boolean oldValue = editable; + editable = flag; + firePropertyChange(EDITABLE_PROPERTY, oldValue, editable); + } - public void scrollRowToVisible(int row) - { - scrollPathToVisible(getPathForRow(row)); - } - - public boolean getScrollsOnExpand() - { - return scrollsOnExpand; - } - - public void setScrollsOnExpand(boolean scroll) - { - if (scrollsOnExpand == scroll) - return; - - boolean oldValue = scrollsOnExpand; - scrollsOnExpand = scroll; - firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); - } - - public void setSelectionPath(TreePath path) - { - selectionModel.setSelectionPath(path); - } - - public void setSelectionPaths(TreePath[] paths) - { - selectionModel.setSelectionPaths(paths); - } - - public void setSelectionRow(int row) - { - TreePath path = getPathForRow(row); - - if (path != null) - selectionModel.setSelectionPath(path); - } - - public void setSelectionRows(int[] rows) - { - // Make sure we have an UI so getPathForRow() does not return null. - if (rows == null || getUI() == null) - return; - - TreePath[] paths = new TreePath[rows.length]; - - for (int i = rows.length - 1; i >= 0; --i) - paths[i] = getPathForRow(rows[i]); - - setSelectionPaths(paths); - } - - public void setSelectionInterval(int index0, int index1) - { - TreePath[] paths = getPathBetweenRows(index0, index1); - - if (paths != null) - setSelectionPaths(paths); - } - - public void addSelectionPath(TreePath path) - { - selectionModel.addSelectionPath(path); - } - - public void addSelectionPaths(TreePath[] paths) - { - selectionModel.addSelectionPaths(paths); - } - - public void addSelectionRow(int row) - { - TreePath path = getPathForRow(row); - - if (path != null) - selectionModel.addSelectionPath(path); - } - - public void addSelectionRows(int[] rows) - { - // Make sure we have an UI so getPathForRow() does not return null. - if (rows == null || getUI() == null) - return; - - TreePath[] paths = new TreePath[rows.length]; + /** + * Checks if the root element is visible. + * + * @return <code>true</code> if the root element is visible, + * <code>false</code> otherwise + */ + public boolean isRootVisible() + { + return rootVisible; + } - for (int i = rows.length - 1; i >= 0; --i) - paths[i] = getPathForRow(rows[i]); - - addSelectionPaths(paths); - } + public void setRootVisible(boolean flag) + { + if (rootVisible == flag) + return; - public void addSelectionInterval(int index0, int index1) - { - TreePath[] paths = getPathBetweenRows(index0, index1); - - if (paths != null) - addSelectionPaths(paths); - } - - public void removeSelectionPath(TreePath path) - { - selectionModel.removeSelectionPath(path); - } - - public void removeSelectionPaths(TreePath[] paths) - { - selectionModel.removeSelectionPaths(paths); - } + boolean oldValue = rootVisible; + rootVisible = flag; + firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); + } - public void removeSelectionRow(int row) - { - TreePath path = getPathForRow(row); + public boolean getShowsRootHandles() + { + return showsRootHandles; + } - if (path != null) - selectionModel.removeSelectionPath(path); - } + public void setShowsRootHandles(boolean flag) + { + if (showsRootHandles == flag) + return; + + boolean oldValue = showsRootHandles; + showsRootHandles = flag; + firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag); + } - public void removeSelectionRows(int[] rows) - { - if (rows == null || getUI() == null) - return; + public TreeCellEditor getCellEditor() + { + return cellEditor; + } - TreePath[] paths = new TreePath[rows.length]; + public void setCellEditor(TreeCellEditor editor) + { + if (cellEditor == editor) + return; - for (int i = rows.length - 1; i >= 0; --i) - paths[i] = getPathForRow(rows[i]); + TreeCellEditor oldValue = cellEditor; + cellEditor = editor; + firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor); + } - removeSelectionPaths(paths); - } + public TreeCellRenderer getCellRenderer() + { + return cellRenderer; + } - public void removeSelectionInterval(int index0, int index1) - { - TreePath[] paths = getPathBetweenRows(index0, index1); + public void setCellRenderer(TreeCellRenderer newRenderer) + { + if (cellRenderer == newRenderer) + return; - if (paths != null) - removeSelectionPaths(paths); - } + TreeCellRenderer oldValue = cellRenderer; + cellRenderer = newRenderer; + firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer); + } - public void clearSelection() - { - selectionModel.clearSelection(); - setLeadSelectionPath(null); - } + public TreeSelectionModel getSelectionModel() + { + return selectionModel; + } - public TreePath getLeadSelectionPath() - { - return leadSelectionPath; - } + public void setSelectionModel(TreeSelectionModel model) + { + if (selectionModel == model) + return; - /** - * @since 1.3 - */ - public void setLeadSelectionPath(TreePath path) - { - if (leadSelectionPath == path) - return; - - TreePath oldValue = leadSelectionPath; - leadSelectionPath = path; - firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); - } - - /** - * @since 1.3 - */ - public TreePath getAnchorSelectionPath() - { - return anchorSelectionPath; - } + if (selectionModel != null) + selectionModel.removeTreeSelectionListener(selectionRedirector); - /** - * @since 1.3 - */ - public void setAnchorSelectionPath(TreePath path) - { - if (anchorSelectionPath == path) - return; - - TreePath oldValue = anchorSelectionPath; - anchorSelectionPath = path; - firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path); - } - - public int getLeadSelectionRow() - { - return selectionModel.getLeadSelectionRow(); - } - - public int getMaxSelectionRow() - { - return selectionModel.getMaxSelectionRow(); - } - - public int getMinSelectionRow() - { - return selectionModel.getMinSelectionRow(); - } - - public int getSelectionCount() - { - return selectionModel.getSelectionCount(); - } - - public TreePath getSelectionPath() - { - return selectionModel.getSelectionPath(); - } - - public TreePath[] getSelectionPaths() - { - return selectionModel.getSelectionPaths(); - } - - public int[] getSelectionRows() - { - return selectionModel.getSelectionRows(); - } - - public boolean isPathSelected(TreePath path) - { - return selectionModel.isPathSelected(path); - } - - public boolean isRowSelected(int row) - { - return selectionModel.isPathSelected(getPathForRow(row)); - } - - public boolean isSelectionEmpty() - { - return selectionModel.isSelectionEmpty(); - } - - /** - * Return the value of the <code>dragEnabled</code> property. - * - * @return the value - * - * @since 1.4 - */ - public boolean getDragEnabled() - { - return dragEnabled; - } - - /** - * Set the <code>dragEnabled</code> property. - * - * @param enabled new value - * - * @since 1.4 - */ - public void setDragEnabled(boolean enabled) - { + TreeSelectionModel oldValue = selectionModel; + selectionModel = model; - dragEnabled = enabled; - } + if (selectionModel != null) + selectionModel.addTreeSelectionListener(selectionRedirector); - public int getRowCount() - { - TreeUI ui = getUI(); + firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model); + revalidate(); + repaint(); + } - if (ui != null) - return ui.getRowCount(this); + public int getVisibleRowCount() + { + return visibleRowCount; + } - return 0; - } + public void setVisibleRowCount(int rows) + { + if (visibleRowCount == rows) + return; - public void collapsePath(TreePath path) - { - try - { - fireTreeWillCollapse(path); - } - catch (ExpandVetoException ev) - { - } - setExpandedState(path, false); - fireTreeCollapsed(path); - } + int oldValue = visibleRowCount; + visibleRowCount = rows; + firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows); + } - public void collapseRow(int row) - { - if (row < 0 || row >= getRowCount()) - return; + public boolean isLargeModel() + { + return largeModel; + } - TreePath path = getPathForRow(row); + public void setLargeModel(boolean large) + { + if (largeModel == large) + return; - if (path != null) - collapsePath(path); - } + boolean oldValue = largeModel; + largeModel = large; + firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large); + } - public void expandPath(TreePath path) - { - // Don't expand if last path component is a leaf node. - if ((path == null) || (treeModel.isLeaf(path.getLastPathComponent()))) - return; - - try - { - fireTreeWillExpand(path); - } - catch (ExpandVetoException ev) - { - } - - setExpandedState(path, true); - fireTreeExpanded(path); - } + public int getRowHeight() + { + return rowHeight; + } - public void expandRow(int row) - { - if (row < 0 || row >= getRowCount()) - return; + public void setRowHeight(int height) + { + if (rowHeight == height) + return; - TreePath path = getPathForRow(row); + int oldValue = rowHeight; + rowHeight = height; + firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height); + } - if (path != null) - expandPath(path); - } + public boolean isFixedRowHeight() + { + return rowHeight > 0; + } - public boolean isCollapsed(TreePath path) - { - return !isExpanded(path); - } + public boolean getInvokesStopCellEditing() + { + return invokesStopCellEditing; + } - public boolean isCollapsed(int row) - { - if (row < 0 || row >= getRowCount()) - return false; + public void setInvokesStopCellEditing(boolean invoke) + { + if (invokesStopCellEditing == invoke) + return; - TreePath path = getPathForRow(row); + boolean oldValue = invokesStopCellEditing; + invokesStopCellEditing = invoke; + firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, + oldValue, invoke); + } - if (path != null) - return isCollapsed(path); + /** + * @since 1.3 + */ + public int getToggleClickCount() + { + return toggleClickCount; + } - return false; - } + /** + * @since 1.3 + */ + public void setToggleClickCount(int count) + { + if (toggleClickCount == count) + return; - public boolean isExpanded(TreePath path) - { - if (path == null) - return false; + int oldValue = toggleClickCount; + toggleClickCount = count; + firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count); + } - Object state = nodeStates.get(path); + public void scrollPathToVisible(TreePath path) + { + if (path == null) + return; + + Object[] oPath = path.getPath(); + TreePath temp = new TreePath(oPath[0]); + boolean stop = false; + int i = 1; + while (!stop) + { + while (isVisible(temp)) + if (i < oPath.length) + temp = temp.pathByAddingChild(oPath[i++]); + else + { + stop = true; + break; + } + makeVisible(temp); + } + Rectangle rect = getPathBounds(path); + scrollRectToVisible(rect); + revalidate(); + repaint(); + } - if ((state == null) || (state != EXPANDED)) - return false; + public void scrollRowToVisible(int row) + { + scrollPathToVisible(getPathForRow(row)); + } - TreePath parent = path.getParentPath(); + public boolean getScrollsOnExpand() + { + return scrollsOnExpand; + } - if (parent != null) - return isExpanded(parent); + public void setScrollsOnExpand(boolean scroll) + { + if (scrollsOnExpand == scroll) + return; - return true; - } + boolean oldValue = scrollsOnExpand; + scrollsOnExpand = scroll; + firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); + } - public boolean isExpanded(int row) - { - if (row < 0 || row >= getRowCount()) - return false; + public void setSelectionPath(TreePath path) + { + selectionModel.setSelectionPath(path); + } - TreePath path = getPathForRow(row); + public void setSelectionPaths(TreePath[] paths) + { + selectionModel.setSelectionPaths(paths); + } - if (path != null) - return isExpanded(path); + public void setSelectionRow(int row) + { + TreePath path = getPathForRow(row); - return false; - } + if (path != null) + selectionModel.setSelectionPath(path); + } - /** - * @since 1.3 - */ - public boolean getExpandsSelectedPaths() - { - return expandsSelectedPaths; - } + public void setSelectionRows(int[] rows) + { + // Make sure we have an UI so getPathForRow() does not return null. + if (rows == null || getUI() == null) + return; - /** - * @since 1.3 - */ - public void setExpandsSelectedPaths(boolean flag) - { - if (expandsSelectedPaths == flag) - return; + TreePath[] paths = new TreePath[rows.length]; - boolean oldValue = expandsSelectedPaths; - expandsSelectedPaths = flag; - firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag); - } + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - public Rectangle getPathBounds(TreePath path) - { - TreeUI ui = getUI(); + setSelectionPaths(paths); + } - if (ui == null) - return null; + public void setSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - return ui.getPathBounds(this, path); - } + if (paths != null) + setSelectionPaths(paths); + } - public Rectangle getRowBounds(int row) - { - TreePath path = getPathForRow(row); + public void addSelectionPath(TreePath path) + { + selectionModel.addSelectionPath(path); + } - if (path != null) - return getPathBounds(path); + public void addSelectionPaths(TreePath[] paths) + { + selectionModel.addSelectionPaths(paths); + } - return null; - } + public void addSelectionRow(int row) + { + TreePath path = getPathForRow(row); - public boolean isEditing() - { - TreeUI ui = getUI(); + if (path != null) + selectionModel.addSelectionPath(path); + } - if (ui != null) - return ui.isEditing(this); + public void addSelectionRows(int[] rows) + { + // Make sure we have an UI so getPathForRow() does not return null. + if (rows == null || getUI() == null) + return; - return false; - } + TreePath[] paths = new TreePath[rows.length]; - public boolean stopEditing() - { - TreeUI ui = getUI(); + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - if (ui != null) - return ui.stopEditing(this); + addSelectionPaths(paths); + } - return false; - } + public void addSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - public void cancelEditing() - { - TreeUI ui = getUI(); + if (paths != null) + addSelectionPaths(paths); + } - if (ui != null) - ui.cancelEditing(this); - } + public void removeSelectionPath(TreePath path) + { + selectionModel.removeSelectionPath(path); + } - public void startEditingAtPath(TreePath path) - { - TreeUI ui = getUI(); + public void removeSelectionPaths(TreePath[] paths) + { + selectionModel.removeSelectionPaths(paths); + } - if (ui != null) - ui.startEditingAtPath(this, path); - } + public void removeSelectionRow(int row) + { + TreePath path = getPathForRow(row); - public TreePath getEditingPath() - { - TreeUI ui = getUI(); + if (path != null) + selectionModel.removeSelectionPath(path); + } - if (ui != null) - return ui.getEditingPath(this); + public void removeSelectionRows(int[] rows) + { + if (rows == null || getUI() == null) + return; - return null; - } + TreePath[] paths = new TreePath[rows.length]; - public TreePath getPathForLocation(int x, int y) - { - TreePath path = getClosestPathForLocation(x, y); + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - if (path != null) - { - Rectangle rect = getPathBounds(path); + removeSelectionPaths(paths); + } - if ((rect != null) && rect.contains(x, y)) - return path; - } + public void removeSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - return null; - } + if (paths != null) + removeSelectionPaths(paths); + } - public int getRowForLocation(int x, int y) - { - TreePath path = getPathForLocation(x, y); + public void clearSelection() + { + selectionModel.clearSelection(); + setLeadSelectionPath(null); + } - if (path != null) - return getRowForPath(path); + public TreePath getLeadSelectionPath() + { + return leadSelectionPath; + } - return -1; - } + /** + * @since 1.3 + */ + public void setLeadSelectionPath(TreePath path) + { + if (leadSelectionPath == path) + return; + + TreePath oldValue = leadSelectionPath; + leadSelectionPath = path; + firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); + } - public TreePath getClosestPathForLocation(int x, int y) - { - TreeUI ui = getUI(); + /** + * @since 1.3 + */ + public TreePath getAnchorSelectionPath() + { + return anchorSelectionPath; + } - if (ui != null) - return ui.getClosestPathForLocation(this, x, y); + /** + * @since 1.3 + */ + public void setAnchorSelectionPath(TreePath path) + { + if (anchorSelectionPath == path) + return; - return null; - } + TreePath oldValue = anchorSelectionPath; + anchorSelectionPath = path; + firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path); + } - public int getClosestRowForLocation(int x, int y) - { - TreePath path = getClosestPathForLocation(x, y); + public int getLeadSelectionRow() + { + return selectionModel.getLeadSelectionRow(); + } - if (path != null) - return getRowForPath(path); + public int getMaxSelectionRow() + { + return selectionModel.getMaxSelectionRow(); + } - return -1; - } + public int getMinSelectionRow() + { + return selectionModel.getMinSelectionRow(); + } - public Object getLastSelectedPathComponent() - { - TreePath path = getSelectionPath(); + public int getSelectionCount() + { + return selectionModel.getSelectionCount(); + } - if (path != null) - return path.getLastPathComponent(); + public TreePath getSelectionPath() + { + return selectionModel.getSelectionPath(); + } - return null; - } + public TreePath[] getSelectionPaths() + { + return selectionModel.getSelectionPaths(); + } - private void doExpandParents(TreePath path, boolean state) - { - TreePath parent = path.getParentPath(); - - if (!isExpanded(parent) && parent != null) - doExpandParents(parent, false); + public int[] getSelectionRows() + { + return selectionModel.getSelectionRows(); + } - nodeStates.put(path, state ? EXPANDED : COLLAPSED); - } + public boolean isPathSelected(TreePath path) + { + return selectionModel.isPathSelected(path); + } - protected void setExpandedState(TreePath path, boolean state) - { - if (path == null) - return; - TreePath parent = path.getParentPath(); + public boolean isRowSelected(int row) + { + return selectionModel.isPathSelected(getPathForRow(row)); + } - doExpandParents(path, state); - } + public boolean isSelectionEmpty() + { + return selectionModel.isSelectionEmpty(); + } - protected void clearToggledPaths() - { - nodeStates.clear(); - } + /** + * Return the value of the <code>dragEnabled</code> property. + * + * @return the value + * + * @since 1.4 + */ + public boolean getDragEnabled() + { + return dragEnabled; + } - protected Enumeration getDescendantToggledPaths(TreePath parent) - { - if (parent == null) - return null; + /** + * Set the <code>dragEnabled</code> property. + * + * @param enabled new value + * + * @since 1.4 + */ + public void setDragEnabled(boolean enabled) + { + dragEnabled = enabled; + } - Enumeration nodes = nodeStates.keys(); - Vector result = new Vector(); + public int getRowCount() + { + TreeUI ui = getUI(); - while (nodes.hasMoreElements()) - { - TreePath path = (TreePath) nodes.nextElement(); + if (ui != null) + return ui.getRowCount(this); - if (path.isDescendant(parent)) - result.addElement(path); - } + return 0; + } - return result.elements(); - } + public void collapsePath(TreePath path) + { + try + { + fireTreeWillCollapse(path); + } + catch (ExpandVetoException ev) + { + // We do nothing if attempt has been vetoed. + } + setExpandedState(path, false); + fireTreeCollapsed(path); + } - public boolean hasBeenExpanded(TreePath path) - { - if (path == null) - return false; + public void collapseRow(int row) + { + if (row < 0 || row >= getRowCount()) + return; - return nodeStates.get(path) != null; - } + TreePath path = getPathForRow(row); - public boolean isVisible(TreePath path) - { - if (path == null) - return false; + if (path != null) + collapsePath(path); + } - TreePath parent = path.getParentPath(); + public void expandPath(TreePath path) + { + // Don't expand if path is null + if (path == null) + return; + + try + { + fireTreeWillExpand(path); + } + catch (ExpandVetoException ev) + { + // We do nothing if attempt has been vetoed. + } + + setExpandedState(path, true); + fireTreeExpanded(path); + } - if (parent == null) - return true; // Is root node. + public void expandRow(int row) + { + if (row < 0 || row >= getRowCount()) + return; - return isExpanded(parent); - } + TreePath path = getPathForRow(row); - public void makeVisible(TreePath path) - { - if (path == null) - return; + if (path != null) + expandPath(path); + } - expandPath(path.getParentPath()); - } + public boolean isCollapsed(TreePath path) + { + return !isExpanded(path); + } - public boolean isPathEditable(TreePath path) - { - return isEditable(); - } + public boolean isCollapsed(int row) + { + if (row < 0 || row >= getRowCount()) + return false; - /** - * Creates and returns an instance of {@link TreeModelHandler}. - * - * @returns an instance of {@link TreeModelHandler} - */ - protected TreeModelListener createTreeModelListener() - { - return new TreeModelHandler(); - } - - /** - * Returns a sample TreeModel that can be used in a JTree. This can be used - * in Bean- or GUI-Builders to show something interesting. - * - * @return a sample TreeModel that can be used in a JTree - */ - protected static TreeModel getDefaultTreeModel() - { - DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); - DefaultMutableTreeNode child1 = new DefaultMutableTreeNode( - "Child node 1"); - DefaultMutableTreeNode child11 = new DefaultMutableTreeNode( - "Child node 1.1"); - DefaultMutableTreeNode child12 = new DefaultMutableTreeNode( - "Child node 1.2"); - DefaultMutableTreeNode child13 = new DefaultMutableTreeNode( - "Child node 1.3"); - DefaultMutableTreeNode child2 = new DefaultMutableTreeNode( - "Child node 2"); - DefaultMutableTreeNode child21 = new DefaultMutableTreeNode( - "Child node 2.1"); - DefaultMutableTreeNode child22 = new DefaultMutableTreeNode( - "Child node 2.2"); - DefaultMutableTreeNode child23 = new DefaultMutableTreeNode( - "Child node 2.3"); - DefaultMutableTreeNode child24 = new DefaultMutableTreeNode( - "Child node 2.4"); - - DefaultMutableTreeNode child3 = new DefaultMutableTreeNode( - "Child node 3"); - root.add(child1); - root.add(child2); - root.add(child3); - child1.add(child11); - child1.add(child12); - child1.add(child13); - child2.add(child21); - child2.add(child22); - child2.add(child23); - child2.add(child24); - return new DefaultTreeModel(root); - } - - /** - * Converts the specified value to a String. This is used by the renderers - * of this JTree and its nodes. - * - * This implementation simply returns <code>value.toString()</code> and - * ignores all other parameters. Subclass this method to control the - * conversion. - * - * @param value the value that is converted to a String - * @param selected indicates if that value is selected or not - * @param expanded indicates if that value is expanded or not - * @param leaf indicates if that value is a leaf node or not - * @param row the row of the node - * @param hasFocus indicates if that node has focus or not - */ - public String convertValueToText(Object value, boolean selected, - boolean expanded, boolean leaf, int row, boolean hasFocus) - { - return value.toString(); - } - - /** - * A String representation of this JTree. This is intended to be used for - * debugging. The returned string may be empty but may not be - * <code>null</code>. - * - * @return a String representation of this JTree - */ - public String paramString() - { - // TODO: this is completely legal, but it would possibly be nice - // to return some more content, like the tree structure, some properties - // etc ... - return ""; - } - - /** - * Returns all TreePath objects which are a descendants of the given path - * and are exapanded at the moment of the execution of this method. If the - * state of any node is beeing toggled while this method is executing this - * change may be left unaccounted. - * - * @param path The parent of this request - * @return An Enumeration containing TreePath objects - */ - public Enumeration getExpandedDescendants(TreePath path) - { - Enumeration paths = nodeStates.keys(); - Vector relevantPaths = new Vector(); - while (paths.hasMoreElements()) - { - TreePath nextPath = (TreePath) paths.nextElement(); - if (nodeStates.get(nextPath) == EXPANDED - && path.isDescendant(nextPath)) - { - relevantPaths.add(nextPath); - } - } - return relevantPaths.elements(); - } - - /** - * Returns the next table element (beginning from the row - * <code>startingRow</code> that starts with <code>prefix</code>. - * Searching is done in the direction specified by <code>bias</code>. - * - * @param prefix the prefix to search for in the cell values - * @param startingRow the index of the row where to start searching from - * @param bias the search direction, either {@link Position.Bias#Forward} or - * {@link Position.Bias#Backward} - * - * @return the path to the found element or -1 if no such element has been - * found - * - * @throws IllegalArgumentException if prefix is <code>null</code> or - * startingRow is not valid - * - * @since 1.4 - */ - public TreePath getNextMatch(String prefix, int startingRow, - Position.Bias bias) - { - if (prefix == null) - throw new IllegalArgumentException( - "The argument 'prefix' must not be" + " null."); - if (startingRow < 0) - throw new IllegalArgumentException( - "The argument 'startingRow' must not" - + " be less than zero."); - - int size = getRowCount(); - if (startingRow > size) - throw new IllegalArgumentException( - "The argument 'startingRow' must not" - + " be greater than the number of" - + " elements in the TreeModel."); - - TreePath foundPath = null; - if (bias == Position.Bias.Forward) - { - for (int i = startingRow; i < size; i++) - { - TreePath path = getPathForRow(i); - Object o = path.getLastPathComponent(); - // FIXME: in the following call to convertValueToText the - // last argument (hasFocus) should be done right. - String item = convertValueToText(o, isRowSelected(i), - isExpanded(i), treeModel.isLeaf(o), i, false); - if (item.startsWith(prefix)) - { - foundPath = path; - break; - } - } - } else - { - for (int i = startingRow; i >= 0; i--) - { - TreePath path = getPathForRow(i); - Object o = path.getLastPathComponent(); - // FIXME: in the following call to convertValueToText the - // last argument (hasFocus) should be done right. - String item = convertValueToText(o, isRowSelected(i), - isExpanded(i), treeModel.isLeaf(o), i, false); - if (item.startsWith(prefix)) - { - foundPath = path; - break; - } - } - } - return foundPath; - } - - /** - * Removes any paths in the current set of selected paths that are - * descendants of <code>path</code>. If <code>includePath</code> is set - * to <code>true</code> and <code>path</code> itself is selected, then - * it will be removed too. - * - * @param path the path from which selected descendants are to be removed - * @param includeSelected if <code>true</code> then <code>path</code> itself - * will also be remove if it's selected - * - * @return <code>true</code> if something has been removed, - * <code>false</code> otherwise - * - * @since 1.3 - */ - protected boolean removeDescendantSelectedPaths(TreePath path, - boolean includeSelected) - { - boolean removedSomething = false; - TreePath[] selected = getSelectionPaths(); - for (int index = 0; index < selected.length; index++) - { - if ((selected[index] == path && includeSelected) - || (selected[index].isDescendant(path))) - { - removeSelectionPath(selected[index]); - removedSomething = true; - } - } - return removedSomething; - } + TreePath path = getPathForRow(row); + + if (path != null) + return isCollapsed(path); + + return false; + } + + public boolean isExpanded(TreePath path) + { + if (path == null) + return false; + + Object state = nodeStates.get(path); + + if ((state == null) || (state != EXPANDED)) + return false; + + TreePath parent = path.getParentPath(); + + if (parent != null) + return isExpanded(parent); + + return true; + } + + public boolean isExpanded(int row) + { + if (row < 0 || row >= getRowCount()) + return false; + + TreePath path = getPathForRow(row); + + if (path != null) + return isExpanded(path); + + return false; + } + + /** + * @since 1.3 + */ + public boolean getExpandsSelectedPaths() + { + return expandsSelectedPaths; + } + + /** + * @since 1.3 + */ + public void setExpandsSelectedPaths(boolean flag) + { + if (expandsSelectedPaths == flag) + return; + + boolean oldValue = expandsSelectedPaths; + expandsSelectedPaths = flag; + firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag); + } + + public Rectangle getPathBounds(TreePath path) + { + TreeUI ui = getUI(); + + if (ui == null) + return null; + + return ui.getPathBounds(this, path); + } + + public Rectangle getRowBounds(int row) + { + TreePath path = getPathForRow(row); + + if (path != null) + return getPathBounds(path); + + return null; + } + + public boolean isEditing() + { + TreeUI ui = getUI(); + + if (ui != null) + return ui.isEditing(this); + + return false; + } + + public boolean stopEditing() + { + TreeUI ui = getUI(); + + if (ui != null) + return ui.stopEditing(this); + + return false; + } + + public void cancelEditing() + { + TreeUI ui = getUI(); + + if (ui != null) + ui.cancelEditing(this); + } + + public void startEditingAtPath(TreePath path) + { + TreeUI ui = getUI(); + + if (ui != null) + ui.startEditingAtPath(this, path); + } + + public TreePath getEditingPath() + { + TreeUI ui = getUI(); + + if (ui != null) + return ui.getEditingPath(this); + + return null; + } + + public TreePath getPathForLocation(int x, int y) + { + TreePath path = getClosestPathForLocation(x, y); + + if (path != null) + { + Rectangle rect = getPathBounds(path); + + if ((rect != null) && rect.contains(x, y)) + return path; + } + + return null; + } + + public int getRowForLocation(int x, int y) + { + TreePath path = getPathForLocation(x, y); + + if (path != null) + return getRowForPath(path); + + return -1; + } + + public TreePath getClosestPathForLocation(int x, int y) + { + TreeUI ui = getUI(); + + if (ui != null) + return ui.getClosestPathForLocation(this, x, y); + + return null; + } + + public int getClosestRowForLocation(int x, int y) + { + TreePath path = getClosestPathForLocation(x, y); + + if (path != null) + return getRowForPath(path); + + return -1; + } + + public Object getLastSelectedPathComponent() + { + TreePath path = getSelectionPath(); + + if (path != null) + return path.getLastPathComponent(); + + return null; + } + + private void doExpandParents(TreePath path, boolean state) + { + TreePath parent = path.getParentPath(); + + if (!isExpanded(parent) && parent != null) + doExpandParents(parent, false); + + nodeStates.put(path, state ? EXPANDED : COLLAPSED); + } + + protected void setExpandedState(TreePath path, boolean state) + { + if (path == null) + return; + + doExpandParents(path, state); + } + + protected void clearToggledPaths() + { + nodeStates.clear(); + } + + protected Enumeration getDescendantToggledPaths(TreePath parent) + { + if (parent == null) + return null; + + Enumeration nodes = nodeStates.keys(); + Vector result = new Vector(); + + while (nodes.hasMoreElements()) + { + TreePath path = (TreePath) nodes.nextElement(); + + if (path.isDescendant(parent)) + result.addElement(path); + } + + return result.elements(); + } + + public boolean hasBeenExpanded(TreePath path) + { + if (path == null) + return false; + + return nodeStates.get(path) != null; + } + + public boolean isVisible(TreePath path) + { + if (path == null) + return false; + + TreePath parent = path.getParentPath(); + + if (parent == null) + return true; // Is root node. + + return isExpanded(parent); + } + + public void makeVisible(TreePath path) + { + if (path == null) + return; + + expandPath(path.getParentPath()); + } + + public boolean isPathEditable(TreePath path) + { + return isEditable(); + } + + /** + * Creates and returns an instance of {@link TreeModelHandler}. + * + * @return an instance of {@link TreeModelHandler} + */ + protected TreeModelListener createTreeModelListener() + { + return new TreeModelHandler(); + } + + /** + * Returns a sample TreeModel that can be used in a JTree. This can be used + * in Bean- or GUI-Builders to show something interesting. + * + * @return a sample TreeModel that can be used in a JTree + */ + protected static TreeModel getDefaultTreeModel() + { + DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); + DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1"); + DefaultMutableTreeNode child11 = + new DefaultMutableTreeNode("Child node 1.1"); + DefaultMutableTreeNode child12 = + new DefaultMutableTreeNode("Child node 1.2"); + DefaultMutableTreeNode child13 = + new DefaultMutableTreeNode("Child node 1.3"); + DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2"); + DefaultMutableTreeNode child21 = + new DefaultMutableTreeNode("Child node 2.1"); + DefaultMutableTreeNode child22 = + new DefaultMutableTreeNode("Child node 2.2"); + DefaultMutableTreeNode child23 = + new DefaultMutableTreeNode("Child node 2.3"); + DefaultMutableTreeNode child24 = + new DefaultMutableTreeNode("Child node 2.4"); + + DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3"); + root.add(child1); + root.add(child2); + root.add(child3); + child1.add(child11); + child1.add(child12); + child1.add(child13); + child2.add(child21); + child2.add(child22); + child2.add(child23); + child2.add(child24); + return new DefaultTreeModel(root); + } + + /** + * Converts the specified value to a String. This is used by the renderers + * of this JTree and its nodes. + * + * This implementation simply returns <code>value.toString()</code> and + * ignores all other parameters. Subclass this method to control the + * conversion. + * + * @param value the value that is converted to a String + * @param selected indicates if that value is selected or not + * @param expanded indicates if that value is expanded or not + * @param leaf indicates if that value is a leaf node or not + * @param row the row of the node + * @param hasFocus indicates if that node has focus or not + */ + public String convertValueToText(Object value, boolean selected, + boolean expanded, boolean leaf, int row, boolean hasFocus) + { + return value.toString(); + } + + /** + * A String representation of this JTree. This is intended to be used for + * debugging. The returned string may be empty but may not be + * <code>null</code>. + * + * @return a String representation of this JTree + */ + public String paramString() + { + // TODO: this is completely legal, but it would possibly be nice + // to return some more content, like the tree structure, some properties + // etc ... + return ""; + } + + /** + * Returns all TreePath objects which are a descendants of the given path + * and are exapanded at the moment of the execution of this method. If the + * state of any node is beeing toggled while this method is executing this + * change may be left unaccounted. + * + * @param path The parent of this request + * + * @return An Enumeration containing TreePath objects + */ + public Enumeration getExpandedDescendants(TreePath path) + { + Enumeration paths = nodeStates.keys(); + Vector relevantPaths = new Vector(); + while (paths.hasMoreElements()) + { + TreePath nextPath = (TreePath) paths.nextElement(); + if (nodeStates.get(nextPath) == EXPANDED + && path.isDescendant(nextPath)) + { + relevantPaths.add(nextPath); + } + } + return relevantPaths.elements(); + } + + /** + * Returns the next table element (beginning from the row + * <code>startingRow</code> that starts with <code>prefix</code>. + * Searching is done in the direction specified by <code>bias</code>. + * + * @param prefix the prefix to search for in the cell values + * @param startingRow the index of the row where to start searching from + * @param bias the search direction, either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} + * + * @return the path to the found element or -1 if no such element has been + * found + * + * @throws IllegalArgumentException if prefix is <code>null</code> or + * startingRow is not valid + * + * @since 1.4 + */ + public TreePath getNextMatch(String prefix, int startingRow, + Position.Bias bias) + { + if (prefix == null) + throw new IllegalArgumentException("The argument 'prefix' must not be" + + " null."); + if (startingRow < 0) + throw new IllegalArgumentException("The argument 'startingRow' must not" + + " be less than zero."); + + int size = getRowCount(); + if (startingRow > size) + throw new IllegalArgumentException("The argument 'startingRow' must not" + + " be greater than the number of" + + " elements in the TreeModel."); + + TreePath foundPath = null; + if (bias == Position.Bias.Forward) + { + for (int i = startingRow; i < size; i++) + { + TreePath path = getPathForRow(i); + Object o = path.getLastPathComponent(); + // FIXME: in the following call to convertValueToText the + // last argument (hasFocus) should be done right. + String item = convertValueToText(o, isRowSelected(i), + isExpanded(i), treeModel.isLeaf(o), + i, false); + if (item.startsWith(prefix)) + { + foundPath = path; + break; + } + } + } + else + { + for (int i = startingRow; i >= 0; i--) + { + TreePath path = getPathForRow(i); + Object o = path.getLastPathComponent(); + // FIXME: in the following call to convertValueToText the + // last argument (hasFocus) should be done right. + String item = convertValueToText(o, isRowSelected(i), + isExpanded(i), treeModel.isLeaf(o), i, false); + if (item.startsWith(prefix)) + { + foundPath = path; + break; + } + } + } + return foundPath; + } + + /** + * Removes any paths in the current set of selected paths that are + * descendants of <code>path</code>. If <code>includePath</code> is set + * to <code>true</code> and <code>path</code> itself is selected, then + * it will be removed too. + * + * @param path the path from which selected descendants are to be removed + * @param includeSelected if <code>true</code> then <code>path</code> itself + * will also be remove if it's selected + * + * @return <code>true</code> if something has been removed, + * <code>false</code> otherwise + * + * @since 1.3 + */ + protected boolean removeDescendantSelectedPaths(TreePath path, + boolean includeSelected) + { + boolean removedSomething = false; + TreePath[] selected = getSelectionPaths(); + for (int index = 0; index < selected.length; index++) + { + if ((selected[index] == path && includeSelected) + || (selected[index].isDescendant(path))) + { + removeSelectionPath(selected[index]); + removedSomething = true; + } + } + return removedSomething; + } + + /** + * Removes any descendants of the TreePaths in toRemove that have been + * expanded. + * + * @param toRemove - Enumeration of TreePaths that need to be removed from + * cache of toggled tree paths. + */ + protected void removeDescendantToggledPaths(Enumeration toRemove) + { + while (toRemove.hasMoreElements()) + { + TreePath current = (TreePath) toRemove.nextElement(); + Enumeration descendants = getDescendantToggledPaths(current); + + while (descendants.hasMoreElements()) + { + TreePath currentDes = (TreePath) descendants.nextElement(); + if (isExpanded(currentDes)) + nodeStates.remove(currentDes); + } + } + } + + /** + * Sent when the tree has changed enough that we need to resize the bounds, + * but not enough that we need to remove the expanded node set (e.g nodes + * were expanded or collapsed, or nodes were inserted into the tree). You + * should never have to invoke this, the UI will invoke this as it needs to. + */ + public void treeDidChange() + { + repaint(); + } } diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java index d750bad29cc..5f20f0aa60a 100644 --- a/libjava/classpath/javax/swing/JViewport.java +++ b/libjava/classpath/javax/swing/JViewport.java @@ -41,6 +41,7 @@ package javax.swing; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Image; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; @@ -49,6 +50,9 @@ import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.Serializable; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -95,17 +99,41 @@ import javax.swing.plaf.ViewportUI; * the underlying child at position <code>(-VX,-VY)</code></p> * */ -public class JViewport extends JComponent +public class JViewport extends JComponent implements Accessible { + /** + * Provides accessibility support for <code>JViewport</code>. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJViewport extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJViewport</code>. + */ + public AccessibleJViewport() + { + // Nothing to do here. + } + + /** + * Returns the accessible role of <code>JViewport</code>, which is + * {@link AccessibleRole#VIEWPORT}. + * + * @return the accessible role of <code>JViewport</code> + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.VIEWPORT; + } + } /** * A {@link java.awt.event.ComponentListener} that listens for - * changes of the view's size. This class forbids changes of the view - * component's size that would exceed the viewport's size. + * changes of the view's size. This triggers a revalidate() call on the + * viewport. */ - protected class ViewListener - extends ComponentAdapter - implements Serializable + protected class ViewListener extends ComponentAdapter implements Serializable { private static final long serialVersionUID = -2812489404285958070L; @@ -114,57 +142,53 @@ public class JViewport extends JComponent */ protected ViewListener() { + // Nothing to do here. } /** * Receives notification when a component (in this case: the view - * component) changes it's size. + * component) changes it's size. This simply triggers a revalidate() on the + * viewport. * * @param ev the ComponentEvent describing the change */ public void componentResized(ComponentEvent ev) { - // According to some tests that I did with Sun's implementation - // this class is supposed to make sure that the view component - // is not resized to a larger size than the viewport. - // This is not documented anywhere. What I did is: I subclassed JViewport - // and ViewListener and 'disabled' the componentResized method by - // overriding it and not calling super.componentResized(). - // When this method is disabled I can set the size on the view component - // normally, when it is enabled, it gets immediatly resized back, - // after a resize attempt that would exceed the Viewport's size. - Component comp = ev.getComponent(); - Dimension newSize = comp.getSize(); - Dimension viewportSize = getSize(); - boolean revert = false; - if (newSize.width > viewportSize.width) - { - newSize.width = viewportSize.width; - revert = true; - } - if (newSize.height > viewportSize.height) - { - newSize.height = viewportSize.height; - revert = true; - } - if (revert == true) - comp.setSize(newSize); + revalidate(); } } - private static final long serialVersionUID = -6925142919680527970L; - public static final int SIMPLE_SCROLL_MODE = 0; public static final int BLIT_SCROLL_MODE = 1; public static final int BACKINGSTORE_SCROLL_MODE = 2; + private static final long serialVersionUID = -6925142919680527970L; + + protected boolean scrollUnderway; + protected boolean isViewSizeSet; + + /** + * This flag indicates whether we use a backing store for drawing. + * + * @deprecated since JDK 1.3 + */ + protected boolean backingStore; + + /** + * The backingstore image used for the backingstore and blit scroll methods. + */ + protected Image backingStoreImage; + + /** + * The position at which the view has been drawn the last time. This is used + * to determine the bittable area. + */ + protected Point lastPaintPosition; + ChangeEvent changeEvent = new ChangeEvent(this); int scrollMode; - protected boolean scrollUnderway; - protected boolean isViewSizeSet; - /** * The width and height of the Viewport's area in terms of view * coordinates. Typically this will be the same as the width and height @@ -172,29 +196,75 @@ public class JViewport extends JComponent * width and height, which it may do, for example if it magnifies or * rotates its view. * - * @see #toViewCoordinates + * @see #toViewCoordinates(Dimension) */ Dimension extentSize; /** * The width and height of the view in its own coordinate space. */ - Dimension viewSize; - Point lastPaintPosition; - /** * The ViewListener instance. */ ViewListener viewListener; + /** + * Stores the location from where to blit. This is a cached Point object used + * in blitting calculations. + */ + Point cachedBlitFrom; + + /** + * Stores the location where to blit to. This is a cached Point object used + * in blitting calculations. + */ + Point cachedBlitTo; + + /** + * Stores the width of the blitted area. This is a cached Dimension object + * used in blitting calculations. + */ + Dimension cachedBlitSize; + + /** + * Stores the bounds of the area that needs to be repainted. This is a cached + * Rectangle object used in blitting calculations. + */ + Rectangle cachedBlitPaint; + + boolean damaged = true; + + /** + * A flag indicating if the size of the viewport has changed since the + * last repaint. This is used in double buffered painting to check if we + * need a new double buffer, or can reuse the old one. + */ + boolean sizeChanged = true; + public JViewport() { setOpaque(true); - setScrollMode(BLIT_SCROLL_MODE); - setLayout(createLayoutManager()); + String scrollModeProp = + System.getProperty("gnu.javax.swing.JViewport.scrollMode", + "BLIT"); + int myScrollMode; + if (scrollModeProp.equalsIgnoreCase("simple")) + myScrollMode = SIMPLE_SCROLL_MODE; + else if (scrollModeProp.equalsIgnoreCase("backingstore")) + myScrollMode = BACKINGSTORE_SCROLL_MODE; + else + myScrollMode = BLIT_SCROLL_MODE; + setScrollMode(myScrollMode); + updateUI(); + setLayout(createLayoutManager()); + lastPaintPosition = new Point(); + cachedBlitFrom = new Point(); + cachedBlitTo = new Point(); + cachedBlitSize = new Dimension(); + cachedBlitPaint = new Rectangle(); } public Dimension getExtentSize() @@ -248,9 +318,14 @@ public class JViewport extends JComponent viewSize = newSize; Component view = getView(); if (view != null) - view.setSize(viewSize); + { + if (newSize != view.getSize()) + { + view.setSize(viewSize); + fireStateChanged(); + } + } isViewSizeSet = true; - fireStateChanged(); } /** @@ -275,13 +350,17 @@ public class JViewport extends JComponent public void setViewPosition(Point p) { + if (getViewPosition().equals(p)) + return; Component view = getView(); if (view != null) { Point q = new Point(-p.x, -p.y); view.setLocation(q); + isViewSizeSet = false; fireStateChanged(); } + repaint(); } public Rectangle getViewRect() @@ -331,12 +410,8 @@ public class JViewport extends JComponent public void setView(Component v) { - while (getComponentCount() > 0) - { - if (viewListener != null) - getView().removeComponentListener(viewListener); - remove(0); - } + if (viewListener != null) + getView().removeComponentListener(viewListener); if (v != null) { @@ -346,37 +421,25 @@ public class JViewport extends JComponent add(v); fireStateChanged(); } - } - - public void revalidate() - { - fireStateChanged(); - super.revalidate(); + revalidate(); + repaint(); } public void reshape(int x, int y, int w, int h) { - boolean changed = - (x != getX()) - || (y != getY()) - || (w != getWidth()) - || (h != getHeight()); + if (w != getWidth() || h != getHeight()) + sizeChanged = true; super.reshape(x, y, w, h); - if (changed) - fireStateChanged(); - } - - protected void addImpl(Component comp, Object constraints, int index) - { - if (getComponentCount() > 0) - remove(getComponents()[0]); - - super.addImpl(comp, constraints, index); + if (sizeChanged) + { + damaged = true; + fireStateChanged(); + } } - public final Insets getInsets() + public final Insets getInsets() { - return new Insets(0,0,0,0); + return new Insets(0, 0, 0, 0); } public final Insets getInsets(Insets insets) @@ -390,6 +453,14 @@ public class JViewport extends JComponent return insets; } + + /** + * Overridden to return <code>false</code>, so the JViewport's paint method + * gets called instead of directly calling the children. This is necessary + * in order to get a useful clipping and translation on the children. + * + * @return <code>false</code> + */ public boolean isOptimizedDrawingEnabled() { return false; @@ -397,7 +468,36 @@ public class JViewport extends JComponent public void paint(Graphics g) { - paintComponent(g); + Component view = getView(); + + if (view == null) + return; + + Point pos = getViewPosition(); + Rectangle viewBounds = view.getBounds(); + Rectangle portBounds = getBounds(); + + if (viewBounds.width == 0 + || viewBounds.height == 0 + || portBounds.width == 0 + || portBounds.height == 0) + return; + + switch (getScrollMode()) + { + + case JViewport.BACKINGSTORE_SCROLL_MODE: + paintBackingStore(g); + break; + case JViewport.BLIT_SCROLL_MODE: + paintBlit(g); + break; + case JViewport.SIMPLE_SCROLL_MODE: + default: + paintSimple(g); + break; + } + damaged = false; } public void addChangeListener(ChangeListener listener) @@ -415,13 +515,6 @@ public class JViewport extends JComponent return (ChangeListener[]) getListeners(ChangeListener.class); } - protected void fireStateChanged() - { - ChangeListener[] listeners = getChangeListeners(); - for (int i = 0; i < listeners.length; ++i) - listeners[i].stateChanged(changeEvent); - } - /** * This method returns the String ID of the UI class of Separator. * @@ -467,6 +560,90 @@ public class JViewport extends JComponent } /** + * Scrolls the view so that contentRect becomes visible. + * + * @param contentRect the rectangle to make visible within the view + */ + public void scrollRectToVisible(Rectangle contentRect) + { + Component view = getView(); + if (view == null) + return; + + Point pos = getViewPosition(); + Rectangle viewBounds = getView().getBounds(); + Rectangle portBounds = getBounds(); + + if (isShowing()) + getView().validate(); + + // If the bottom boundary of contentRect is below the port + // boundaries, scroll up as necessary. + if (contentRect.y + contentRect.height + viewBounds.y > portBounds.height) + pos.y = contentRect.y + contentRect.height - portBounds.height; + // If contentRect.y is above the port boundaries, scroll down to + // contentRect.y. + if (contentRect.y + viewBounds.y < 0) + pos.y = contentRect.y; + // If the right boundary of contentRect is right from the port + // boundaries, scroll left as necessary. + if (contentRect.x + contentRect.width + viewBounds.x > portBounds.width) + pos.x = contentRect.x + contentRect.width - portBounds.width; + // If contentRect.x is left from the port boundaries, scroll right to + // contentRect.x. + if (contentRect.x + viewBounds.x < 0) + pos.x = contentRect.x; + setViewPosition(pos); + } + + /** + * Returns the accessible context for this <code>JViewport</code>. This + * will be an instance of {@link AccessibleJViewport}. + * + * @return the accessible context for this <code>JViewport</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJViewport(); + return accessibleContext; + } + + /** + * Forward repaint to parent to make sure only one paint is performed by the + * RepaintManager. + * + * @param tm number of milliseconds to defer the repaint request + * @param x the X coordinate of the upper left corner of the dirty area + * @param y the Y coordinate of the upper left corner of the dirty area + * @param w the width of the dirty area + * @param h the height of the dirty area + */ + public void repaint(long tm, int x, int y, int w, int h) + { + Component parent = getParent(); + if (parent != null) + { + parent.repaint(tm, x + getX(), y + getY(), w, h); + } + } + + protected void addImpl(Component comp, Object constraints, int index) + { + if (getComponentCount() > 0) + remove(getComponents()[0]); + + super.addImpl(comp, constraints, index); + } + + protected void fireStateChanged() + { + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0; i < listeners.length; ++i) + listeners[i].stateChanged(changeEvent); + } + + /** * Creates a {@link ViewListener} that is supposed to listen for * size changes on the view component. * @@ -489,43 +666,217 @@ public class JViewport extends JComponent } /** - * Scrolls the view so that contentRect becomes visible. + * Computes the parameters for the blitting scroll method. <code>dx</code> + * and <code>dy</code> specifiy the X and Y offset by which the viewport + * is scrolled. All other arguments are output parameters and are filled by + * this method. * - * @param contentRect the rectangle to make visible within the view + * <code>blitFrom</code> holds the position of the blit rectangle in the + * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea + * is copied to. + * + * <code>blitSize</code> holds the size of the blit area and + * <code>blitPaint</code> is the area of the view that needs to be painted. + * + * This method returns <code>true</code> if blitting is possible and + * <code>false</code> if the viewport has to be repainted completetly without + * blitting. + * + * @param dx the horizontal delta + * @param dy the vertical delta + * @param blitFrom the position from where to blit; set by this method + * @param blitTo the position where to blit area is copied to; set by this + * method + * @param blitSize the size of the blitted area; set by this method + * @param blitPaint the area that needs repainting; set by this method + * + * @return <code>true</code> if blitting is possible, + * <code>false</code> otherwise */ - public void scrollRectToVisible(Rectangle contentRect) + protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, + Dimension blitSize, Rectangle blitPaint) + { + if ((dx != 0 && dy != 0) || damaged) + // We cannot blit if the viewport is scrolled in both directions at + // once. + return false; + + Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds()); + + // Compute the blitFrom and blitTo parameters. + blitFrom.x = portBounds.x; + blitFrom.y = portBounds.y; + blitTo.x = portBounds.x; + blitTo.y = portBounds.y; + + if (dy > 0) + { + blitFrom.y = portBounds.y + dy; + } + else if (dy < 0) + { + blitTo.y = portBounds.y - dy; + } + else if (dx > 0) + { + blitFrom.x = portBounds.x + dx; + } + else if (dx < 0) + { + blitTo.x = portBounds.x - dx; + } + + // Compute size of the blit area. + if (dx != 0) + { + blitSize.width = portBounds.width - Math.abs(dx); + blitSize.height = portBounds.height; + } + else if (dy != 0) + { + blitSize.width = portBounds.width; + blitSize.height = portBounds.height - Math.abs(dy); + } + + // Compute the blitPaint parameter. + blitPaint.setBounds(portBounds); + if (dy > 0) + { + blitPaint.y = portBounds.y + portBounds.height - dy; + blitPaint.height = dy; + } + else if (dy < 0) + { + blitPaint.height = -dy; + } + if (dx > 0) + { + blitPaint.x = portBounds.x + portBounds.width - dx; + blitPaint.width = dx; + } + else if (dx < 0) + { + blitPaint.width = -dx; + } + + return true; + } + + /** + * Paints the viewport in case we have a scrollmode of + * {@link #SIMPLE_SCROLL_MODE}. + * + * This simply paints the view directly on the surface of the viewport. + * + * @param g the graphics context to use + */ + void paintSimple(Graphics g) { Point pos = getViewPosition(); - Rectangle viewBounds = getView().getBounds(); - Rectangle portBounds = getBounds(); - - // FIXME: should validate the view if it is not valid, however - // this may cause excessive validation when the containment - // hierarchy is being created. - - // if contentRect is larger than the portBounds, center the view - if (contentRect.height > portBounds.height || - contentRect.width > portBounds.width) + Component view = getView(); + boolean translated = false; + try + { + g.translate(-pos.x, -pos.y); + translated = true; + view.paint(g); + } + finally { - setViewPosition(new Point(contentRect.x, contentRect.y)); - return; + if (translated) + g.translate (pos.x, pos.y); } - - // Y-DIRECTION - if (contentRect.y < -viewBounds.y) - setViewPosition(new Point(pos.x, contentRect.y)); - else if (contentRect.y + contentRect.height > - -viewBounds.y + portBounds.height) - setViewPosition (new Point(pos.x, contentRect.y - - (portBounds.height - contentRect.height))); - - // X-DIRECTION - pos = getViewPosition(); - if (contentRect.x < -viewBounds.x) - setViewPosition(new Point(contentRect.x, pos.y)); - else if (contentRect.x + contentRect.width > - -viewBounds.x + portBounds.width) - setViewPosition (new Point(contentRect.x - - (portBounds.width - contentRect.width), pos.y)); + } + + /** + * Paints the viewport in case we have a scroll mode of + * {@link #BACKINGSTORE_SCROLL_MODE}. + * + * This method uses a backing store image to paint the view to, which is then + * subsequently painted on the screen. This should make scrolling more + * smooth. + * + * @param g the graphics context to use + */ + void paintBackingStore(Graphics g) + { + // If we have no backing store image yet or the size of the component has + // changed, we need to rebuild the backing store. + if (backingStoreImage == null || sizeChanged) + { + backingStoreImage = createImage(getWidth(), getHeight()); + sizeChanged = false; + Graphics g2 = backingStoreImage.getGraphics(); + paintSimple(g2); + g2.dispose(); + } + // Otherwise we can perform the blitting on the backing store image: + // First we move the part that remains visible after scrolling, then + // we only need to paint the bit that becomes newly visible. + else + { + Graphics g2 = backingStoreImage.getGraphics(); + Point viewPosition = getViewPosition(); + int dx = viewPosition.x - lastPaintPosition.x; + int dy = viewPosition.y - lastPaintPosition.y; + boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, + cachedBlitSize, cachedBlitPaint); + if (canBlit) + { + // Copy the part that remains visible during scrolling. + g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, + cachedBlitSize.width, cachedBlitSize.height, + cachedBlitTo.x - cachedBlitFrom.x, + cachedBlitTo.y - cachedBlitFrom.y); + // Now paint the part that becomes newly visible. + g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y, + cachedBlitPaint.width, cachedBlitPaint.height); + paintSimple(g2); + } + // If blitting is not possible for some reason, fall back to repainting + // everything. + else + { + paintSimple(g2); + } + g2.dispose(); + } + // Actually draw the backingstore image to the graphics context. + g.drawImage(backingStoreImage, 0, 0, this); + // Update the lastPaintPosition so that we know what is already drawn when + // we paint the next time. + lastPaintPosition.setLocation(getViewPosition()); + } + + /** + * Paints the viewport in case we have a scrollmode of + * {@link #BLIT_SCROLL_MODE}. + * + * This paints the viewport using a backingstore and a blitting algorithm. + * Only the newly exposed area of the view is painted from the view painting + * methods, the remainder is copied from the backing store. + * + * @param g the graphics context to use + */ + void paintBlit(Graphics g) + { + // We cannot perform blitted painting as it is described in Sun's API docs. + // There it is suggested that this painting method should blit directly + // on the parent window's surface. This is not possible because when using + // Swing's double buffering (at least our implementation), it would + // immediatly be painted when the buffer is painted on the screen. For this + // to work we would need a kind of hole in the buffer image. And honestly + // I find this method not very elegant. + // The alternative, blitting directly on the buffer image, is also not + // possible because the buffer image gets cleared everytime when an opaque + // parent component is drawn on it. + + // What we do instead is falling back to the backing store approach which + // is in fact a mixed blitting/backing store approach where the blitting + // is performed on the backing store image and this is then drawn to the + // graphics context. This is very robust and works independent of the + // painting mechanism that is used by Swing. And it should have comparable + // performance characteristics as the blitting method. + paintBackingStore(g); } } diff --git a/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java index 449900370c6..cc0ac7fd95a 100644 --- a/libjava/classpath/javax/swing/JWindow.java +++ b/libjava/classpath/javax/swing/JWindow.java @@ -60,6 +60,21 @@ import javax.accessibility.AccessibleContext; */ public class JWindow extends Window implements Accessible, RootPaneContainer { + /** + * Provides accessibility support for <code>JWindow</code>. + */ + protected class AccessibleJWindow extends Window.AccessibleAWTWindow + { + /** + * Creates a new instance of <code>AccessibleJWindow</code>. + */ + public AccessibleJWindow() + { + super(); + // Nothing to do here. + } + } + private static final long serialVersionUID = 5420698392125238833L; protected JRootPane rootPane; @@ -71,13 +86,6 @@ public class JWindow extends Window implements Accessible, RootPaneContainer protected AccessibleContext accessibleContext; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - public JWindow() { super(SwingUtilities.getOwnerFrame()); @@ -113,7 +121,7 @@ public class JWindow extends Window implements Accessible, RootPaneContainer super.setLayout(new BorderLayout(1, 1)); getRootPane(); // will do set/create // Now we're done init stage, adds and layouts go to content pane. - initStageDone = true; + setRootPaneCheckingEnabled(true); } public Dimension getPreferredSize() @@ -125,13 +133,8 @@ public class JWindow extends Window implements Accessible, RootPaneContainer { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -192,15 +195,10 @@ public class JWindow extends Window implements Accessible, RootPaneContainer { // If we're adding in the initialization stage use super.add. // otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JWindow directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } public void remove(Component comp) @@ -235,7 +233,9 @@ public class JWindow extends Window implements Accessible, RootPaneContainer public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJWindow(); + return accessibleContext; } protected String paramString() diff --git a/libjava/classpath/javax/swing/KeyStroke.java b/libjava/classpath/javax/swing/KeyStroke.java index 12a280c217a..b57a7119207 100644 --- a/libjava/classpath/javax/swing/KeyStroke.java +++ b/libjava/classpath/javax/swing/KeyStroke.java @@ -51,6 +51,7 @@ public class KeyStroke // Called by java.awt.AWTKeyStroke.registerSubclass via reflection. private KeyStroke() { + // Nothing to do here. } private KeyStroke(char keyChar, int keyCode, int modifiers, diff --git a/libjava/classpath/javax/swing/KeyboardManager.java b/libjava/classpath/javax/swing/KeyboardManager.java new file mode 100644 index 00000000000..d3868309d08 --- /dev/null +++ b/libjava/classpath/javax/swing/KeyboardManager.java @@ -0,0 +1,280 @@ +/* KeyboardManager.java -- + Copyright (C) 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 javax.swing; + +import java.applet.Applet; +import java.awt.Component; +import java.awt.Container; +import java.awt.Window; +import java.awt.event.KeyEvent; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +/** + * This class maintains a mapping from top-level containers to a + * Hashtable. The Hashtable maps KeyStrokes to Components to be used when + * Components register keyboard actions with the condition + * JComponent.WHEN_IN_FOCUSED_WINDOW. + * + * @author Anthony Balkissoon <abalkiss@redhat.com> + * + */ +class KeyboardManager +{ + /** Shared instance of KeyboardManager **/ + static KeyboardManager manager = new KeyboardManager(); + + /** + * A mapping between top level containers and Hashtables that + * map KeyStrokes to Components. + */ + Hashtable topLevelLookup = new Hashtable(); + + /** + * A mapping between top level containers and Vectors of JMenuBars + * used to allow all the JMenuBars within a top level container + * a chance to consume key events. + */ + Hashtable menuBarLookup = new Hashtable(); + /** + * Returns the shared instance of KeyboardManager. + * @return the shared instance of KeybaordManager. + */ + public static KeyboardManager getManager() + { + return manager; + } + /** + * Returns the top-level ancestor for the given JComponent. + * @param c the JComponent whose top-level ancestor we want + * @return the top-level ancestor for the given JComponent. + */ + static Container findTopLevel (Component c) + { + Container topLevel = (c instanceof Container) ? (Container) c + : c.getParent(); + while (topLevel != null && + !(topLevel instanceof Window) && + !(topLevel instanceof Applet) && + !(topLevel instanceof JInternalFrame)) + topLevel = topLevel.getParent(); + return topLevel; + } + + /** + * Returns the Hashtable that maps KeyStrokes to Components, for + * the specified top-level container c. If no Hashtable exists + * we create and register it here and return the newly created + * Hashtable. + * + * @param c the top-level container whose Hashtable we want + * @return the Hashtable mapping KeyStrokes to Components for the + * specified top-level container + */ + Hashtable getHashtableForTopLevel (Container c) + { + Hashtable keyToComponent = (Hashtable)topLevelLookup.get(c); + if (keyToComponent == null) + { + keyToComponent = new Hashtable(); + topLevelLookup.put(c, keyToComponent); + } + return keyToComponent; + } + + /** + * Registers a KeyStroke with a Component. This does not register + * the KeyStroke to a specific Action. When searching for a + * WHEN_IN_FOCUSED_WINDOW binding we will first go up to the focused + * top-level Container, then get the Hashtable that maps KeyStrokes + * to components for that particular top-level Container, then + * call processKeyBindings on that component with the condition + * JComponent.WHEN_IN_FOCUSED_WINDOW. + * @param comp the JComponent associated with the KeyStroke + * @param key the KeyStroke + */ + public void registerBinding(JComponent comp, KeyStroke key) + { + // This method associates a KeyStroke with a particular JComponent + // When the KeyStroke occurs, if this component's top-level ancestor + // has focus (one of its children is the focused Component) then + // comp.processKeyBindings will be called with condition + // JComponent.WHEN_IN_FOCUSED_WINDOW. + + // Look for the JComponent's top-level parent and return if it is null + Container topLevel = findTopLevel(comp); + if (topLevel == null) + return; + + // Now get the Hashtable for this top-level container + Hashtable keyToComponent = getHashtableForTopLevel(topLevel); + + // And add the new binding to this Hashtable + // FIXME: should allow more than one JComponent to be associated + // with a KeyStroke, in case one of them is disabled + keyToComponent.put(key, comp); + } + + public void clearBindingsForComp(JComponent comp) + { + // This method clears all the WHEN_IN_FOCUSED_WINDOW bindings associated + // with <code>comp</code>. This is used for a terribly ineffcient + // strategy in which JComponent.updateComponentInputMap simply clears + // all bindings associated with its component and then reloads all the + // bindings from the updated ComponentInputMap. This is only a preliminary + // strategy and should be improved upon once the WHEN_IN_FOCUSED_WINDOW + // bindings work. + + // Find the top-level ancestor + + Container topLevel = findTopLevel(comp); + if (topLevel == null) + return; + // And now get its Hashtable + Hashtable keyToComponent = getHashtableForTopLevel(topLevel); + + Enumeration keys = keyToComponent.keys(); + Object temp; + + // Iterate through the keys and remove any key whose value is comp + while (keys.hasMoreElements()) + { + temp = keys.nextElement(); + if (comp == (JComponent)keyToComponent.get(temp)) + keyToComponent.remove(temp); + } + } + + /** + * This method registers all the bindings in the given ComponentInputMap. + * Rather than call registerBinding on all the keys, we do the work here + * so that we don't duplicate finding the top-level container and + * getting its Hashtable. + * + * @param map the ComponentInputMap whose bindings we want to register + */ + public void registerEntireMap (ComponentInputMap map) + { + if (map == null) + return; + JComponent comp = map.getComponent(); + KeyStroke[] keys = map.keys(); + if (keys == null) + return; + // Find the top-level container associated with this ComponentInputMap + Container topLevel = findTopLevel(comp); + if (topLevel == null) + return; + + // Register the KeyStrokes in the top-level container's Hashtable + Hashtable keyToComponent = getHashtableForTopLevel(topLevel); + for (int i = 0; i < keys.length; i++) + keyToComponent.put(keys[i], comp); + } + + public boolean processKeyStroke (Component comp, KeyStroke key, KeyEvent e) + { + boolean pressed = e.getID() == KeyEvent.KEY_PRESSED; + + // Look for the top-level ancestor + Container topLevel = findTopLevel(comp); + if (topLevel == null) + return false; + // Now get the Hashtable for that top-level container + Hashtable keyToComponent = getHashtableForTopLevel(topLevel); + Enumeration keys = keyToComponent.keys(); + JComponent target = (JComponent)keyToComponent.get(key); + if (target != null && target.processKeyBinding + (key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed)) + return true; + + // Have to give all the JMenuBars a chance to consume the event + Vector menuBars = getVectorForTopLevel(topLevel); + for (int i = 0; i < menuBars.size(); i++) + if (((JMenuBar)menuBars.elementAt(i)).processKeyBinding(key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed)) + return true; + return false; + } + + /** + * Returns the Vector of JMenuBars associated with the top-level + * @param c the top-level container whose JMenuBar Vector we want + * @return the Vector of JMenuBars for this top level container + */ + Vector getVectorForTopLevel(Container c) + { + Vector result = (Vector) menuBarLookup.get(c); + if (result == null) + { + result = new Vector(); + menuBarLookup.put (c, result); + } + return result; + } + + /** + * In processKeyStroke, KeyManager must give all JMenuBars in the + * focused top-level container a chance to process the event. So, + * JMenuBars must be registered in KeyManager and associated with a + * top-level container. That's what this method is for. + * @param menuBar the JMenuBar to register + */ + public void registerJMenuBar (JMenuBar menuBar) + { + Container topLevel = findTopLevel(menuBar); + Vector menuBars = getVectorForTopLevel(topLevel); + if (!menuBars.contains(menuBar)) + menuBars.add(menuBar); + } + + /** + * Unregisters a JMenuBar from its top-level container. This is + * called before the JMenuBar is actually removed from the container + * so findTopLevel will still find us the correct top-level container. + * @param menuBar the JMenuBar to unregister. + */ + public void unregisterJMenuBar (JMenuBar menuBar) + { + Container topLevel = findTopLevel(menuBar); + Vector menuBars = getVectorForTopLevel(topLevel); + if (menuBars.contains(menuBar)) + menuBars.remove(menuBar); + } +} diff --git a/libjava/classpath/javax/swing/ListCellRenderer.java b/libjava/classpath/javax/swing/ListCellRenderer.java index 6ce115ea70c..e234d184dfd 100644 --- a/libjava/classpath/javax/swing/ListCellRenderer.java +++ b/libjava/classpath/javax/swing/ListCellRenderer.java @@ -44,9 +44,7 @@ import java.awt.Component; */ public interface ListCellRenderer { - Component getListCellRendererComponent(JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus); + Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, + boolean cellHasFocus); } diff --git a/libjava/classpath/javax/swing/ListSelectionModel.java b/libjava/classpath/javax/swing/ListSelectionModel.java index f4680d737fb..324c056431b 100644 --- a/libjava/classpath/javax/swing/ListSelectionModel.java +++ b/libjava/classpath/javax/swing/ListSelectionModel.java @@ -46,41 +46,51 @@ import javax.swing.event.ListSelectionListener; */ public interface ListSelectionModel { + int SINGLE_SELECTION = 0; + int SINGLE_INTERVAL_SELECTION = 1; + int MULTIPLE_INTERVAL_SELECTION = 2; void setSelectionMode(int a); + int getSelectionMode(); - + void clearSelection(); - + int getMinSelectionIndex(); + int getMaxSelectionIndex(); boolean isSelectedIndex(int a); boolean isSelectionEmpty(); + void setSelectionInterval(int index0, int index1); - void addSelectionInterval(int index0, - int index1); - void removeSelectionInterval(int index0, - int index1); - void insertIndexInterval(int index, - int length, - boolean before); - void removeIndexInterval(int index0, - int index1); + + void addSelectionInterval(int index0, int index1); + + void removeSelectionInterval(int index0, int index1); + + void insertIndexInterval(int index, int length, boolean before); + + void removeIndexInterval(int index0, int index1); int getAnchorSelectionIndex(); + void setAnchorSelectionIndex(int index); + int getLeadSelectionIndex(); + void setLeadSelectionIndex(int index); void setValueIsAdjusting(boolean valueIsAdjusting); + boolean getValueIsAdjusting(); void addListSelectionListener(ListSelectionListener listener); - void removeListSelectionListener(ListSelectionListener listener); + + void removeListSelectionListener(ListSelectionListener listener); } diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java index 8858742711d..1a67e849735 100644 --- a/libjava/classpath/javax/swing/LookAndFeel.java +++ b/libjava/classpath/javax/swing/LookAndFeel.java @@ -38,9 +38,17 @@ exception statement from your version. */ package javax.swing; +import java.awt.Color; import java.awt.Component; +import java.awt.Font; import java.awt.Toolkit; +import java.net.URL; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentInputMapUIResource; +import javax.swing.plaf.IconUIResource; +import javax.swing.plaf.InputMapUIResource; +import javax.swing.plaf.UIResource; import javax.swing.text.JTextComponent; public abstract class LookAndFeel @@ -104,6 +112,8 @@ public abstract class LookAndFeel */ public void initialize() { + // We do nothing here. This method is meant to be overridden by + // LookAndFeel implementations. } /** @@ -113,14 +123,27 @@ public abstract class LookAndFeel */ public static void installBorder(JComponent c, String defaultBorderName) { + Border b = c.getBorder(); + if (b == null || b instanceof UIResource) + c.setBorder(UIManager.getBorder(defaultBorderName)); } /** * Convenience method for initializing a component's foreground and * background color properties with values from the current defaults table. */ - public static void installColors(JComponent c, String defaultBgName, String defaultFgName) + public static void installColors(JComponent c, String defaultBgName, + String defaultFgName) { + // Install background. + Color bg = c.getBackground(); + if (bg == null || bg instanceof UIResource) + c.setBackground(UIManager.getColor(defaultBgName)); + + // Install foreground. + Color fg = c.getForeground(); + if (fg == null || fg instanceof UIResource) + c.setForeground(UIManager.getColor(defaultFgName)); } /** @@ -128,10 +151,16 @@ public abstract class LookAndFeel * and font properties with values from the current defaults table. */ public static void installColorsAndFont(JComponent component, - String defaultBgName, - String defaultFgName, - String defaultFontName) + String defaultBgName, + String defaultFgName, + String defaultFontName) { + // Install colors. + installColors(component, defaultBgName, defaultFgName); + // Install font. + Font f = component.getFont(); + if (f == null || f instanceof UIResource) + component.setFont(UIManager.getFont(defaultFontName)); } /** @@ -156,19 +185,47 @@ public abstract class LookAndFeel public abstract boolean isSupportedLookAndFeel(); /** - * Loads the bindings in keys into retMap. + * Loads the bindings in keys into retMap. Does not remove existing entries + * from retMap. <code>keys</code> describes the InputMap, every even indexed + * item is either a KeyStroke or a String representing a KeyStroke and every + * odd indexed item is the Object associated with that KeyStroke in an + * ActionMap. + * + * @param retMap the InputMap into which we load bindings + * @param keys the Object array describing the InputMap as above */ public static void loadKeyBindings(InputMap retMap, Object[] keys) { + if (keys == null) + return; + for (int i = 0; i < keys.length - 1; i+= 2) + { + Object key = keys[i]; + KeyStroke keyStroke; + if (key instanceof KeyStroke) + keyStroke = (KeyStroke)key; + else + keyStroke = KeyStroke.getKeyStroke((String)key); + retMap.put(keyStroke, keys[i+1]); + } } /** - * Creates a ComponentInputMap from keys. + * Creates a ComponentInputMap from keys. + * <code>keys</code> describes the InputMap, every even indexed + * item is either a KeyStroke or a String representing a KeyStroke and every + * odd indexed item is the Object associated with that KeyStroke in an + * ActionMap. + * + * @param c the JComponent associated with the ComponentInputMap + * @param keys the Object array describing the InputMap as above */ public static ComponentInputMap makeComponentInputMap(JComponent c, Object[] keys) { - return null; + ComponentInputMap retMap = new ComponentInputMapUIResource(c); + loadKeyBindings(retMap, keys); + return retMap; } /** @@ -177,23 +234,55 @@ public abstract class LookAndFeel */ public static Object makeIcon(Class baseClass, String gifFile) { - return null; + final URL file = baseClass.getResource(gifFile); + return new UIDefaults.LazyValue() + { + public Object createValue(UIDefaults table) + { + return new IconUIResource(new ImageIcon(file)); + } + }; } /** * Creates a InputMap from keys. + * <code>keys</code> describes the InputMap, every even indexed + * item is either a KeyStroke or a String representing a KeyStroke and every + * odd indexed item is the Object associated with that KeyStroke in an + * ActionMap. + * + * @param keys the Object array describing the InputMap as above */ public static InputMap makeInputMap(Object[] keys) { - return null; + InputMap retMap = new InputMapUIResource(); + loadKeyBindings(retMap, keys); + return retMap; } /** - * Convenience method for building lists of KeyBindings. + * Convenience method for building lists of KeyBindings. + * <code>keyBindingList</code> is an array of KeyStroke-Action pairs where + * even indexed elements are KeyStrokes or Strings representing KeyStrokes + * and odd indexed elements are the associated Actions. + * + * @param keyBindingList the array of KeyStroke-Action pairs + * @return a JTextComponent.KeyBinding array */ public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList) { - return null; + JTextComponent.KeyBinding[] retBindings = + new JTextComponent.KeyBinding[keyBindingList.length / 2]; + for (int i = 0; i < keyBindingList.length - 1; i+= 2) + { + KeyStroke stroke; + if (keyBindingList[i] instanceof KeyStroke) + stroke = (KeyStroke)keyBindingList[i]; + else + stroke = KeyStroke.getKeyStroke((String)keyBindingList[i]); + retBindings[i/2] = new JTextComponent.KeyBinding(stroke, (String)keyBindingList[i+1]); + } + return retBindings; } /** @@ -224,6 +313,8 @@ public abstract class LookAndFeel */ public void uninitialize() { + // We do nothing here. This method is meant to be overridden by + // LookAndFeel implementations. } /** @@ -232,5 +323,7 @@ public abstract class LookAndFeel */ public static void uninstallBorder(JComponent c) { + if (c.getBorder() instanceof UIResource) + c.setBorder(null); } } diff --git a/libjava/classpath/javax/swing/MenuElement.java b/libjava/classpath/javax/swing/MenuElement.java index 46eb8c2a5fe..dab7b9cf1ad 100644 --- a/libjava/classpath/javax/swing/MenuElement.java +++ b/libjava/classpath/javax/swing/MenuElement.java @@ -47,47 +47,43 @@ import java.awt.event.MouseEvent; * * @author Andrew Selkirk */ -public interface MenuElement { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * processMouseEvent - * @param event TODO - * @param path TODO - * @param manager TODO - */ - void processMouseEvent(MouseEvent event, - MenuElement[] path, MenuSelectionManager manager); - - /** - * processKeyEvent - * @param event TODO - * @param path TODO - * @param manager TODO - */ - void processKeyEvent(KeyEvent event, - MenuElement[] path, MenuSelectionManager manager); - - /** - * menuSelectionChanged - * @param included TODO - */ - void menuSelectionChanged(boolean included); - - /** - * getSubElements - * @returns MenuElement[] - */ - MenuElement[] getSubElements(); - - /** - * getComponent - * @returns Component - */ - Component getComponent(); - - -} // MenuElement +public interface MenuElement +{ + + /** + * processMouseEvent + * @param event TODO + * @param path TODO + * @param manager TODO + */ + void processMouseEvent(MouseEvent event, MenuElement[] path, + MenuSelectionManager manager); + + /** + * processKeyEvent + * @param event TODO + * @param path TODO + * @param manager TODO + */ + void processKeyEvent(KeyEvent event, MenuElement[] path, + MenuSelectionManager manager); + + /** + * menuSelectionChanged + * @param included TODO + */ + void menuSelectionChanged(boolean included); + + /** + * getSubElements + * @returns MenuElement[] + */ + MenuElement[] getSubElements(); + + /** + * getComponent + * @returns Component + */ + Component getComponent(); + +} diff --git a/libjava/classpath/javax/swing/MenuSelectionManager.java b/libjava/classpath/javax/swing/MenuSelectionManager.java index 32d56b958a1..4e52751065a 100644 --- a/libjava/classpath/javax/swing/MenuSelectionManager.java +++ b/libjava/classpath/javax/swing/MenuSelectionManager.java @@ -146,7 +146,9 @@ public class MenuSelectionManager { // Convert sourcePoint to screen coordinates. Point sourcePointOnScreen = sourcePoint; - SwingUtilities.convertPointToScreen(sourcePointOnScreen, source); + + if (source.isShowing()) + SwingUtilities.convertPointToScreen(sourcePointOnScreen, source); Point compPointOnScreen; Component resultComp = null; @@ -168,7 +170,10 @@ public class MenuSelectionManager && sourcePointOnScreen.y < compPointOnScreen.y + size.height) { Point p = sourcePointOnScreen; - SwingUtilities.convertPointFromScreen(p, comp); + + if (comp.isShowing()) + SwingUtilities.convertPointFromScreen(p, comp); + resultComp = SwingUtilities.getDeepestComponentAt(comp, p.x, p.y); break; } diff --git a/libjava/classpath/javax/swing/MutableComboBoxModel.java b/libjava/classpath/javax/swing/MutableComboBoxModel.java index ee79dac03a5..93091786e53 100644 --- a/libjava/classpath/javax/swing/MutableComboBoxModel.java +++ b/libjava/classpath/javax/swing/MutableComboBoxModel.java @@ -76,7 +76,7 @@ public interface MutableComboBoxModel extends ComboBoxModel /** * This method removes given element from the data model * - * @param element to remove. + * @param object element to remove. */ void removeElement(Object object); -} // MutableComboBoxModel +} diff --git a/libjava/classpath/javax/swing/OverlayLayout.java b/libjava/classpath/javax/swing/OverlayLayout.java index e8aef98a521..56b8c8bb67a 100644 --- a/libjava/classpath/javax/swing/OverlayLayout.java +++ b/libjava/classpath/javax/swing/OverlayLayout.java @@ -1,5 +1,5 @@ -/* OverlayLayout.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* OverlayLayout.java -- A layout manager + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,155 +37,376 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTError; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Insets; import java.awt.LayoutManager2; import java.io.Serializable; /** - * OverlayLayout - * @author Andrew Selkirk - * @version 1.0 + * A layout manager that lays out the components of a container one over + * another. + * + * The components take as much space as is available in the container, but not + * more than specified by their maximum size. + * + * The overall layout is mainly affected by the components + * <code>alignmentX</code> and <code>alignmentY</code> properties. All + * components are aligned, so that their alignment points (for either + * direction) are placed in one line (the baseline for this direction). + * + * For example: An X alignment of 0.0 means that the component's alignment + * point is at it's left edge, an X alignment of 0.5 means that the alignment + * point is in the middle, an X alignment of 1.0 means, the aligment point is + * at the right edge. So if you have three components, the first with 0.0, the + * second with 0.5 and the third with 1.0, then they are laid out like this: + * + * <pre> + * +-------+ + * | 1 | + * +-------+ + * +-------+ + * | 2 | + * +-------+ + * +---------+ + * | 3 + + * +---------+ + * </pre> + * The above picture shows the X alignment between the components. An Y + * alignment like shown above cannot be achieved with this layout manager. The + * components are place on top of each other, with the X alignment shown above. + * + * @author Roman Kennke (kennke@aicas.com) + * @author Andrew Selkirk */ -public class OverlayLayout - implements LayoutManager2, Serializable +public class OverlayLayout implements LayoutManager2, Serializable { private static final long serialVersionUID = 18082829169631543L; - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * target - */ - private Container target; - - /** - * xChildren - */ - private SizeRequirements[] xChildren; - - /** - * yChildren - */ - private SizeRequirements[] yChildren; - - /** - * xTotal - */ - private SizeRequirements xTotal; - - /** - * yTotal - */ - private SizeRequirements yTotal; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor OverlayLayout - * @param target TODO - */ - public OverlayLayout(Container target) { - // TODO - } // OverlayLayout() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * invalidateLayout - * @param target TODO - */ - public void invalidateLayout(Container target) { - // TODO - } // invalidateLayout() - - /** - * addLayoutComponent - * @param string TODO - * @param component TODO - */ - public void addLayoutComponent(String string, Component component) { - // TODO - } // addLayoutComponent() - - /** - * addLayoutComponent - * @param component TODO - * @param constraints TODO - */ - public void addLayoutComponent(Component component, Object constraints) { - // TODO - } // addLayoutComponent() - - /** - * removeLayoutComponent - * @param component TODO - */ - public void removeLayoutComponent(Component component) { - // TODO - } // removeLayoutComponent() - - /** - * preferredLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension preferredLayoutSize(Container target) { - return null; // TODO - } // preferredLayoutSize() - - /** - * minimumLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension minimumLayoutSize(Container target) { - return null; // TODO - } // minimumLayoutSize() - - /** - * maximumLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension maximumLayoutSize(Container target) { - return null; // TODO - } // maximumLayoutSize() - - /** - * getLayoutAlignmentX - * @param target TODO - * @returns float - */ - public float getLayoutAlignmentX(Container target) { - return (float) 0.0; // TODO - } // getLayoutAlignmentX() - - /** - * getLayoutAlignmentY - * @param target TODO - * @returns float - */ - public float getLayoutAlignmentY(Container target) { - return (float) 0.0; // TODO - } // getLayoutAlignmentY() - - /** - * layoutContainer - * @param target TODO - */ - public void layoutContainer(Container target) { - // TODO - } // layoutContainer() - - -} // OverlayLayout + /** + * The container to be laid out. + */ + private Container target; + + /** + * The size requirements of the containers children for the X direction. + */ + private SizeRequirements[] xChildren; + + /** + * The size requirements of the containers children for the Y direction. + */ + private SizeRequirements[] yChildren; + + /** + * The size requirements of the container to be laid out for the X direction. + */ + private SizeRequirements xTotal; + + /** + * The size requirements of the container to be laid out for the Y direction. + */ + private SizeRequirements yTotal; + + /** + * The offsets of the child components in the X direction. + */ + private int[] offsetsX; + + /** + * The offsets of the child components in the Y direction. + */ + private int[] offsetsY; + + /** + * The spans of the child components in the X direction. + */ + private int[] spansX; + + /** + * The spans of the child components in the Y direction. + */ + private int[] spansY; + + /** + * Creates a new OverlayLayout for the specified container. + * + * @param target the container to be laid out + */ + public OverlayLayout(Container target) + { + this.target = target; + } + + /** + * Notifies the layout manager that the layout has become invalid. It throws + * away cached layout information and recomputes it the next time it is + * requested. + * + * @param target not used here + */ + public void invalidateLayout(Container target) + { + xChildren = null; + yChildren = null; + xTotal = null; + yTotal = null; + offsetsX = null; + offsetsY = null; + spansX = null; + spansY = null; + } + + /** + * This method is not used in this layout manager. + * + * @param string not used here + * @param component not used here + */ + public void addLayoutComponent(String string, Component component) + { + // Nothing to do here. + } + + /** + * This method is not used in this layout manager. + * + * @param component not used here + * @param constraints not used here + */ + public void addLayoutComponent(Component component, Object constraints) + { + // Nothing to do here. + } + + /** + * This method is not used in this layout manager. + * + * @param component not used here + */ + public void removeLayoutComponent(Component component) + { + // Nothing to do here. + } + + /** + * Returns the preferred size of the container that is laid out. This is + * computed by the children's preferred sizes, taking their alignments into + * account. + * + * @param target not used here + * + * @returns the preferred size of the container that is laid out + */ + public Dimension preferredLayoutSize(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkTotalRequirements(); + return new Dimension(xTotal.preferred, yTotal.preferred); + } + + /** + * Returns the minimum size of the container that is laid out. This is + * computed by the children's minimum sizes, taking their alignments into + * account. + * + * @param target not used here + * + * @returns the minimum size of the container that is laid out + */ + public Dimension minimumLayoutSize(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkTotalRequirements(); + return new Dimension(xTotal.minimum, yTotal.minimum); + } + + /** + * Returns the maximum size of the container that is laid out. This is + * computed by the children's maximum sizes, taking their alignments into + * account. + * + * @param target not used here + * + * @returns the maximum size of the container that is laid out + */ + public Dimension maximumLayoutSize(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkTotalRequirements(); + return new Dimension(xTotal.maximum, yTotal.maximum); + } + + /** + * Returns the X alignment of the container that is laid out. This is + * computed by the children's preferred sizes, taking their alignments into + * account. + * + * @param target not used here + * + * @returns the X alignment of the container that is laid out + */ + public float getLayoutAlignmentX(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkTotalRequirements(); + return xTotal.alignment; + } + + /** + * Returns the Y alignment of the container that is laid out. This is + * computed by the children's preferred sizes, taking their alignments into + * account. + * + * @param target not used here + * + * @returns the X alignment of the container that is laid out + */ + public float getLayoutAlignmentY(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkTotalRequirements(); + return yTotal.alignment; + } + + /** + * Lays out the container and it's children. + * + * The children are laid out one over another. + * + * The components take as much space as is available in the container, but + * not more than specified by their maximum size. + * + * The overall layout is mainly affected by the components + * <code>alignmentX</code> and <code>alignmentY</code> properties. All + * components are aligned, so that their alignment points (for either + * direction) are placed in one line (the baseline for this direction). + * + * For example: An X alignment of 0.0 means that the component's alignment + * point is at it's left edge, an X alignment of 0.5 means that the alignment + * point is in the middle, an X alignment of 1.0 means, the aligment point is + * at the right edge. So if you have three components, the first with 0.0, + * the second with 0.5 and the third with 1.0, then they are laid out like + * this: + * + * <pre> + * +-------+ + * | 1 | + * +-------+ + * +-------+ + * | 2 | + * +-------+ + * +---------+ + * | 3 + + * +---------+ + * </pre> + * The above picture shows the X alignment between the components. An Y + * alignment like shown above cannot be achieved with this layout manager. + * The components are place on top of each other, with the X alignment shown + * above. + * + * @param target not used here + */ + public void layoutContainer(Container target) + { + if (target != this.target) + throw new AWTError("OverlayLayout can't be shared"); + + checkLayout(); + Component[] children = target.getComponents(); + for (int i = 0; i < children.length; i++) + children[i].setBounds(offsetsX[i], offsetsY[i], spansX[i], spansY[i]); + } + + /** + * Makes sure that the xChildren and yChildren fields are correctly set up. + * A call to {@link #invalidateLayout(Container)} sets these fields to null, + * so they have to be set up again. + */ + private void checkRequirements() + { + if (xChildren == null || yChildren == null) + { + Component[] children = target.getComponents(); + xChildren = new SizeRequirements[children.length]; + yChildren = new SizeRequirements[children.length]; + for (int i = 0; i < children.length; i++) + { + if (! children[i].isVisible()) + { + xChildren[i] = new SizeRequirements(); + yChildren[i] = new SizeRequirements(); + } + else + { + xChildren[i] = + new SizeRequirements(children[i].getMinimumSize().width, + children[i].getPreferredSize().width, + children[i].getMaximumSize().width, + children[i].getAlignmentX()); + yChildren[i] = + new SizeRequirements(children[i].getMinimumSize().height, + children[i].getPreferredSize().height, + children[i].getMaximumSize().height, + children[i].getAlignmentY()); + } + } + } + } + + /** + * Makes sure that the xTotal and yTotal fields are set up correctly. A call + * to {@link #invalidateLayout} sets these fields to null and they have to be + * recomputed. + */ + private void checkTotalRequirements() + { + if (xTotal == null || yTotal == null) + { + checkRequirements(); + xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren); + yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren); + } + } + + /** + * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set + * up correctly. A call to {@link #invalidateLayout} sets these fields + * to null and they have to be recomputed. + */ + private void checkLayout() + { + if (offsetsX == null || offsetsY == null || spansX == null + || spansY == null) + { + checkRequirements(); + checkTotalRequirements(); + int len = target.getComponents().length; + offsetsX = new int[len]; + offsetsY = new int[len]; + spansX = new int[len]; + spansY = new int[len]; + + Insets in = target.getInsets(); + int width = target.getWidth() - in.left - in.right; + int height = target.getHeight() - in.top - in.bottom; + + SizeRequirements.calculateAlignedPositions(width, xTotal, + xChildren, offsetsX, spansX); + SizeRequirements.calculateAlignedPositions(height, yTotal, + yChildren, offsetsY, spansY); + } + } +} diff --git a/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java index 69e1f516802..cbb243e285a 100644 --- a/libjava/classpath/javax/swing/Popup.java +++ b/libjava/classpath/javax/swing/Popup.java @@ -39,6 +39,8 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Point; /** @@ -89,6 +91,7 @@ public class Popup */ protected Popup() { + // Nothing to do here. } @@ -129,6 +132,7 @@ public class Popup */ JWindow window; + private Component contents; /** * Constructs a new <code>JWindowPopup</code> given its owner, @@ -155,10 +159,11 @@ public class Popup /* Checks whether contents is null. */ super(owner, contents, x, y); + this.contents = contents; window = new JWindow(); - window.getRootPane().add(contents); + window.getContentPane().add(contents); window.setLocation(x, y); - window.pack(); + window.setFocusableWindowState(false); } @@ -168,6 +173,7 @@ public class Popup */ public void show() { + window.setSize(contents.getSize()); window.show(); } @@ -186,4 +192,106 @@ public class Popup window.dispose(); } } + + /** + * A popup that displays itself within the JLayeredPane of a JRootPane of + * the containment hierarchy of the owner component. + * + * @author Roman Kennke (kennke@aicas.com) + */ + static class LightweightPopup extends Popup + { + /** + * The owner component for this popup. + */ + Component owner; + + /** + * The contents that should be shown. + */ + Component contents; + + /** + * The X location in screen coordinates. + */ + int x; + + /** + * The Y location in screen coordinates. + */ + int y; + + /** + * The panel that holds the content. + */ + private JPanel panel; + + /** + * The layered pane of the owner. + */ + private JLayeredPane layeredPane; + + /** + * Constructs a new <code>LightweightPopup</code> given its owner, + * contents and the screen position where the popup + * will appear. + * + * @param owner the component that should own the popup window; this + * provides the JRootPane in which we place the popup window + * + * @param contents the contents that will be displayed inside + * the <code>Popup</code>. + * + * @param x the horizontal position where the Popup will appear in screen + * coordinates + * + * @param y the vertical position where the Popup will appear in screen + * coordinates + * + * @throws IllegalArgumentException if <code>contents</code> + * is <code>null</code>. + */ + public LightweightPopup(Component owner, Component contents, int x, int y) + { + super(owner, contents, x, y); + this.owner = owner; + this.contents = contents; + this.x = x; + this.y = y; + + JRootPane rootPane = SwingUtilities.getRootPane(owner); + JLayeredPane layeredPane = rootPane.getLayeredPane(); + this.layeredPane = layeredPane; + } + + /** + * Places the popup within the JLayeredPane of the owner component and + * makes it visible. + */ + public void show() + { + // We insert a JPanel between the layered pane and the contents so we + // can fiddle with the setLocation() method without disturbing a + // JPopupMenu (which overrides setLocation in an unusual manner). + if (panel == null) + { + panel = new JPanel(); + panel.setLayout(new FlowLayout(0, 0, 0)); + } + + panel.add(contents); + panel.setSize(contents.getSize()); + Point layeredPaneLoc = layeredPane.getLocationOnScreen(); + panel.setLocation(x - layeredPaneLoc.x, y - layeredPaneLoc.y); + layeredPane.add(panel, JLayeredPane.POPUP_LAYER); + } + + /** + * Removes the popup from the JLayeredPane thus making it invisible. + */ + public void hide() + { + layeredPane.remove(panel); + } + } } diff --git a/libjava/classpath/javax/swing/PopupFactory.java b/libjava/classpath/javax/swing/PopupFactory.java index 29cf86d5530..7bb2529cd54 100644 --- a/libjava/classpath/javax/swing/PopupFactory.java +++ b/libjava/classpath/javax/swing/PopupFactory.java @@ -39,6 +39,8 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; /** @@ -55,8 +57,8 @@ public class PopupFactory /** * The shared factory object. * - * @see #getSharedFactory - * @see #setSharedFactory + * @see #getSharedInstance + * @see #setSharedInstance */ private static PopupFactory sharedFactory; @@ -69,6 +71,7 @@ public class PopupFactory */ public PopupFactory() { + // Nothing to do here. } @@ -134,6 +137,30 @@ public class PopupFactory public Popup getPopup(Component owner, Component contents, int x, int y) { - return new Popup.JWindowPopup(owner, contents, x, y); + Popup popup = null; + // By default we enable lightweight popups since they are more efficient + // than heavyweight popups. + boolean lightweightEnabled = true; + // Special case JPopupMenu here, since it supports a lightweightEnabled + // flag that we must respect. + if (contents instanceof JPopupMenu) + { + JPopupMenu menu = (JPopupMenu) contents; + lightweightEnabled = menu.isLightWeightPopupEnabled(); + } + + // If we have a root pane and the contents fits within the root pane and + // lightweight popups are enabled, than we can use a lightweight popup. + JRootPane root = SwingUtilities.getRootPane(owner); + Point rootLoc = root.getLocationOnScreen(); + Dimension contentsSize = contents.getSize(); + Dimension rootSize = root.getSize(); + if (x >= rootLoc.x && y > rootLoc.y + && (x - rootLoc.x) + contentsSize.width < rootSize.width + && (y - rootLoc.y) + contentsSize.height < rootSize.height) + popup = new Popup.LightweightPopup(owner, contents, x, y); + else + popup = new Popup.JWindowPopup(owner, contents, x, y); + return popup; } } diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java index 844258f1b90..60f1c7145c0 100644 --- a/libjava/classpath/javax/swing/ProgressMonitor.java +++ b/libjava/classpath/javax/swing/ProgressMonitor.java @@ -1,5 +1,5 @@ /* ProgressMonitor.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,191 +38,386 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; /** - * ProgressMonitor - * @author Andrew Selkirk - * @version 1.0 + * <p>Using this class you can easily monitor tasks where you cannot + * estimate the duration exactly.</p> + * + * <p>A ProgressMonitor instance waits until the first time setProgress + * is called. When <code>millisToDecideToPopup</code> time elapsed the + * instance estimates the duration until the whole operation is completed. + * If this duration exceeds <code>millisToPopup</code> a non-modal dialog + * with a message and a progress bar is shown.</p> + * + * <p>The value of <code>millisToDecideToPopup</code> defaults to + * <code>500</code> and <code>millisToPopup</code> to + * <code>2000</code>.</p> + * + * @author Andrew Selkirk + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.2 + * @status updated to 1.2 */ -public class ProgressMonitor { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * parentComponent - */ - private Component component; - - /** - * note - */ - private String note; - - /** - * message - */ - private Object message; - - /** - * millisToDecideToPopup - */ - private int millisToDecideToPopup; - - /** - * millisToPopup - */ - private int millisToPopup; - - /** - * min - */ - private int minimum; - - /** - * max - */ - private int maximum; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor ProgressMonitor - * @param component TODO - * @param message TODO - * @param note TODO - * @param minimum TODO - * @param maximum TODO - */ - public ProgressMonitor(Component component, Object message, - String note, int minimum, int maximum) { - - // Set Data - this.component = component; - this.message = message; - this.note = note; - this.minimum = minimum; - this.maximum = maximum; - - // TODO - } // ProgressMonitor() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * close - */ - public void close() { - // TODO - } // close() - - /** - * setProgress - * @param progress TODO - */ - public void setProgress(int progress) { - // TODO - } // setProgress() - - /** - * getMinimum - * @returns int - */ - public int getMinimum() { - return minimum; // TODO - } // getMinimum() - - /** - * setMinimum - * @param minimum TODO - */ - public void setMinimum(int minimum) { - this.minimum = minimum; - // TODO - } // setMinimum() - - /** - * getMaximum - * @returns int - */ - public int getMaximum() { - return maximum; // TODO - } // getMaximum() - - /** - * setMaximum - * @param maximum TODO - */ - public void setMaximum(int maximum) { - this.maximum = maximum; - // TODO - } // setMaximum() - - /** - * isCanceled - * @returns boolean - */ - public boolean isCanceled() { - return false; // TODO - } // isCanceled() - - /** - * getMillisToDecideToPopup - * @returns int - */ - public int getMillisToDecideToPopup() { - return millisToDecideToPopup; // TODO - } // getMillisToDecideToPopup() - - /** - * setMillisToDecideToPopup - * @param time TODO - */ - public void setMillisToDecideToPopup(int time) { - millisToDecideToPopup = time; - // TODO - } // setMillisToDecideToPopup() - - /** - * getMillisToPopup - * @returns int - */ - public int getMillisToPopup() { - return millisToPopup; // TODO - } // getMillisToPopup() - - /** - * setMillisToPopup - * @param time TODO - */ - public void setMillisToPopup(int time) { - millisToPopup = time; - // TODO - } // setMillisToPopup() - - /** - * getNote - * @returns String - */ - public String getNote() { - return note; // TODO - } // getNote() - - /** - * setNote - * @param note TODO - */ - public void setNote(String note) { - this.note = note; - // TODO - } // setNote() - - -} // ProgressMonitor +public class ProgressMonitor +{ + /** + * parentComponent + */ + Component component; + + /** + * note + */ + String note; + + /** + * message + */ + Object message; + + /** + * millisToDecideToPopup + */ + int millisToDecideToPopup = 500; + + /** + * millisToPopup + */ + int millisToPopup = 2000; + + int min, max, progress; + + JProgressBar progressBar; + + JLabel noteLabel; + + JDialog progressDialog; + + Timer timer; + + boolean canceled; + + /** + * Constructor ProgressMonitor + * @param component The parent component of the progress dialog or <code>null</code>. + * @param message A constant message object which works in the way it does in <code>JOptionPane</code>. + * @param note A string message which can be changed while the operation goes on. + * @param minimum The minimum value for the operation (start value). + * @param maximum The maximum value for the operation (end value). + */ + public ProgressMonitor(Component component, Object message, + String note, int minimum, int maximum) + { + + // Set data. + this.component = component; + this.message = message; + this.note = note; + + min = minimum; + max = maximum; + } + + /** + * <p>Hides the dialog and stops any measurements.</p> + * + * <p>Has no effect when <code>setProgress</code> is not at least + * called once.</p> + */ + public void close() + { + if ( progressDialog != null ) + { + progressDialog.setVisible(false); + } + + if ( timer != null ) + { + timer.stop(); + timer = null; + } + } + + /** + * <p>Updates the progress value.</p> + * + * <p>When called for the first time this initializes a timer + * which decides after <code>millisToDecideToPopup</code> time + * whether to show a progress dialog or not.</p> + * + * <p>If the progress value equals or exceeds the maximum + * value the progress dialog is closed automatically.</p> + * + * @param progress New progress value. + */ + public void setProgress(int progress) + { + this.progress = progress; + + // Initializes and starts a timer with a task + // which measures the duration and displays + // a progress dialog if neccessary. + if ( timer == null && progressDialog == null ) + { + timer = new Timer(25, null); + timer.addActionListener(new TimerListener()); + timer.start(); + } + + // Cancels timer and hides progress dialog if the + // maximum value is reached. + if ( progressBar != null && this.progress >= progressBar.getMaximum() ) + { + // The reason for using progressBar.getMaximum() instead of max is that + // we want to prevent that changes to the value have any effect after the + // progress dialog is visible (This is how the JDK behaves.). + close(); + } + + } + + /** Returns the minimum or start value of the operation. + * + * @returns Minimum or start value of the operation. + */ + public int getMinimum() + { + return min; + } + + /** + * <p>Use this method to set the minimum or start value of + * your operation.</p> + * + * <p>For typical application like copy operation this will be + * zero.</p> + * + * <p>Keep in mind that changing this value after the progress + * dialog is made visible has no effect upon the progress bar.</p> + * + * @param minimum The new minimum value. + */ + public void setMinimum(int minimum) + { + min = minimum; + } + + /** + * Return the maximum or end value of your operation. + * + * @returns Maximum or end value. + */ + public int getMaximum() + { + return max; + } + + /** + * <p>Sets the maximum or end value of the operation to the + * given integer.</p> + * + * @param maximum + */ + public void setMaximum(int maximum) + { + max = maximum; + } + + /** + * Returns whether the user canceled the operation. + * + * @returns Whether the operation was canceled. + */ + public boolean isCanceled() + { + // The value is predefined to false + // and changes only when the user clicks + // the cancel button in the progress dialog. + return canceled; + } + + /** + * Returns the amount of milliseconds to wait + * until the ProgressMonitor should decide whether + * a progress dialog is to be shown or not. + * + * @returns The duration in milliseconds. + */ + public int getMillisToDecideToPopup() + { + return millisToDecideToPopup; + } + + /** + * Sets the amount of milliseconds to wait until the + * ProgressMonitor should decide whether a progress dialog + * is to be shown or not. + * + * <p>This method has no effect when the progress dialog + * is already visible.</p> + * + * @param time The duration in milliseconds. + */ + public void setMillisToDecideToPopup(int time) + { + millisToDecideToPopup = time; + } + + /** + * getMillisToPopup + * @returns int + */ + public int getMillisToPopup() + { + return millisToPopup; + } + + /** + * setMillisToPopup + * @param time TODO + */ + public void setMillisToPopup(int time) + { + millisToPopup = time; + } + + /** + * Returns a message which is shown in the progress dialog. + * + * @returns The changeable message visible in the progress dialog. + */ + public String getNote() + { + return note; + } + + /** + * <p>Set the message shown in the progess dialog.</p> + * + * <p>Changing the note while the progress dialog is visible + * is possible.</p> + * + * @param note A message shown in the progress dialog. + */ + public void setNote(String note) + { + if ( noteLabel != null ) + { + noteLabel.setText(note); + } + else + { + this.note = note; + } + } + + /** Internal method that creates the progress dialog. + */ + void createDialog() + { + // If there is no note we suppress the generation of the + // label. + Object[] tmp = (note == null) ? + new Object[] + { + message, + progressBar = new JProgressBar(min, max) + } + : + new Object[] + { + message, + noteLabel = new JLabel(note), + progressBar = new JProgressBar(min, max) + }; + + JOptionPane pane = new JOptionPane(tmp, JOptionPane.INFORMATION_MESSAGE); + + // FIXME: Internationalize the button + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent ae) + { + canceled = true; + } + }); + + pane.setOptions(new Object[] { cancelButton }); + + // FIXME: Internationalize the title + progressDialog = pane.createDialog(component, "Progress ..."); + progressDialog.setModal(false); + progressDialog.setResizable(true); + + progressDialog.pack(); + progressDialog.setVisible(true); + + } + + /** An ActionListener implementation which does the measurements + * and estimations of the ProgressMonitor. + */ + class TimerListener implements ActionListener + { + long timestamp; + + int lastProgress; + + boolean first = true; + + TimerListener() + { + timestamp = System.currentTimeMillis(); + } + + public void actionPerformed(ActionEvent ae) + { + long now = System.currentTimeMillis(); + + if ( first ) + { + if (( now - timestamp ) > millisToDecideToPopup ) + { + first = false; + long expected = ( now - timestamp ) * ( max - min ) / ( progress - min ); + + if ( expected > millisToPopup ) + { + createDialog(); + } + } + else + { + // We have not waited long enough to make a decision, + // so return and try again when the timer is invoked. + return; + } + } + else if ( progressDialog != null ) + { + // The progress dialog is being displayed. We now calculate + // whether setting the progress bar to the current progress + // value would result in a visual difference. + int delta = progress - progressBar.getValue(); + + if ( ( delta * progressBar.getWidth() / (max - min) ) > 0 ) + { + // At least one pixel would change. + progressBar.setValue(progress); + } + } + else + { + // No dialog necessary + timer.stop(); + timer = null; + } + + timestamp = now; + } + } + +} diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java index 2022a1c24a3..02ac597b3a4 100644 --- a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java +++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java @@ -1,5 +1,5 @@ /* ProgressMonitorInputStream.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,122 +39,187 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; + import java.io.FilterInputStream; -import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.IOException; /** * ProgressMonitorInputStream * @author Andrew Selkirk - * @version 1.0 + * @author Robert Schuster (robertschuster@fsfe.org) + * @status updated to 1.2 + * @since 1.2 */ -public class ProgressMonitorInputStream extends FilterInputStream { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * monitor - */ - private ProgressMonitor monitor; - - /** - * nread - */ - private int nread; - - /** - * size - */ - private int size; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor ProgressMonitorInputStream - * @param component TODO - * @param message TODO - * @param stream TODO - */ - public ProgressMonitorInputStream(Component component, Object message, - InputStream stream) { - super(stream); - // TODO - } // ProgressMonitorInputStream() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * reset - * @exception IOException TODO - */ - public synchronized void reset() throws IOException { - // TODO - } // reset() - - /** - * read - * @exception IOException TODO - * @returns int - */ - public int read() throws IOException { - return 0; // TODO - } // read() - - /** - * read - * @param data TODO - * @exception IOException TODO - * @returns int - */ - public int read(byte[] data) throws IOException { - return 0; // TODO - } // read() - - /** - * read - * @param data TODO - * @param offset TODO - * @param length TODO - * @exception IOException TODO - * @returns int - */ - public int read(byte[] data, int offset, int length) throws IOException { - return 0; // TODO - } // read() - - /** - * skip - * @param length TODO - * @exception IOException TODO - * @returns long - */ - public long skip(long length) throws IOException { - return 0; // TODO - } // skip() - - /** - * close - * @exception IOException TODO - */ - public void close() throws IOException { - // TODO - } // close() - - /** - * getProgressMonitor - * @returns ProgressMonitor - */ - public ProgressMonitor getProgressMonitor() { - return null; // TODO - } // getProgressMonitor() - - -} // ProgressMonitorInputStream +public class ProgressMonitorInputStream extends FilterInputStream +{ + + /** + * monitor + */ + private ProgressMonitor monitor; + + /** + * read + */ + private int read; + + /** + * Constructor ProgressMonitorInputStream + * @param component TODO + * @param message TODO + * @param stream TODO + */ + public ProgressMonitorInputStream(Component component, Object message, + InputStream stream) + { + super(stream); + + int max = 0; + + try + { + max = stream.available(); + } + catch ( IOException ioe ) + { + // Behave like the JDK here. + } + + monitor = new ProgressMonitor( + component, message, null, 0, max ); + } + + /** + * reset + * @exception IOException TODO + */ + public void reset() throws IOException + { + super.reset(); + + checkMonitorCanceled(); + + // TODO: The docs says the monitor should be resetted. But to which + // value? (mark is not overridden) + } + + /** + * read + * @exception IOException TODO + * @returns int + */ + public int read() throws IOException + { + int t = super.read(); + + monitor.setProgress(++read); + + checkMonitorCanceled(); + + return t; + } + + /** + * read + * @param data TODO + * @exception IOException TODO + * @returns int + */ + public int read(byte[] data) throws IOException + { + int t = super.read(data); + + if ( t > 0 ) + { + read += t; + monitor.setProgress(read); + + checkMonitorCanceled(); + } + else + { + monitor.close(); + } + + return t; + } + + /** + * read + * @param data TODO + * @param offset TODO + * @param length TODO + * @exception IOException TODO + * @returns int + */ + public int read(byte[] data, int offset, int length) throws IOException + { + int t = super.read(data, offset, length); + + if ( t > 0 ) + { + read += t; + monitor.setProgress(read); + + checkMonitorCanceled(); + } + else + { + monitor.close(); + } + + return t; + } + + /** + * skip + * @param length TODO + * @exception IOException TODO + * @returns long + */ + public long skip(long length) throws IOException + { + long t = super.skip(length); + + // 'read' may overflow here in rare situations. + assert ( (long) read + t <= (long) Integer.MAX_VALUE ); + + read += (int) t; + + monitor.setProgress(read); + + checkMonitorCanceled(); + + return t; + } + + /** + * close + * @exception IOException TODO + */ + public void close() throws IOException + { + super.close(); + monitor.close(); + } + + /** + * getProgressMonitor + * @returns ProgressMonitor + */ + public ProgressMonitor getProgressMonitor() + { + return monitor; + } + + private void checkMonitorCanceled() throws InterruptedIOException + { + if ( monitor.isCanceled() ) + { + throw new InterruptedIOException("ProgressMonitor was canceled"); + } + } + +} diff --git a/libjava/classpath/javax/swing/Renderer.java b/libjava/classpath/javax/swing/Renderer.java index c803e38fcc0..9a0e81a9f36 100644 --- a/libjava/classpath/javax/swing/Renderer.java +++ b/libjava/classpath/javax/swing/Renderer.java @@ -49,24 +49,20 @@ import java.awt.Component; * * @author Andrew Selkirk */ -public interface Renderer { +public interface Renderer +{ + /** + * setValue + * @param value TODO + * @param selected TODO + */ + void setValue(Object value, boolean selected); - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- + /** + * getComponent + * @returns Component + */ + Component getComponent(); - /** - * setValue - * @param value TODO - * @param selected TODO - */ - void setValue(Object value, boolean selected); - /** - * getComponent - * @returns Component - */ - Component getComponent(); - - -} // Renderer +} diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index 698dbe8e898..b857b126180 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -1,5 +1,5 @@ /* RepaintManager.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,12 +43,11 @@ import java.awt.Dimension; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.VolatileImage; -import java.util.Enumeration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; -import java.util.Hashtable; import java.util.Iterator; -import java.util.Map; -import java.util.Vector; /** * <p>The repaint manager holds a set of dirty regions, invalid components, @@ -66,7 +65,11 @@ import java.util.Vector; */ public class RepaintManager { - + /** + * The current repaint managers, indexed by their ThreadGroups. + */ + static HashMap currentRepaintManagers; + /** * <p>A helper class which is placed into the system event queue at * various times in order to facilitate repainting and layout. There is @@ -80,33 +83,95 @@ public class RepaintManager * swing paint thread, which revalidates all invalid components and * repaints any damage in the swing scene.</p> */ - protected class RepaintWorker implements Runnable { + boolean live; + public RepaintWorker() { live = false; } + public synchronized void setLive(boolean b) { live = b; } + public synchronized boolean isLive() { return live; } + public void run() { - RepaintManager rm = RepaintManager.globalManager; + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + RepaintManager rm = + (RepaintManager) currentRepaintManagers.get(threadGroup); setLive(false); rm.validateInvalidComponents(); rm.paintDirtyRegions(); } + + } + + /** + * Compares two components using their depths in the component hierarchy. + * A component with a lesser depth (higher level components) are sorted + * before components with a deeper depth (low level components). This is used + * to order paint requests, so that the higher level components are painted + * before the low level components get painted. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class ComponentComparator implements Comparator + { + + /** + * Compares two components. + * + * @param o1 the first component + * @param o2 the second component + * + * @return a negative integer, if <code>o1</code> is higher in the + * hierarchy than <code>o2</code>, zero, if both are at the same + * level and a positive integer, if <code>o1</code> is deeper in + * the hierarchy than <code>o2</code> + */ + public int compare(Object o1, Object o2) + { + if (o1 instanceof JComponent && o2 instanceof JComponent) + { + JComponent c1 = (JComponent) o1; + JComponent c2 = (JComponent) o2; + return getDepth(c1) - getDepth(c2); + } + else + throw new ClassCastException("This comparator can only be used with " + + "JComponents"); + } + + /** + * Computes the depth for a given JComponent. + * + * @param c the component to compute the depth for + * + * @return the depth of the component + */ + private int getDepth(JComponent c) + { + Component comp = c; + int depth = 0; + while (comp != null) + { + comp = comp.getParent(); + depth++; + } + return depth; + } } - /** * A table storing the dirty regions of components. The keys of this * table are components, the values are rectangles. Each component maps @@ -119,7 +184,20 @@ public class RepaintManager * @see #markCompletelyClean * @see #markCompletelyDirty */ - Hashtable dirtyComponents; + HashMap dirtyComponents; + + HashMap workDirtyComponents; + + /** + * Stores the order in which the components get repainted. + */ + ArrayList repaintOrder; + ArrayList workRepaintOrder; + + /** + * The comparator used for ordered inserting into the repaintOrder list. + */ + Comparator comparator; /** * A single, shared instance of the helper class. Any methods which mark @@ -142,14 +220,15 @@ public class RepaintManager * @see #removeInvalidComponent * @see #validateInvalidComponents */ - Vector invalidComponents; + ArrayList invalidComponents; + ArrayList workInvalidComponents; /** * Whether or not double buffering is enabled on this repaint * manager. This is merely a hint to clients; the RepaintManager will * always return an offscreen buffer when one is requested. * - * @see #getDoubleBufferingEnabled + * @see #isDoubleBufferingEnabled * @see #setDoubleBufferingEnabled */ boolean doubleBufferingEnabled; @@ -176,53 +255,62 @@ public class RepaintManager /** - * The global, shared RepaintManager instance. This is reused for all - * components in all windows. This is package-private to avoid an accessor - * method. - * - * @see #currentManager - * @see #setCurrentManager - */ - static RepaintManager globalManager; - - /** * Create a new RepaintManager object. */ public RepaintManager() { - dirtyComponents = new Hashtable(); - invalidComponents = new Vector(); + dirtyComponents = new HashMap(); + workDirtyComponents = new HashMap(); + repaintOrder = new ArrayList(); + workRepaintOrder = new ArrayList(); + invalidComponents = new ArrayList(); + workInvalidComponents = new ArrayList(); repaintWorker = new RepaintWorker(); doubleBufferMaximumSize = new Dimension(2000,2000); doubleBufferingEnabled = true; } /** - * Get the value of the shared {@link #globalManager} instance, possibly - * returning a special manager associated with the specified - * component. The default implementaiton ignores the component parameter. + * Returns the <code>RepaintManager</code> for the current thread's + * thread group. The default implementation ignores the + * <code>component</code> parameter and returns the same repaint manager + * for all components. * - * @param component A component to look up the manager of + * @param component a component to look up the manager of * - * @return The current repaint manager + * @return the current repaint manager for the calling thread's thread group + * and the specified component * * @see #setCurrentManager */ public static RepaintManager currentManager(Component component) { - if (globalManager == null) - globalManager = new RepaintManager(); - return globalManager; + if (currentRepaintManagers == null) + currentRepaintManagers = new HashMap(); + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + RepaintManager currentManager = + (RepaintManager) currentRepaintManagers.get(threadGroup); + if (currentManager == null) + { + currentManager = new RepaintManager(); + currentRepaintManagers.put(threadGroup, currentManager); + } + return currentManager; } /** - * Get the value of the shared {@link #globalManager} instance, possibly - * returning a special manager associated with the specified - * component. The default implementaiton ignores the component parameter. + * Returns the <code>RepaintManager</code> for the current thread's + * thread group. The default implementation ignores the + * <code>component</code> parameter and returns the same repaint manager + * for all components. * - * @param component A component to look up the manager of + * This method is only here for backwards compatibility with older versions + * of Swing and simply forwards to {@link #currentManager(Component)}. * - * @return The current repaint manager + * @param component a component to look up the manager of + * + * @return the current repaint manager for the calling thread's thread group + * and the specified component * * @see #setCurrentManager */ @@ -232,15 +320,20 @@ public class RepaintManager } /** - * Set the value of the shared {@link #globalManager} instance. + * Sets the repaint manager for the calling thread's thread group. * - * @param manager The new value of the shared instance + * @param manager the repaint manager to set for the current thread's thread + * group * - * @see #currentManager(JComponent) + * @see #currentManager(Component) */ public static void setCurrentManager(RepaintManager manager) { - globalManager = manager; + if (currentRepaintManagers == null) + currentRepaintManagers = new HashMap(); + + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + currentRepaintManagers.put(threadGroup, manager); } /** @@ -287,7 +380,7 @@ public class RepaintManager */ public synchronized void removeInvalidComponent(JComponent component) { - invalidComponents.removeElement(component); + invalidComponents.remove(component); } /** @@ -311,12 +404,13 @@ public class RepaintManager public synchronized void addDirtyRegion(JComponent component, int x, int y, int w, int h) { - if (w == 0 || h == 0) + if (w == 0 || h == 0 || !component.isShowing()) return; - Rectangle r = new Rectangle(x, y, w, h); if (dirtyComponents.containsKey(component)) r = r.union((Rectangle)dirtyComponents.get(component)); + else + insertInRepaintOrder(component); dirtyComponents.put(component, r); if (! repaintWorker.isLive()) { @@ -324,7 +418,23 @@ public class RepaintManager SwingUtilities.invokeLater(repaintWorker); } } - + + /** + * Inserts a component into the repaintOrder list in an ordered fashion, + * using a binary search. + * + * @param c the component to be inserted + */ + private void insertInRepaintOrder(JComponent c) + { + if (comparator == null) + comparator = new ComponentComparator(); + int insertIndex = Collections.binarySearch(repaintOrder, c, comparator); + if (insertIndex < 0) + insertIndex = -(insertIndex + 1); + repaintOrder.add(insertIndex, c); + } + /** * Get the dirty region associated with a component, or <code>null</code> * if the component has no dirty region. @@ -341,7 +451,10 @@ public class RepaintManager */ public Rectangle getDirtyRegion(JComponent component) { - return (Rectangle) dirtyComponents.get(component); + Rectangle dirty = (Rectangle) dirtyComponents.get(component); + if (dirty == null) + dirty = new Rectangle(); + return dirty; } /** @@ -359,6 +472,7 @@ public class RepaintManager { Rectangle r = component.getBounds(); addDirtyRegion(component, r.x, r.y, r.width, r.height); + component.isCompletelyDirty = true; } /** @@ -374,7 +488,11 @@ public class RepaintManager */ public void markCompletelyClean(JComponent component) { - dirtyComponents.remove(component); + synchronized (this) + { + dirtyComponents.remove(component); + } + component.isCompletelyDirty = false; } /** @@ -393,13 +511,9 @@ public class RepaintManager */ public boolean isCompletelyDirty(JComponent component) { - Rectangle dirty = (Rectangle) dirtyComponents.get(component); - if (dirty == null) + if (! dirtyComponents.containsKey(component)) return false; - Rectangle r = component.getBounds(); - if (r == null) - return true; - return dirty.contains(r); + return component.isCompletelyDirty; } /** @@ -408,58 +522,56 @@ public class RepaintManager */ public void validateInvalidComponents() { - for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); ) + // In order to keep the blocking of application threads minimal, we switch + // the invalidComponents field with the workInvalidComponents field and + // work with the workInvalidComponents field. + synchronized(this) + { + ArrayList swap = invalidComponents; + invalidComponents = workInvalidComponents; + workInvalidComponents = swap; + } + for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); ) { - JComponent comp = (JComponent) e.nextElement(); + JComponent comp = (JComponent) i.next(); if (! (comp.isVisible() && comp.isShowing())) continue; comp.validate(); } - invalidComponents.clear(); + workInvalidComponents.clear(); } /** * Repaint all regions of all components which have been marked dirty in * the {@link #dirtyComponents} table. */ - public void paintDirtyRegions() + public synchronized void paintDirtyRegions() { - // step 1: pull out roots and calculate spanning damage - - HashMap roots = new HashMap(); - for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); ) + // In order to keep the blocking of application threads minimal, we switch + // the dirtyComponents field with the workdirtyComponents field and the + // repaintOrder field with the workRepaintOrder field and work with the + // work* fields. + synchronized(this) + { + ArrayList swap = workRepaintOrder; + workRepaintOrder = repaintOrder; + repaintOrder = swap; + HashMap swap2 = workDirtyComponents; + workDirtyComponents = dirtyComponents; + dirtyComponents = swap2; + } + for (Iterator i = workRepaintOrder.iterator(); i.hasNext();) { - JComponent comp = (JComponent) e.nextElement(); - if (! (comp.isVisible() && comp.isShowing())) - continue; - Rectangle damaged = getDirtyRegion(comp); - if (damaged.width == 0 || damaged.height == 0) + JComponent comp = (JComponent) i.next(); + // If a component is marked completely clean in the meantime, then skip + // it. + Rectangle damaged = (Rectangle) workDirtyComponents.get(comp); + if (damaged == null || damaged.isEmpty()) continue; - JRootPane root = comp.getRootPane(); - // If the component has no root, no repainting will occur. - if (root == null) - continue; - Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root); - if (! roots.containsKey(root)) - { - roots.put(root, rootDamage); - } - else - { - roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage)); - } - } - dirtyComponents.clear(); - - // step 2: paint those roots - Iterator i = roots.entrySet().iterator(); - while(i.hasNext()) - { - Map.Entry ent = (Map.Entry) i.next(); - JRootPane root = (JRootPane) ent.getKey(); - Rectangle rect = (Rectangle) ent.getValue(); - root.paintImmediately(rect); + comp.paintImmediately(damaged); } + workRepaintOrder.clear(); + workDirtyComponents.clear(); } /** diff --git a/libjava/classpath/javax/swing/RootPaneContainer.java b/libjava/classpath/javax/swing/RootPaneContainer.java index b121f958a92..4b1bece214d 100644 --- a/libjava/classpath/javax/swing/RootPaneContainer.java +++ b/libjava/classpath/javax/swing/RootPaneContainer.java @@ -48,53 +48,49 @@ import java.awt.Container; * * @author Andrew Selkirk */ -public interface RootPaneContainer { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getRootPane - * @returns JRootPane - */ - JRootPane getRootPane(); - - /** - * setContentPane - * @param contentPane TODO - */ - void setContentPane(Container contentPane); - - /** - * getContentPane - * @returns Container - */ - Container getContentPane(); - - /** - * setLayeredPane - * @param layeredPane TODO - */ - void setLayeredPane(JLayeredPane layeredPane); - - /** - * getLayeredPane - * @returns JLayeredPane - */ - JLayeredPane getLayeredPane(); - - /** - * setGlassPane - * @param glassPane TODO - */ - void setGlassPane(Component glassPane); - - /** - * getGlassPane - * @returns Component - */ - Component getGlassPane(); - - -} // RootPaneContainer +public interface RootPaneContainer +{ + + /** + * getRootPane + * @returns JRootPane + */ + JRootPane getRootPane(); + + /** + * setContentPane + * @param contentPane TODO + */ + void setContentPane(Container contentPane); + + /** + * getContentPane + * @returns Container + */ + Container getContentPane(); + + /** + * setLayeredPane + * @param layeredPane TODO + */ + void setLayeredPane(JLayeredPane layeredPane); + + /** + * getLayeredPane + * @returns JLayeredPane + */ + JLayeredPane getLayeredPane(); + + /** + * setGlassPane + * @param glassPane TODO + */ + void setGlassPane(Component glassPane); + + /** + * getGlassPane + * @returns Component + */ + Component getGlassPane(); + +} diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java index 75a6f9aa208..edf1f1f4292 100644 --- a/libjava/classpath/javax/swing/ScrollPaneLayout.java +++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java @@ -43,12 +43,9 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; -import java.awt.Point; import java.awt.Rectangle; import java.io.Serializable; -import javax.swing.border.Border; - /** * ScrollPaneLayout * @author Andrew Selkirk @@ -60,8 +57,11 @@ public class ScrollPaneLayout private static final long serialVersionUID = -4480022884523193743L; public static class UIResource extends ScrollPaneLayout - implements javax.swing.plaf.UIResource { - public UIResource() { + implements javax.swing.plaf.UIResource + { + public UIResource() + { + super(); } } @@ -77,8 +77,9 @@ public class ScrollPaneLayout protected int vsbPolicy; protected int hsbPolicy; - public ScrollPaneLayout() { - + public ScrollPaneLayout() + { + // Nothing to do here. } public void syncWithScrollPane(JScrollPane scrollPane) { @@ -95,11 +96,31 @@ public class ScrollPaneLayout upperRight = scrollPane.getCorner(UPPER_RIGHT_CORNER); } + /** + * Removes an existing component. If oldComponent is not null + * and is not equal to newComponent, oldComponent must be removed + * from its parent. + * @param oldComponent the old Component that may need to be removed. + * @param newComponent the Component to add. + * @return the newComponent + */ protected Component addSingletonComponent(Component oldComponent, - Component newComponent) { - return null; + Component newComponent) + { + if (oldComponent != null && oldComponent != newComponent) + oldComponent.getParent().remove(oldComponent); + return newComponent; } + /** + * Add the specified component to the layout. + * @param key must be one of VIEWPORT, VERTICAL_SCROLLBAR, + * HORIZONTAL_SCROLLBAR, ROW_HEADER, COLUMN_HEADER, + * LOWER_RIGHT_CORNER, LOWER_LEFT_CORNER, UPPER_RIGHT_CORNER, + * UPPER_LEFT_CORNER. + * @param component the Component to add + * @throws IllegalArgumentException if key is not as above + */ public void addLayoutComponent(String key, Component component) { if (key == VIEWPORT) @@ -120,6 +141,8 @@ public class ScrollPaneLayout lowerLeft = component; else if (key == UPPER_LEFT_CORNER) upperLeft = component; + else + throw new IllegalArgumentException(); } public void removeLayoutComponent(Component component) { @@ -147,9 +170,20 @@ public class ScrollPaneLayout { return vsbPolicy; } - + + /** + * Sets the vertical scrollbar policy. + * @param policy must be one of VERTICAL_SCROLLBAR_AS_NEEDED, + * VERTICAL_SCROLLBAR_NEVER, VERTICAL_SCROLLBAR_ALWAYS. + * @throws IllegalArgumentException if policy is not one of the valid + * JScrollBar policies. + */ public void setVerticalScrollBarPolicy(int policy) { + if (policy != VERTICAL_SCROLLBAR_AS_NEEDED && + policy != VERTICAL_SCROLLBAR_NEVER && + policy != VERTICAL_SCROLLBAR_ALWAYS) + throw new IllegalArgumentException("Illegal Scrollbar Policy"); vsbPolicy = policy; } @@ -158,8 +192,19 @@ public class ScrollPaneLayout return hsbPolicy; } + /** + * Sets the horizontal scrollbar policy. + * @param policy must be one of HORIZONTAL_SCROLLBAR_AS_NEEDED, + * HORIZONTAL_SCROLLBAR_NEVER, HORIZONTAL_SCROLLBAR_ALWAYS. + * @throws IllegalArgumentException if policy is not one of the valid + * JScrollbar policies. + */ public void setHorizontalScrollBarPolicy(int policy) { + if (policy != HORIZONTAL_SCROLLBAR_AS_NEEDED && + policy != HORIZONTAL_SCROLLBAR_NEVER && + policy != HORIZONTAL_SCROLLBAR_ALWAYS) + throw new IllegalArgumentException("Illegal Scrollbar Policy"); hsbPolicy = policy; } @@ -188,6 +233,12 @@ public class ScrollPaneLayout return colHead; } + /** + * Returns the Component at the specified corner. + * @param key the corner. + * @return the Component at the specified corner, or null if + * key is not one of the four valid corners. + */ public Component getCorner(String key) { if (key == LOWER_RIGHT_CORNER) @@ -201,151 +252,53 @@ public class ScrollPaneLayout return null; } - private static void maybeSetPreferredSize(JComponent src, Dimension dim) - { - Dimension tmp = null; - if (src != null) - tmp = src.getPreferredSize(); - if (tmp != null) - dim.setSize(tmp); - } - - private static void maybeSetMinimumSize(JComponent src, Dimension dim) - { - Dimension tmp = null; - if (src != null) - tmp = src.getMinimumSize(); - if (tmp != null) - dim.setSize(tmp); - } - public Dimension preferredLayoutSize(Container parent) { - if (parent != null && parent instanceof JScrollPane) - { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - Dimension insetsSize = new Dimension(0,0); - Dimension viewportSize = new Dimension(0,0); - Dimension viewportInsetsSize = new Dimension(0,0); - Dimension columnHeaderSize = new Dimension(0,0); - Dimension rowHeaderSize = new Dimension(0,0); - Dimension verticalScrollBarSize = new Dimension(0,0); - Dimension horizontalScrollBarSize = new Dimension(0,0); - - Insets insets = sc.getInsets(); - Border viewportBorder = sc.getViewportBorder(); - Insets viewportInsets = null; - - if (viewportBorder != null) - { - viewportInsets = viewportBorder.getBorderInsets(parent); - if (viewportInsets != null) - viewportInsetsSize.setSize(viewportInsets.left + viewportInsets.right, - viewportInsets.top + viewportInsets.bottom); - } - - if (insets != null) - insetsSize.setSize(insets.left + insets.right, - insets.top + insets.bottom); - - if (viewport != null) - { - Component view = null; - Scrollable scr = null; - Dimension pref = null; - - view = viewport.getView(); - if (view != null && view instanceof Scrollable) - scr = (Scrollable) view; - if (scr != null) - pref = scr.getPreferredScrollableViewportSize(); - if (pref == null) - pref = viewport.getPreferredSize(); - if (pref != null) - viewportSize.setSize(pref); - } - - maybeSetPreferredSize(colHead, columnHeaderSize); - maybeSetPreferredSize(rowHead, rowHeaderSize); - maybeSetPreferredSize(vsb, verticalScrollBarSize); - maybeSetPreferredSize(hsb, horizontalScrollBarSize); - - return new Dimension(insetsSize.width - + viewportSize.width - + viewportInsetsSize.width - + rowHeaderSize.width - + verticalScrollBarSize.width, - insetsSize.height - + viewportSize.height - + viewportInsetsSize.height - + columnHeaderSize.height - + horizontalScrollBarSize.height); - } - } - else - { - return new Dimension(0,0); - } + // Sun's implementation simply throws a ClassCastException if + // parent is no JScrollPane, so do we. + JScrollPane sc = (JScrollPane) parent; + Dimension viewportSize = viewport.getPreferredSize(); + Dimension viewSize = viewport.getViewSize(); + int width = viewportSize.width; + int height = viewportSize.height; + + // horizontal scrollbar needed if the view's preferred width + // is larger than the viewport's preferred width + if (hsb != null && viewSize.width > viewportSize.width) + height += hsb.getPreferredSize().height; + + // vertical scrollbar needed if the view's preferred height + // is larger than the viewport's preferred height + if (vsb != null && viewSize.height > viewportSize.height) + width += vsb.getPreferredSize().width; + if (rowHead != null && rowHead.isVisible()) + width += rowHead.getPreferredSize().width; + if (colHead != null && colHead.isVisible()) + height += colHead.getPreferredSize().height; + Insets i = sc.getInsets(); + return new Dimension(width + i.left + i.right, + height + i.left + i.right); } public Dimension minimumLayoutSize(Container parent) { - if (parent instanceof JScrollPane) - { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - Dimension insetsSize = new Dimension(0,0); - Dimension viewportSize = new Dimension(0,0); - Dimension viewportInsetsSize = new Dimension(0,0); - Dimension columnHeaderSize = new Dimension(0,0); - Dimension rowHeaderSize = new Dimension(0,0); - Dimension verticalScrollBarSize = new Dimension(0,0); - Dimension horizontalScrollBarSize = new Dimension(0,0); - - Insets insets = sc.getInsets(); - Border viewportBorder = sc.getViewportBorder(); - Insets viewportInsets = null; - - if (viewportBorder != null) - { - viewportInsets = viewportBorder.getBorderInsets(parent); - if (viewportInsets != null) - viewportInsetsSize.setSize(viewportInsets.left + viewportInsets.right, - viewportInsets.top + viewportInsets.bottom); - } - - if (insets != null) - insetsSize.setSize(insets.left + insets.right, - insets.top + insets.bottom); - - maybeSetMinimumSize(colHead, columnHeaderSize); - maybeSetMinimumSize(rowHead, rowHeaderSize); - - if (vsbPolicy != VERTICAL_SCROLLBAR_NEVER) - maybeSetMinimumSize(vsb, verticalScrollBarSize); - - if (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER) - maybeSetMinimumSize(hsb, horizontalScrollBarSize); - - return new Dimension(insetsSize.width - + viewportSize.width - + viewportInsetsSize.width - + rowHeaderSize.width - + verticalScrollBarSize.width, - insetsSize.height - + viewportSize.height - + viewportInsetsSize.height - + columnHeaderSize.height - + horizontalScrollBarSize.height); - } - } - else - { - return new Dimension(0,0); - } + // Sun's implementation simply throws a ClassCastException if + // parent is no JScrollPane, so do we. + JScrollPane sc = (JScrollPane) parent; + Dimension viewportSize = viewport.getMinimumSize(); + int width = viewportSize.width; + int height = viewportSize.height; + if (hsb != null && hsb.isVisible()) + height += hsb.getMinimumSize().height; + if (vsb != null && vsb.isVisible()) + width += vsb.getMinimumSize().width; + if (rowHead != null && rowHead.isVisible()) + width += rowHead.getMinimumSize().width; + if (colHead != null && colHead.isVisible()) + height += colHead.getMinimumSize().height; + Insets i = sc.getInsets(); + return new Dimension(width + i.left + i.right, + height + i.top + i.bottom); } /** @@ -371,100 +324,91 @@ public class ScrollPaneLayout */ public void layoutContainer(Container parent) { - if (parent instanceof JScrollPane) + // Sun's implementation simply throws a ClassCastException if + // parent is no JScrollPane, so do we. + JScrollPane sc = (JScrollPane) parent; + JViewport viewport = sc.getViewport(); + Dimension viewSize = viewport.getViewSize(); + + int x1 = 0, x2 = 0, x3 = 0, x4 = 0; + int y1 = 0, y2 = 0, y3 = 0, y4 = 0; + Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null); + + x1 = scrollPaneBounds.x; + y1 = scrollPaneBounds.y; + x4 = scrollPaneBounds.x + scrollPaneBounds.width; + y4 = scrollPaneBounds.y + scrollPaneBounds.height; + if (colHead != null) + y2 = y1 + colHead.getPreferredSize().height; + else + y2 = y1; + + if (rowHead != null) + x2 = x1 + rowHead.getPreferredSize().width; + else + x2 = x1; + + int vsbPolicy = sc.getVerticalScrollBarPolicy(); + int hsbPolicy = sc.getHorizontalScrollBarPolicy(); + + boolean showVsb = + (vsb != null) + && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) + || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED + && viewSize.height > (y4 - y2))); + boolean showHsb = + (hsb != null) + && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) + || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED + && viewSize.width > (x4 - x2))); + + if (!showVsb) + x3 = x4; + else + x3 = x4 - vsb.getPreferredSize().width; + + if (!showHsb) + y3 = y4; + else + y3 = y4 - hsb.getPreferredSize().height; + + // now set the layout + if (viewport != null) + viewport.setBounds(new Rectangle(x2, y2, x3 - x2, y3 - y2)); + + if (colHead != null) + colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1)); + + if (rowHead != null) + rowHead.setBounds(new Rectangle(x1, y2, x2 - x1, y3 - y2)); + + if (showVsb) + { + vsb.setVisible(true); + vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2)); + } + else if (vsb != null) + vsb.setVisible(false); + + if (showHsb) { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - JViewport viewport = sc.getViewport(); - Dimension viewSize = viewport.getViewSize(); - Point viewPos = viewport.getViewPosition(); - - int x1 = 0, x2 = 0, x3 = 0, x4 = 0; - int y1 = 0, y2 = 0, y3 = 0, y4 = 0; - - Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null); - - x1 = scrollPaneBounds.x; - y1 = scrollPaneBounds.y; - x4 = scrollPaneBounds.x + scrollPaneBounds.width; - y4 = scrollPaneBounds.y + scrollPaneBounds.height; - - if (colHead != null) - y2 = y1 + colHead.getPreferredSize().height; - else - y2 = y1; - - if (rowHead != null) - x2 = x1 + rowHead.getPreferredSize().width; - else - x2 = x1; - - int vsbPolicy = sc.getVerticalScrollBarPolicy(); - int hsbPolicy = sc.getHorizontalScrollBarPolicy(); - - x3 = x4 - vsb.getPreferredSize().width; - y3 = y4 - hsb.getPreferredSize().height; - - boolean showVsb = - (vsb != null) - && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) - || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED - && viewSize.height > (y3 - y2))); - - boolean showHsb = - (hsb != null) - && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) - || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED - && viewSize.width > (x3 - x2))); - - if (!showVsb) - x3 = x4; - - if (!showHsb) - y3 = y4; - - // now set the layout - - if (viewport != null) - viewport.setBounds(new Rectangle(x2, y2, x3-x2, y3-y2)); - - if (colHead != null) - colHead.setBounds(new Rectangle(x2, y1, x3-x2, y2-y1)); - - if (rowHead != null) - rowHead.setBounds(new Rectangle(x1, y2, x2-x1, y3-y2)); - - if (showVsb) - { - vsb.setVisible(true); - vsb.setBounds(new Rectangle(x3, y2, x4-x3, y3-y2)); - } - else if (vsb != null) - vsb.setVisible(false); - - if (showHsb) - { - hsb.setVisible(true); - hsb.setBounds(new Rectangle(x2, y3, x3-x2, y4-y3)); - } - else if (hsb != null) - hsb.setVisible(false); - - if (upperLeft != null) - upperLeft.setBounds(new Rectangle(x1, y1, x2-x1, y2-y1)); - - if (upperRight != null) - upperRight.setBounds(new Rectangle(x3, y1, x4-x3, y2-y1)); - - if (lowerLeft != null) - lowerLeft.setBounds(new Rectangle(x1, y3, x2-x1, y4-y3)); - - if (lowerRight != null) - lowerRight.setBounds(new Rectangle(x3, y3, x4-x3, y4-y3)); - - } + hsb.setVisible(true); + hsb.setBounds(new Rectangle(x2, y3, x3 - x2, y4 - y3)); } + else if (hsb != null) + hsb.setVisible(false); + + if (upperLeft != null) + upperLeft.setBounds(new Rectangle(x1, y1, x2 - x1, y2 - y1)); + + if (upperRight != null) + upperRight.setBounds(new Rectangle(x3, y1, x4 - x3, y2 - y1)); + + if (lowerLeft != null) + lowerLeft.setBounds(new Rectangle(x1, y3, x2 - x1, y4 - y3)); + + if (lowerRight != null) + lowerRight.setBounds(new Rectangle(x3, y3, x4 - x3, y4 - y3)); } /** diff --git a/libjava/classpath/javax/swing/Scrollable.java b/libjava/classpath/javax/swing/Scrollable.java index 19732192330..9dce665d626 100644 --- a/libjava/classpath/javax/swing/Scrollable.java +++ b/libjava/classpath/javax/swing/Scrollable.java @@ -48,9 +48,16 @@ import java.awt.Rectangle; */ public interface Scrollable { - Dimension getPreferredScrollableViewportSize(); - int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction); - int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction); - boolean getScrollableTracksViewportWidth(); - boolean getScrollableTracksViewportHeight(); + Dimension getPreferredScrollableViewportSize(); + + int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, + int direction); + + int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, + int direction); + + boolean getScrollableTracksViewportWidth(); + + boolean getScrollableTracksViewportHeight(); + } diff --git a/libjava/classpath/javax/swing/SingleSelectionModel.java b/libjava/classpath/javax/swing/SingleSelectionModel.java index b5380c857a3..d57443b56bb 100644 --- a/libjava/classpath/javax/swing/SingleSelectionModel.java +++ b/libjava/classpath/javax/swing/SingleSelectionModel.java @@ -46,46 +46,41 @@ import javax.swing.event.ChangeListener; * * @author Andrew Selkirk */ -public interface SingleSelectionModel { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getSelectedIndex - * @returns int - */ - int getSelectedIndex(); - - /** - * setSelectedIndex - * @param index TODO - */ - void setSelectedIndex(int index); - - /** - * clearSelection - */ - void clearSelection(); - - /** - * isSelected - * @returns boolean - */ - boolean isSelected(); - - /** - * addChangeListener - * @param listener TODO - */ - void addChangeListener(ChangeListener listener); - - /** - * removeChangeListener - * @param listener TODO - */ - void removeChangeListener(ChangeListener listener); - - -} // SingleSelectionModel +public interface SingleSelectionModel +{ + /** + * getSelectedIndex + * @returns int + */ + int getSelectedIndex(); + + /** + * setSelectedIndex + * @param index TODO + */ + void setSelectedIndex(int index); + + /** + * clearSelection + */ + void clearSelection(); + + /** + * isSelected + * @returns boolean + */ + boolean isSelected(); + + /** + * addChangeListener + * @param listener TODO + */ + void addChangeListener(ChangeListener listener); + + /** + * removeChangeListener + * @param listener TODO + */ + void removeChangeListener(ChangeListener listener); + +} diff --git a/libjava/classpath/javax/swing/SizeRequirements.java b/libjava/classpath/javax/swing/SizeRequirements.java index 77b42db1ce9..b9b5c322379 100644 --- a/libjava/classpath/javax/swing/SizeRequirements.java +++ b/libjava/classpath/javax/swing/SizeRequirements.java @@ -116,7 +116,17 @@ public class SizeRequirements implements Serializable */ public String toString() { - return null; // TODO + StringBuilder b = new StringBuilder(); + b.append("<["); + b.append(minimum); + b.append(','); + b.append(preferred); + b.append(','); + b.append(maximum); + b.append("]@"); + b.append(alignment); + b.append('>'); + return b.toString(); } /** @@ -132,13 +142,26 @@ public class SizeRequirements implements Serializable public static SizeRequirements getTiledSizeRequirements(SizeRequirements[] children) { - SizeRequirements result = new SizeRequirements(); + long minimum = 0; + long preferred = 0; + long maximum = 0; for (int i = 0; i < children.length; i++) { - result.minimum += children[i].minimum; - result.preferred += children[i].preferred; - result.maximum += children[i].maximum; + minimum += children[i].minimum; + preferred += children[i].preferred; + maximum += children[i].maximum; } + // Overflow check. + if (minimum > Integer.MAX_VALUE) + minimum = Integer.MAX_VALUE; + if (preferred > Integer.MAX_VALUE) + preferred = Integer.MAX_VALUE; + if (maximum > Integer.MAX_VALUE) + maximum = Integer.MAX_VALUE; + SizeRequirements result = new SizeRequirements((int) minimum, + (int) preferred, + (int) maximum, + 0.5F); return result; } @@ -156,7 +179,34 @@ public class SizeRequirements implements Serializable public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[] children) { - return null; // TODO + float minLeft = 0; + float minRight = 0; + float prefLeft = 0; + float prefRight = 0; + float maxLeft = 0; + float maxRight = 0; + for (int i = 0; i < children.length; i++) + { + float myMinLeft = children[i].minimum * children[i].alignment; + float myMinRight = children[i].minimum - myMinLeft; + minLeft = Math.max(myMinLeft, minLeft); + minRight = Math.max(myMinRight, minRight); + float myPrefLeft = children[i].preferred * children[i].alignment; + float myPrefRight = children[i].preferred - myPrefLeft; + prefLeft = Math.max(myPrefLeft, prefLeft); + prefRight = Math.max(myPrefRight, prefRight); + float myMaxLeft = children[i].maximum * children[i].alignment; + float myMaxRight = children[i].maximum - myMaxLeft; + maxLeft = Math.max(myMaxLeft, maxLeft); + maxRight = Math.max(myMaxRight, maxRight); + } + int minSize = (int) (minLeft + minRight); + int prefSize = (int) (prefLeft + prefRight); + int maxSize = (int) (maxLeft + maxRight); + float align = prefLeft / (prefRight + prefLeft); + if (Float.isNaN(align)) + align = 0; + return new SizeRequirements(minSize, prefSize, maxSize, align); } /** @@ -222,6 +272,7 @@ public class SizeRequirements implements Serializable int[] offsets, int[] spans, boolean forward) { + int span = 0; if (forward) { int offset = 0; @@ -229,6 +280,7 @@ public class SizeRequirements implements Serializable { offsets[i] = offset; spans[i] = children[i].preferred; + span += spans[i]; offset += children[i].preferred; } } @@ -239,9 +291,84 @@ public class SizeRequirements implements Serializable { offset -= children[i].preferred; offsets[i] = offset; + span += spans[i]; spans[i] = children[i].preferred; } } + // Adjust spans so that we exactly fill the allocated region. If + if (span > allocated) + adjustSmaller(allocated, children, spans, span); + else if (span < allocated) + adjustGreater(allocated, children, spans, span); + + // Adjust offsets. + if (forward) + { + int offset = 0; + for (int i = 0; i < children.length; i++) + { + offsets[i] = offset; + offset += spans[i]; + } + } + else + { + int offset = allocated; + for (int i = 0; i < children.length; i++) + { + offset -= spans[i]; + offsets[i] = offset; + } + } + } + + private static void adjustSmaller(int allocated, SizeRequirements[] children, + int[] spans, int span) + { + // Sum up (prefSize - minSize) over all children + int sumDelta = 0; + for (int i = 0; i < children.length; i++) + sumDelta += children[i].preferred - children[i].minimum; + + // If we have sumDelta == 0, then all components have prefSize == maxSize + // and we can't do anything about it. + if (sumDelta == 0) + return; + + // Adjust all sizes according to their preferred and minimum sizes. + for (int i = 0; i < children.length; i++) + { + double factor = ((double) (children[i].preferred - children[i].minimum)) + / ((double) sumDelta); + // In case we have a sumDelta of 0, the factor should also be 0. + if (Double.isNaN(factor)) + factor = 0; + spans[i] -= factor * (span - allocated); + } + } + + private static void adjustGreater(int allocated, SizeRequirements[] children, + int[] spans, int span) + { + // Sum up (maxSize - prefSize) over all children + long sumDelta = 0; + for (int i = 0; i < children.length; i++) + { + sumDelta += children[i].maximum - children[i].preferred; + } + + // If we have sumDelta == 0, then all components have prefSize == maxSize + // and we can't do anything about it. + if (sumDelta == 0) + return; + + // Adjust all sizes according to their preferred and minimum sizes. + for (int i = 0; i < children.length; i++) + { + double factor = ((double) (children[i].maximum - children[i].preferred)) + / ((double) sumDelta); + spans[i] += factor * (allocated - span); + } } /** @@ -271,7 +398,8 @@ public class SizeRequirements implements Serializable SizeRequirements[] children, int[] offsets, int[] spans) { - calculateTiledPositions(allocated, total, children, offsets, spans, true); + calculateAlignedPositions(allocated, total, children, offsets, spans, + true); } /** @@ -306,7 +434,74 @@ public class SizeRequirements implements Serializable int[] offset, int[] spans, boolean forward) { - // TODO + // First we compute the position of the baseline. + float baseline = allocated * total.alignment; + + // Now we can layout the components along the baseline. + for (int i = 0; i < children.length; i++) + { + float align = children[i].alignment; + // Try to fit the component into the available space. + int[] spanAndOffset = new int[2]; + if (align < .5F || baseline == 0) + adjustFromRight(children[i], baseline, allocated, spanAndOffset); + else + adjustFromLeft(children[i], baseline, allocated, spanAndOffset); + spans[i] = spanAndOffset[0]; + offset[i] = spanAndOffset[1]; + } + } + + /** + * Adjusts the span and offset of a component for the aligned layout. + * + * @param reqs + * @param baseline + * @param allocated + * @param spanAndOffset + */ + private static void adjustFromRight(SizeRequirements reqs, float baseline, + int allocated, int[] spanAndOffset) + { + float right = allocated - baseline; + // If the resulting span exceeds the maximum of the component, then adjust + // accordingly. + float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment); + if (right / (1.F - reqs.alignment) > reqs.maximum) + right = maxRight; + // If we have not enough space on the left side, then adjust accordingly. + if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline) + right = ((float) (allocated - baseline)) + / reqs.alignment * (1.F - reqs.alignment); + + spanAndOffset[0] = (int) (right / (1.F - reqs.alignment)); + spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); + } + + /** + * Adjusts the span and offset of a component for the aligned layout. + * + * @param reqs + * @param baseline + * @param allocated + * @param spanAndOffset + */ + private static void adjustFromLeft(SizeRequirements reqs, float baseline, + int allocated, int[] spanAndOffset) + { + float left = baseline; + // If the resulting span exceeds the maximum of the component, then adjust + // accordingly. + float maxLeft = ((float) reqs.maximum) * reqs.alignment; + if (left / reqs.alignment > reqs.maximum) + left = maxLeft; + // If we have not enough space on the right side, then adjust accordingly. + if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline) + left = ((float) (allocated - baseline)) + / (1.F - reqs.alignment) * reqs.alignment; + + spanAndOffset[0] = (int) (left / reqs.alignment); + spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); } /** diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java index cf6e5f042a1..dff966b3e35 100644 --- a/libjava/classpath/javax/swing/SizeSequence.java +++ b/libjava/classpath/javax/swing/SizeSequence.java @@ -42,208 +42,197 @@ package javax.swing; * @author Andrew Selkirk * @version 1.0 */ -public class SizeSequence { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * sizes - */ - private int[] sizes = new int[0]; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor SizeSequence - */ - public SizeSequence() { - sizes = new int[0]; - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param numEntries TODO - */ - public SizeSequence(int numEntries) { - this(numEntries, 0); - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param numEntries TODO - * @param value TODO - */ - public SizeSequence(int numEntries, int value) { - insertEntries(0, numEntries, value); - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param sizes TODO - */ - public SizeSequence(int[] sizes) { - setSizes(sizes); - } // SizeSequence() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * setSize - * @param index TODO - * @param size TODO - */ - public void setSize(int index, int size) { - sizes[index] = size; - } // setSize() - - /** - * getIndex - * @param position TODO - * @returns int - */ - public int getIndex(int position) { - return 0; // TODO - } // getIndex() - - /** - * getSize - * @param index TODO - * @returns int - */ - public int getSize(int index) { - return sizes[index]; - } // getSize() - - /** - * setSizes - * @param sizes TODO - */ - public void setSizes(int[] sizes) { - - // Variables - int index; - - // Initialize Sizes - this.sizes = new int[sizes.length]; - for (index = 0; index < sizes.length; index++) { - this.sizes[index] = sizes[index]; - } // for - - } // setSizes() - - /** - * getSizes - * @returns int[] - */ - public int[] getSizes() { - - // Variables - int[] array; - int index; - - // Create New Array - array = new int[sizes.length]; - for (index = 0; index < sizes.length; index++) { - array[index] = sizes[index]; - } // for - - // Return Newly created array - return array; - - } // getSizes() - - /** - * getPosition - * @param index TODO - * @returns int - */ - public int getPosition(int index) { - - // Variables - int position; - int loop; - - // Process Sizes - position = 0; - for (loop = 0; loop < index; loop++) { - position += sizes[loop]; - } // for - - // Return Position - return position; - - } // getPosition() - - /** - * insertEntries - * @param start TODO - * @param length TODO - * @param value TODO - */ - public void insertEntries(int start, int length, int value) { - - // Variables - int[] array; - int index; - int arrayIndex; - int loop; - - // Create New Array - array = new int[sizes.length + length]; - arrayIndex = 0; - for (index = 0; index < sizes.length; index++) { - if (index == start) { - for (loop = 0; loop < length; loop++) { - array[arrayIndex] = value; - arrayIndex++; - } // for - } else { - array[arrayIndex] = sizes[index]; - arrayIndex++; - } // if - } // for - - } // insertEntries() - - /** - * removeEntries - * @param start TODO - * @param length TODO - */ - public void removeEntries(int start, int length) { - - // Variables - int[] array; - int index; - int arrayIndex; - - // Sanity Check - if ((start + length) > sizes.length) { - throw new IllegalArgumentException("Specified start/length that " + - "is greater than available sizes"); - } // if - - // Create New Array - array = new int[sizes.length - length]; - arrayIndex = 0; - for (index = 0; index < sizes.length; index++) { - if (index == start) { - index += length - 1; - } else { - array[arrayIndex] = sizes[index]; - arrayIndex++; - } // if - } // for - - } // removeEntries() - - -} // SizeSequence +public class SizeSequence +{ + + /** + * sizes + */ + private int[] sizes = new int[0]; + + /** + * Constructor SizeSequence + */ + public SizeSequence() + { + sizes = new int[0]; + } + + /** + * Constructor SizeSequence + * @param numEntries TODO + */ + public SizeSequence(int numEntries) + { + this(numEntries, 0); + } + + /** + * Constructor SizeSequence + * @param numEntries TODO + * @param value TODO + */ + public SizeSequence(int numEntries, int value) + { + insertEntries(0, numEntries, value); + } + + /** + * Constructor SizeSequence + * @param sizes TODO + */ + public SizeSequence(int[] sizes) + { + setSizes(sizes); + } + + /** + * setSize + * @param index TODO + * @param size TODO + */ + public void setSize(int index, int size) + { + sizes[index] = size; + } + + /** + * getIndex + * @param position TODO + * @returns int + */ + public int getIndex(int position) + { + return 0; // TODO + } + + /** + * getSize + * @param index TODO + * @returns int + */ + public int getSize(int index) + { + return sizes[index]; + } + + /** + * setSizes + * @param sizes TODO + */ + public void setSizes(int[] sizes) + { + int index; + // Initialize sizes. + this.sizes = new int[sizes.length]; + for (index = 0; index < sizes.length; index++) + this.sizes[index] = sizes[index]; + + } + + /** + * getSizes + * @returns int[] + */ + public int[] getSizes() + { + int[] array; + int index; + + // Create new array. + array = new int[sizes.length]; + for (index = 0; index < sizes.length; index++) + array[index] = sizes[index]; + + // Return newly created array. + return array; + + } + + /** + * getPosition + * @param index TODO + * @returns int + */ + public int getPosition(int index) + { + int position; + int loop; + + // Process sizes. + position = 0; + for (loop = 0; loop < index; loop++) + position += sizes[loop]; + + // Return position. + return position; + + } + + /** + * insertEntries + * @param start TODO + * @param length TODO + * @param value TODO + */ + public void insertEntries(int start, int length, int value) + { + int[] array; + int index; + int arrayIndex; + int loop; + + // Create new array. + array = new int[sizes.length + length]; + arrayIndex = 0; + for (index = 0; index < sizes.length; index++) + { + if (index == start) + { + for (loop = 0; loop < length; loop++) + { + array[arrayIndex] = value; + arrayIndex++; + } + } + else + { + array[arrayIndex] = sizes[index]; + arrayIndex++; + } + } + + } + + /** + * removeEntries + * @param start TODO + * @param length TODO + */ + public void removeEntries(int start, int length) + { + int[] array; + int index; + int arrayIndex; + + // Sanity check. + if ((start + length) > sizes.length) + throw new IllegalArgumentException("Specified start/length that " + + "is greater than available sizes"); + + // Create new array. + array = new int[sizes.length - length]; + arrayIndex = 0; + for (index = 0; index < sizes.length; index++) + { + if (index == start) + index += length - 1; + else + { + array[arrayIndex] = sizes[index]; + arrayIndex++; + } + } + } + +} diff --git a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java index fada17c6339..96ef3832955 100644 --- a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java +++ b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java @@ -72,7 +72,7 @@ public class SortingFocusTraversalPolicy * simply advance within the containing focus cycle, subject to the * {@link #comparator} order and the {@link #accept} judgment.</p> * - * @see #getNextFocusableComponent + * @see #getImplicitDownCycleTraversal() */ boolean implicitDownCycleTraversal = true; diff --git a/libjava/classpath/javax/swing/SpinnerListModel.java b/libjava/classpath/javax/swing/SpinnerListModel.java index 85dc4efa6f1..d8e2f22d585 100644 --- a/libjava/classpath/javax/swing/SpinnerListModel.java +++ b/libjava/classpath/javax/swing/SpinnerListModel.java @@ -68,231 +68,228 @@ import javax.swing.event.ChangeEvent; * @since 1.4 */ -public class SpinnerListModel - extends AbstractSpinnerModel - implements Serializable +public class SpinnerListModel extends AbstractSpinnerModel + implements Serializable { - /** - * For compatability with Sun's JDK - */ - private static final long serialVersionUID = 3358804052191994516L; + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 3358804052191994516L; - /** - * The backing list for this model. - */ - private List list; + /** + * The backing list for this model. + */ + private List list; - /** - * The current index in the list. - */ - private transient int index; + /** + * The current index in the list. + */ + private transient int index; - /** - * Constructs a default <code>SpinnerListModel</code>. This - * is a model backed by a list containing only the single - * <code>String</code> element, "empty". - */ - public SpinnerListModel() - { - List defaultList; + /** + * Constructs a default <code>SpinnerListModel</code>. This + * is a model backed by a list containing only the single + * <code>String</code> element, "empty". + */ + public SpinnerListModel() + { + List defaultList; - /* Create an empty list */ - defaultList = new ArrayList(); - /* Add the string "empty" */ - defaultList.add("empty"); - /* Set the list */ - setList(defaultList); - } + // Create an empty list. + defaultList = new ArrayList(); + // Add the string "empty". + defaultList.add("empty"); + // Set the list. + setList(defaultList); + } - /** - * Constructs a <code>SpinnerListModel</code> using the supplied list. - * The model maintains a reference to this list, and returns - * consecutive elements in response to calls to <code>getNextValue()</code>. - * The initial value is that at position 0, so an initial call - * to <code>getValue()</code> returns the same as <code>list.get(0)</code>. - * - * @param list The list to use for this model. - * @throws IllegalArgumentException if the list is null or contains no - * elements. - * @see SpinnerListModel#getNextValue() - * @see SpinnerListModel#getValue() - */ - public SpinnerListModel(List list) - { - /* Retain a reference to the valid list */ - setList(list); - } + /** + * Constructs a <code>SpinnerListModel</code> using the supplied list. + * The model maintains a reference to this list, and returns + * consecutive elements in response to calls to <code>getNextValue()</code>. + * The initial value is that at position 0, so an initial call + * to <code>getValue()</code> returns the same as <code>list.get(0)</code>. + * + * @param list The list to use for this model. + * + * @throws IllegalArgumentException if the list is null or contains no + * elements. + * + * @see SpinnerListModel#getNextValue() + * @see SpinnerListModel#getValue() + */ + public SpinnerListModel(List list) + { + // Retain a reference to the valid list. + setList(list); + } - /** - * Constructs a <code>SpinnerListModel</code> using the supplied array. - * The model stores a reference to the wrapper list returned by - * <code>Arrays.asList()</code>. The wrapper list reflects modifications - * in the underlying array, so these changes will also be reflected - * by the model. The model produces consecutive elements from the array - * in response to calls to <code>getNextValue()</code>. The initial - * value returned by <code>getValue()</code> is the same as - * <code>array[0]</code>. - * - * @param array The array to use for this model. - * @throws IllegalArgumentException if the array is null or contains - * no elements. - * @see Arrays#asList(Object[]) - * @see SpinnerListModel#getNextValue() - * @see SpinnerListModel#getValue() - */ - public SpinnerListModel(Object[] array) - { - /* Check for a null or zero-sized array */ - if (array == null || array.length == 0) - { - throw new IllegalArgumentException("The supplied array was invalid."); - } - /* - Retain a reference to a wrapper around the valid array - The array, in list form, will be tested again here, but we can't really - avoid this -- a null value to Arrays.asList will throw a NullPointerException - */ - setList(Arrays.asList(array)); - } + /** + * Constructs a <code>SpinnerListModel</code> using the supplied array. + * The model stores a reference to the wrapper list returned by + * <code>Arrays.asList()</code>. The wrapper list reflects modifications + * in the underlying array, so these changes will also be reflected + * by the model. The model produces consecutive elements from the array + * in response to calls to <code>getNextValue()</code>. The initial + * value returned by <code>getValue()</code> is the same as + * <code>array[0]</code>. + * + * @param array The array to use for this model. + * + * @throws IllegalArgumentException if the array is null or contains + * no elements. + * + * @see Arrays#asList(Object[]) + * @see SpinnerListModel#getNextValue() + * @see SpinnerListModel#getValue() + */ + public SpinnerListModel(Object[] array) + { + // Check for a null or zero-sized array. + if (array == null || array.length == 0) + { + throw new IllegalArgumentException("The supplied array was invalid."); + } - /** - * Returns the backing list for this model. - * - * @return The backing list. - */ - public List getList() - { - return list; - } + // Retain a reference to a wrapper around the valid array. + // The array, in list form, will be tested again here, but we can't really + // avoid this -- a null value to Arrays.asList will throw a + // NullPointerException. + setList(Arrays.asList(array)); + } + + /** + * Returns the backing list for this model. + * + * @return The backing list. + */ + public List getList() + { + return list; + } - /** - * Returns the next value from the list, which is the same as the element - * stored at the current index + 1. Null is returned if there are no more - * values to be returned (the end of the list has been reached). An - * ambiguity can occur here, as null may also be returned as a valid list - * element. This operation does not change the current value. - * - * @return The next value from the list or null. - */ - public Object getNextValue() - { - /* Check for a next value */ - if (index < (list.size() - 1)) - { - /* Return the element at the next index */ - return list.get(index + 1); - } - else - { - /* Return null as this is the end of the list */ - return null; - } + /** + * Returns the next value from the list, which is the same as the element + * stored at the current index + 1. Null is returned if there are no more + * values to be returned (the end of the list has been reached). An + * ambiguity can occur here, as null may also be returned as a valid list + * element. This operation does not change the current value. + * + * @return The next value from the list or null. + */ + public Object getNextValue() + { + // Check for a next value. + if (index < (list.size() - 1)) + // Return the element at the next index. + return list.get(index + 1); + else + // Return null as this is the end of the list. + return null; } - /** - * Returns the previous value from the list, which is the same as the element - * stored at the current index - 1. Null is returned if there are no more - * values to be returned (the start of the list has been reached). An - * ambiguity can occur here, as null may also be returned as a valid list - * element. This operation does not change the current value. - * - * @return The previous value from the list or null. - */ - public Object getPreviousValue() - { - /* Check for a previous value. */ - if (index > 0) - { - /* Return the element at the previous position */ - return list.get(index - 1); - } + /** + * Returns the previous value from the list, which is the same as the element + * stored at the current index - 1. Null is returned if there are no more + * values to be returned (the start of the list has been reached). An + * ambiguity can occur here, as null may also be returned as a valid list + * element. This operation does not change the current value. + * + * @return The previous value from the list or null. + */ + public Object getPreviousValue() + { + // Check for a previous value. + if (index > 0) + // Return the element at the previous position. + return list.get(index - 1); else - { - /* Return null as this is the start of the list */ - return null; - } + // Return null as this is the start of the list. + return null; } - /** - * Returns the current value of the model. Initially, this will - * be the element at position 0. On later invocations, this will - * be the last element returned by <code>getNextValue()</code> - * or <code>getPreviousValue()</code>. - * - * @return The current value. - * @see SpinnerListModel#getPreviousValue() - * @see SpinnerListModel#getNextValue() - */ - public Object getValue() - { - return list.get(index); - } + /** + * Returns the current value of the model. Initially, this will + * be the element at position 0. On later invocations, this will + * be the last element returned by <code>getNextValue()</code> + * or <code>getPreviousValue()</code>. + * + * @return The current value. + * + * @see SpinnerListModel#getPreviousValue() + * @see SpinnerListModel#getNextValue() + */ + public Object getValue() + { + return list.get(index); + } - /** - * Changes the backing list for this model. The model only stores - * a reference to the list, so any changes made to the list elsewhere - * will be reflected in the values returned by the model. A - * <code>ChangeEvent</code> is fired if the list being used actually - * changes (i.e. the new list is not referentially equal (!=) to the - * old one). - * - * @param list The new list to use. - * @throws IllegalArgumentException if the list is null or contains - * no elements. - * @see ChangeEvent - */ - public void setList(List list) - { - /* Check for null or zero size list */ - if (list == null || list.size() == 0) - { - throw new IllegalArgumentException("The supplied list was invalid."); - } - /* Check for a change of referenced list */ - if (this.list != list) - { - /* Store the new list */ - this.list = list; - /* Notify listeners of a change */ - fireStateChanged(); - } - /* We reset the other values in either case */ - /* Set the index to 0 */ - index = 0; - } + /** + * Changes the backing list for this model. The model only stores + * a reference to the list, so any changes made to the list elsewhere + * will be reflected in the values returned by the model. A + * <code>ChangeEvent</code> is fired if the list being used actually + * changes (i.e. the new list is not referentially equal (!=) to the + * old one). + * + * @param list The new list to use. + * + * @throws IllegalArgumentException if the list is null or contains + * no elements. + * + * @see ChangeEvent + */ + public void setList(List list) + { + // Check for null or zero size list. + if (list == null || list.size() == 0) + throw new IllegalArgumentException("The supplied list was invalid."); - /** - * Sets the current value of the model to be the one supplied. - * The value must exist within the backing list in order for - * the change to take place. Otherwise, an exception is thrown. - * The value used is the first occurrence of the value within - * the backing list. Listeners are notified of this change. - * Following the change, <code>getNextValue()</code> and - * <code>getPreviousValue()</code> return the objects following - * and prior to the supplied value, respectively. - * - * @param value The requested new value of the list. - * @throws IllegalArgumentException if the supplied value does - * not exist in the backing list. - * @see SpinnerListModel#getPreviousValue() - * @see SpinnerListModel#getNextValue() - */ - public void setValue(Object value) - { - int valueIndex; + // Check for a change of referenced list. + if (this.list != list) + { + // Store the new list. + this.list = list; + // Notify listeners of a change. + fireStateChanged(); + } + // We reset the other values in either case. + // Set the index to 0. + index = 0; + } - /* Search for the value in the list */ - valueIndex = list.indexOf(value); - /* Check for the value being found */ - if (valueIndex == -1) - { - throw new IllegalArgumentException("The supplied value does not " - + "exist in this list"); - } - /* Make the indices match */ - index = valueIndex; - /* Notify the listeners */ - fireStateChanged(); - } + /** + * Sets the current value of the model to be the one supplied. + * The value must exist within the backing list in order for + * the change to take place. Otherwise, an exception is thrown. + * The value used is the first occurrence of the value within + * the backing list. Listeners are notified of this change. + * Following the change, <code>getNextValue()</code> and + * <code>getPreviousValue()</code> return the objects following + * and prior to the supplied value, respectively. + * + * @param value The requested new value of the list. + * + * @throws IllegalArgumentException if the supplied value does + * not exist in the backing list. + * + * @see SpinnerListModel#getPreviousValue() + * @see SpinnerListModel#getNextValue() + */ + public void setValue(Object value) + { + int valueIndex; + + // Search for the value in the list. + valueIndex = list.indexOf(value); + // Check for the value being found. + if (valueIndex == -1) + throw new IllegalArgumentException("The supplied value does not " + + "exist in this list"); + // Make the indices match. + index = valueIndex; + // Notify the listeners. + fireStateChanged(); + } } diff --git a/libjava/classpath/javax/swing/Spring.java b/libjava/classpath/javax/swing/Spring.java index 69c88c77d96..8f7105d496d 100644 --- a/libjava/classpath/javax/swing/Spring.java +++ b/libjava/classpath/javax/swing/Spring.java @@ -65,6 +65,7 @@ public abstract class Spring */ protected Spring() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java index df9ddffb654..592cc0e02a9 100644 --- a/libjava/classpath/javax/swing/SpringLayout.java +++ b/libjava/classpath/javax/swing/SpringLayout.java @@ -395,46 +395,40 @@ public class SpringLayout implements LayoutManager2 public SpringLayout.Constraints getConstraints(Component c) { Constraints constraints = (Constraints) constraintsMap.get(c); + if (constraints == null) { Container parent = c.getParent(); constraints = new Constraints(); + if (parent != null) { - constraints.setX - (Spring.constant(parent.getInsets().left)); - constraints.setY - (Spring.constant(parent.getInsets().top)); + constraints.setX(Spring.constant(parent.getInsets().left)); + constraints.setY(Spring.constant(parent.getInsets().top)); } else { - constraints.setX - (Spring.constant(0)); - constraints.setY - (Spring.constant(0)); - + constraints.setX(Spring.constant(0)); + constraints.setY(Spring.constant(0)); } - constraints.setWidth - (Spring.constant(c.getMinimumSize().width, - c.getPreferredSize().width, - c.getMaximumSize().width)); - constraints.setHeight - (Spring.constant(c.getMinimumSize().height, - c.getPreferredSize().height, - c.getMaximumSize().height)); - - constraintsMap.put(c, constraints); - } + constraints.setWidth(Spring.constant(c.getMinimumSize().width, + c.getPreferredSize().width, + c.getMaximumSize().width)); + constraints.setHeight(Spring.constant(c.getMinimumSize().height, + c.getPreferredSize().height, + c.getMaximumSize().height)); + constraintsMap.put(c, constraints); return constraints; } /** * Returns the X alignment of the Container <code>p</code>. - * - * @param p the {@link java.awt.Container} for which to determine the X - * alignment. + * + * @param p + * the {@link java.awt.Container} for which to determine the X + * alignment. * @return always 0.0 */ public float getLayoutAlignmentX(Container p) @@ -480,6 +474,7 @@ public class SpringLayout implements LayoutManager2 for (int index = 0; index < components.length; index++) { Component c = components[index]; + Constraints constraints = getConstraints(c); int x = constraints.getX().getValue(); int y = constraints.getY().getValue(); @@ -597,7 +592,6 @@ public class SpringLayout implements LayoutManager2 if (bottomEdge > maxY) maxY = bottomEdge; } - return new Dimension(maxX, maxY); } @@ -621,7 +615,6 @@ public class SpringLayout implements LayoutManager2 Spring strut = Spring.constant(pad); Spring otherEdge = constraints2.getConstraint(e2); constraints1.setConstraint(e1, Spring.sum(strut, otherEdge)); - } /** diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index ee5fa74d47a..58b3a78d7aa 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/javax/swing/SwingUtilities.java @@ -840,7 +840,7 @@ public class SwingUtilities iconR.width = icon.getIconWidth(); iconR.height = icon.getIconHeight(); } - if (text == null) + if (text == null || text.equals("")) { textIconGap = 0; textR.width = 0; @@ -890,7 +890,7 @@ public class SwingUtilities iconR.y = 0; textR.y = (horizontalTextPosition == CENTER ? iconR.height + textIconGap - : iconR.height - textR.height); + : Math.max(iconR.height - textR.height, 0)); break; case CENTER: int centerLine = Math.max(textR.height, iconR.height) / 2; @@ -1116,7 +1116,7 @@ public class SwingUtilities * <pre> * [{@link javax.swing.JComponent#getActionMap()}] * --> [{@link javax.swing.ActionMap}] - * parent --> [{@link javax.swing.text.KeymapActionMap}] + * parent --> [{@link javax.swing.text.JTextComponent.KeymapActionMap}] * parent --> [{@link javax.swing.plaf.ActionMapUIResource}] * </pre> * @@ -1138,14 +1138,12 @@ public class SwingUtilities else { ActionMap parent = child.getParent(); - while(parent != null) + while (parent != null && !(parent instanceof ActionMapUIResource)) { child = parent; parent = child.getParent(); } - - if (child != null) - child.setParent(uiActionMap); + child.setParent(uiActionMap); } } @@ -1159,7 +1157,7 @@ public class SwingUtilities * <pre> * [{@link javax.swing.JComponent#getInputMap()}] * --> [{@link javax.swing.InputMap}] - * parent --> [{@link javax.swing.text.KeymapWrapper}] + * parent --> [{@link javax.swing.text.JTextComponent.KeymapWrapper}] * parent --> [{@link javax.swing.plaf.InputMapUIResource}] * </pre> * @@ -1181,11 +1179,13 @@ public class SwingUtilities component.setInputMap(condition, uiInputMap); else { - while(child.getParent() != null - && !(child.getParent() instanceof InputMapUIResource)) - child = child.getParent(); - if (child != null) - child.setParent(uiInputMap); + InputMap parent = child.getParent(); + while (parent != null && !(parent instanceof InputMapUIResource)) + { + child = parent; + parent = parent.getParent(); + } + child.setParent(uiInputMap); } } diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java index 87f420a4367..cf91c23e8ec 100644 --- a/libjava/classpath/javax/swing/Timer.java +++ b/libjava/classpath/javax/swing/Timer.java @@ -46,7 +46,12 @@ import java.util.EventListener; import javax.swing.event.EventListenerList; /** - * Fires one or more action events after the specified delay. + * Fires one or more action events after the specified delay. This is + * a specialised version of <code>java.util.Timer</code> just for + * firing <code>ActionEvent</code>s. All Timers share one (daemon) + * Thread (or java.util.Timer). All events are fired from the event + * queue. + * * @author Ronald Veldema * @author Audrius Meskauskas (audriusa@Bionformatics.org) - bug fixes * and documentation comments @@ -55,48 +60,19 @@ public class Timer implements Serializable { /** - * The timer thread + * Given to the shared java.util.Timer to (possibly repeatedly) call + * queueEvent(). */ - private class Waker - extends Thread + private class Task extends java.util.TimerTask { - /** - * Fires events, pausing for required intervals. - */ public void run() { - running = true; - try - { - sleep(initialDelay); - - queueEvent(); - - while (running) - { - try - { - sleep(delay); - } - catch (InterruptedException e) - { - return; - } - queueEvent(); - - if (logTimers) - System.out.println("javax.swing.Timer -> clocktick"); - - if ( ! repeats) - break; - } - running = false; - } - catch (Exception e) - { - // The timer is no longer running. - running = false; - } + if (logTimers) + System.out.println("javax.swing.Timer -> queueEvent()"); + queueEvent(); + + if (!repeats) + task = null; } } @@ -118,6 +94,14 @@ public class Timer }; /** + * The static java.util.Timer daemon which will be used to schedule + * all javax.swing.Timer.Task objects. The daemon will always be + * running, even if there's no task scheduled in it. + */ + private static java.util.Timer timer = new java.util.Timer("swing.Timer", + true); + + /** * If <code>true</code>, the timer prints a message to * {@link System#out} when firing each event. */ @@ -139,12 +123,6 @@ public class Timer boolean repeats = true; /** - * <code>true</code> if the timer is currently active, firing events - * as scheduled. - */ - boolean running; - - /** * The delay between subsequent repetetive events. */ int delay; @@ -162,10 +140,9 @@ public class Timer int ticks; /** - * Stores the thread that posts events to the queue at required time - * intervals. + * The task that calls queueEvent(). When null this Timer is stopped. */ - private Waker waker; + private Task task; /** * This object manages a "queue" of virtual actionEvents, maintained as a @@ -360,7 +337,7 @@ public class Timer */ public boolean isRunning() { - return running; + return task != null; } /** @@ -398,10 +375,16 @@ public class Timer */ public void start() { - if (isRunning()) - return; - waker = new Waker(); - waker.start(); + Task t = task; + if (t == null) + { + t = new Task(); + if (isRepeats()) + timer.schedule(t, getInitialDelay(), getDelay()); + else + timer.schedule(t, getInitialDelay()); + task = t; + } } /** @@ -409,12 +392,11 @@ public class Timer */ public void stop() { - running = false; - if (waker != null) - waker.interrupt(); - synchronized (queueLock) + Task t = task; + if (t != null) { - queue = 0; + t.cancel(); + task = null; } } @@ -475,11 +457,11 @@ public class Timer */ void queueEvent() { - synchronized (queueLock) + synchronized(queueLock) { - queue++; - if (queue == 1) - SwingUtilities.invokeLater(drainer); + queue++; + if (queue == 1) + SwingUtilities.invokeLater(drainer); } } } diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java index 61693763898..03835794b68 100644 --- a/libjava/classpath/javax/swing/ToolTipManager.java +++ b/libjava/classpath/javax/swing/ToolTipManager.java @@ -44,7 +44,6 @@ import java.awt.FlowLayout; import java.awt.LayoutManager; import java.awt.Panel; import java.awt.Point; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -68,6 +67,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected stillInsideTimerAction() { + // Nothing to do here. } /** @@ -93,6 +93,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected outsideTimerAction() { + // Nothing to do here. } /** @@ -103,6 +104,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ public void actionPerformed(ActionEvent event) { + // TODO: What should be done here, if anything? } } @@ -118,6 +120,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected insideTimerAction() { + // Nothing to do here. } /** @@ -129,8 +132,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener public void actionPerformed(ActionEvent event) { showTip(); - if (insideTimer != null) - insideTimer.start(); } } @@ -183,7 +184,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener * The window used when the tooltip doesn't fit inside the current * container. */ - private static JWindow tooltipWindow; + private static JDialog tooltipWindow; /** * Creates a new ToolTipManager and sets up the timers. @@ -373,7 +374,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener if (exitTimer.isRunning()) { exitTimer.stop(); - showTip(); insideTimer.start(); return; } @@ -424,13 +424,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener insideTimer.stop(); hideTip(); } - - if (currentComponent == null) - currentComponent = (Component) event.getSource(); - - currentComponent.invalidate(); - currentComponent.validate(); - currentComponent.repaint(); } /** @@ -455,16 +448,8 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener public void mouseMoved(MouseEvent event) { currentPoint = event.getPoint(); - if (currentTip != null) - { - if (currentComponent == null) - currentComponent = (Component) event.getSource(); - - String text = ((JComponent) currentComponent).getToolTipText(event); - currentTip.setTipText(text); - } if (enterTimer.isRunning()) - enterTimer.restart(); + enterTimer.restart(); } /** @@ -474,70 +459,103 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ void showTip() { - if (! enabled || currentComponent == null) + if (!enabled || currentComponent == null || !currentComponent.isEnabled() + || (currentTip != null && currentTip.isVisible())) return; - if (currentTip == null - || currentTip.getComponent() != currentComponent + if (currentTip == null || currentTip.getComponent() != currentComponent && currentComponent instanceof JComponent) currentTip = ((JComponent) currentComponent).createToolTip(); + + currentTip.setVisible(true); + Container parent = currentComponent.getParent(); Point p = currentPoint; Dimension dims = currentTip.getPreferredSize(); - if (canToolTipFit(currentTip)) - { - JLayeredPane pane = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class, - currentComponent)) - .getLayeredPane(); - - // This should never happen, but just in case. - if (pane == null) - return; - - if (containerPanel != null) - hideTip(); - if (isLightWeightPopupEnabled()) - { - containerPanel = new Panel(); - JRootPane root = new JRootPane(); - root.getContentPane().add(currentTip); - containerPanel.add(root); - } - else - { - containerPanel = new JPanel(); - containerPanel.add(currentTip); - } - LayoutManager lm = containerPanel.getLayout(); - if (lm instanceof FlowLayout) - { - FlowLayout fm = (FlowLayout) lm; - fm.setVgap(0); - fm.setHgap(0); - } - - p = getGoodPoint(p, pane, currentTip, dims); - - pane.add(containerPanel); - containerPanel.setBounds(p.x, p.y, dims.width, dims.height); - currentTip.setBounds(0, 0, dims.width, dims.height); - - pane.revalidate(); - pane.repaint(); - } + + if (parent instanceof JPopupMenu) + setLightWeightPopupEnabled(((JPopupMenu) parent).isLightWeightPopupEnabled()); else + setLightWeightPopupEnabled(true); + + if (isLightWeightPopupEnabled()) { - SwingUtilities.convertPointToScreen(p, currentComponent); - tooltipWindow = new JWindow(); - tooltipWindow.getContentPane().add(currentTip); - tooltipWindow.setFocusable(false); - tooltipWindow.pack(); - tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height); - tooltipWindow.show(); + JLayeredPane pane = null; + JRootPane r = ((JRootPane) SwingUtilities. + getAncestorOfClass(JRootPane.class, currentComponent)); + if (r != null) + pane = r.getLayeredPane(); + if (pane == null) + return; + + if (containerPanel != null) + hideTip(); + + containerPanel = new Panel(); + JRootPane root = new JRootPane(); + root.getContentPane().add(currentTip); + containerPanel.add(root); + + LayoutManager lm = containerPanel.getLayout(); + if (lm instanceof FlowLayout) + { + FlowLayout fm = (FlowLayout) lm; + fm.setVgap(0); + fm.setHgap(0); + } + + p = SwingUtilities.convertPoint(currentComponent, p, pane); + p = adjustLocation(p, pane, dims); + + pane.add(containerPanel); + containerPanel.setBounds(p.x, p.y, dims.width, dims.height); + currentTip.setBounds(0, 0, dims.width, dims.height); + containerPanel.validate(); + containerPanel.repaint(); + } + else if (currentComponent.isShowing()) + { + SwingUtilities.convertPointToScreen(p, currentComponent); + p = adjustLocation(p, SwingUtilities.getWindowAncestor(currentComponent), + dims); + + tooltipWindow = new JDialog(); + tooltipWindow.setContentPane(currentTip); + tooltipWindow.setUndecorated(true); + tooltipWindow.getRootPane(). + setWindowDecorationStyle(JRootPane.PLAIN_DIALOG); + tooltipWindow.pack(); + tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height); + tooltipWindow.show(); + tooltipWindow.validate(); + tooltipWindow.repaint(); + currentTip.revalidate(); + currentTip.repaint(); } - currentTip.setVisible(true); } /** + * Adjusts the point to a new location on the component, + * using the currentTip's dimensions. + * + * @param p - the point to convert. + * @param c - the component the point is on. + * @param d - the dimensions of the currentTip. + */ + private Point adjustLocation(Point p, Component c, Dimension d) + { + if (p.x + d.width > c.getWidth()) + p.x -= d.width; + if (p.x < 0) + p.x = 0; + if (p.y + d.height < c.getHeight()) + p.y += d.height; + if (p.y + d.height > c.getHeight()) + p.y -= d.height*2; + + return p; + } + + /** * This method hides the ToolTip. * This is package-private to avoid an accessor method. */ @@ -552,15 +570,11 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener if (parent == null) return; parent.remove(containerPanel); - parent.invalidate(); - parent.validate(); - parent.repaint(); parent = currentTip.getParent(); if (parent == null) return; parent.remove(currentTip); - containerPanel = null; } if (tooltipWindow != null) @@ -569,35 +583,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener tooltipWindow.dispose(); tooltipWindow = null; } - } - - /** - * This method returns a point in the LayeredPane where the ToolTip can be - * shown. The point returned (if the ToolTip is to be displayed at the - * preferred dimensions) will always place the ToolTip inside the - * currentComponent if possible. - * - * @param p The last known good point for the mouse. - * @param c The JLayeredPane in the first RootPaneContainer up from the - * currentComponent. - * @param tip The ToolTip to display. - * @param dims The ToolTip preferred dimensions (can be null). - * - * @return A good point to place the ToolTip. - */ - private Point getGoodPoint(Point p, JLayeredPane c, JToolTip tip, - Dimension dims) - { - if (dims == null) - dims = tip.getPreferredSize(); - Rectangle bounds = currentComponent.getBounds(); - if (p.x + dims.width > bounds.width) - p.x = bounds.width - dims.width; - if (p.y + dims.height > bounds.height) - p.y = bounds.height - dims.height; - - p = SwingUtilities.convertPoint(currentComponent, p, c); - return p; + currentTip = null; } /** @@ -622,25 +608,4 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); return target; } - - /** - * This method returns whether the ToolTip can fit in the first - * RootPaneContainer up from the currentComponent. - * - * @param tip The ToolTip. - * - * @return Whether the ToolTip can fit. - */ - private boolean canToolTipFit(JToolTip tip) - { - JRootPane root = (JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class, - currentComponent); - if (root == null) - return false; - Dimension pref = tip.getPreferredSize(); - Dimension rootSize = root.getSize(); - if (rootSize.width > pref.width && rootSize.height > pref.height) - return true; - return false; - } } diff --git a/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java index 96cb9d42abf..4828fdbfa98 100644 --- a/libjava/classpath/javax/swing/TransferHandler.java +++ b/libjava/classpath/javax/swing/TransferHandler.java @@ -75,30 +75,38 @@ public class TransferHandler implements Serializable } } + /** + * Get the system cliboard. If not available, create and return the VM-local + * clipboard. + * + * @param component a component, used to get the toolkit. + * @return the clipboard + */ private static Clipboard getClipboard(JComponent component) { - SecurityManager sm = System.getSecurityManager(); - - if (sm != null) - { - try - { - sm.checkSystemClipboardAccess(); - - // We may access system clipboard. - return component.getToolkit().getSystemClipboard(); - } - catch (SecurityException e) - { - // We may not access system clipboard. - } - } - - // Create VM-local clipboard if non exists yet. - if (clipboard == null) - clipboard = new Clipboard("Clipboard"); - - return clipboard; + // Avoid throwing exception if the system clipboard access failed + // in the past. + if (clipboard != null) + return clipboard; + else + { + try + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSystemClipboardAccess(); + + // We may access system clipboard. + return component.getToolkit().getSystemClipboard(); + } + catch (Exception e) + { + // We may not access system clipboard. + // Create VM-local clipboard if none exists yet. + clipboard = new Clipboard("Clipboard"); + return clipboard; + } + } } } @@ -162,15 +170,18 @@ public class TransferHandler implements Serializable } public void exportAsDrag (JComponent c, InputEvent e, int action) - { + { + // TODO: Implement this properly } protected void exportDone (JComponent c, Transferable data, int action) { + // TODO: Implement this properly } public void exportToClipboard(JComponent c, Clipboard clip, int action) { + // TODO: Implement this properly } public int getSourceActions (JComponent c) diff --git a/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java index ab78ca6442f..f6aee1b944c 100644 --- a/libjava/classpath/javax/swing/UIDefaults.java +++ b/libjava/classpath/javax/swing/UIDefaults.java @@ -577,8 +577,8 @@ public class UIDefaults extends Hashtable * * @param key the key to the requested entry * - * @return the boolean entry for <code>key</code> or null if no such entry - * exists + * @return The boolean entry for <code>key</code> or <code>false</code> if no + * such entry exists. */ public boolean getBoolean(Object key) { @@ -674,9 +674,9 @@ public class UIDefaults extends Hashtable return null; try { - if (loader != null) - return loader.loadClass (className); - return Class.forName (className); + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + return loader.loadClass (className); } catch (Exception e) { diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java index f4648f1e219..15a78190303 100644 --- a/libjava/classpath/javax/swing/UIManager.java +++ b/libjava/classpath/javax/swing/UIManager.java @@ -146,19 +146,29 @@ public class UIManager implements Serializable LookAndFeel laf = (LookAndFeel) lafClass.newInstance(); setLookAndFeel(laf); } + else + { + setLookAndFeel(new MetalLookAndFeel()); + } } catch (Exception ex) { System.err.println("cannot initialize Look and Feel: " + defaultlaf); - System.err.println("error: " + ex.getMessage()); + System.err.println("error: " + ex.toString()); System.err.println("falling back to Metal Look and Feel"); + try + { + setLookAndFeel(new MetalLookAndFeel()); + } + catch (Exception ex2) + { + throw (Error) new AssertionError("There must be no problem installing" + + " the MetalLookAndFeel.") + .initCause(ex2); + } } - currentLookAndFeel = new MetalLookAndFeel(); - currentLookAndFeel.initialize(); - currentUIDefaults = currentLookAndFeel.getDefaults(); - } - + /** * Creates a new instance of the <code>UIManager</code>. There is no need * to construct an instance of this class, since all methods are static. diff --git a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java index 5abe45fe117..f99c0ac19f7 100644 --- a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java +++ b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java @@ -40,8 +40,8 @@ package javax.swing; public class UnsupportedLookAndFeelException extends Exception { - public UnsupportedLookAndFeelException(String a) - { - super(a); - } + public UnsupportedLookAndFeelException(String a) + { + super(a); + } } diff --git a/libjava/classpath/javax/swing/ViewportLayout.java b/libjava/classpath/javax/swing/ViewportLayout.java index 19735839387..884f7cb27a8 100644 --- a/libjava/classpath/javax/swing/ViewportLayout.java +++ b/libjava/classpath/javax/swing/ViewportLayout.java @@ -56,12 +56,17 @@ public class ViewportLayout implements LayoutManager, Serializable public ViewportLayout() { + // Nothing to do here. } + public void addLayoutComponent(String name, Component c) { + // Nothing to do here. } + public void removeLayoutComponent(Component c) { + // Nothing to do here. } public Dimension preferredLayoutSize(Container parent) diff --git a/libjava/classpath/javax/swing/WindowConstants.java b/libjava/classpath/javax/swing/WindowConstants.java index 5e364434484..aaa0cb9a3ab 100644 --- a/libjava/classpath/javax/swing/WindowConstants.java +++ b/libjava/classpath/javax/swing/WindowConstants.java @@ -43,31 +43,26 @@ package javax.swing; * * @author Andrew Selkirk */ -public interface WindowConstants { +public interface WindowConstants +{ + /** + * DO_NOTHING_ON_CLOSE + */ + int DO_NOTHING_ON_CLOSE = 0; - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- + /** + * HIDE_ON_CLOSE + */ + int HIDE_ON_CLOSE = 1; - /** - * DO_NOTHING_ON_CLOSE - */ - int DO_NOTHING_ON_CLOSE = 0; + /** + * DISPOSE_ON_CLOSE + */ + int DISPOSE_ON_CLOSE = 2; - /** - * HIDE_ON_CLOSE - */ - int HIDE_ON_CLOSE = 1; + /** + * EXIT_ON_CLOSE + */ + int EXIT_ON_CLOSE =3; - /** - * DISPOSE_ON_CLOSE - */ - int DISPOSE_ON_CLOSE = 2; - - /** - * EXIT_ON_CLOSE - */ - int EXIT_ON_CLOSE =3; - - -} // WindowConstants +} diff --git a/libjava/classpath/javax/swing/border/AbstractBorder.java b/libjava/classpath/javax/swing/border/AbstractBorder.java index 951debd5207..7cbbcdaa83b 100644 --- a/libjava/classpath/javax/swing/border/AbstractBorder.java +++ b/libjava/classpath/javax/swing/border/AbstractBorder.java @@ -52,20 +52,18 @@ import java.io.Serializable; * @author Sascha Brawer (brawer@dandelis.ch) * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public abstract class AbstractBorder - implements Border, Serializable +public abstract class AbstractBorder implements Border, Serializable { static final long serialVersionUID = -545885975315191844L; - /** * Constructs a new AbstractBorder. */ - public AbstractBorder () + public AbstractBorder() { + // Nothing to do here. } - /** * Performs nothing, because the default implementation provided by * this class is an invisible, zero-width border. Subclasses will @@ -79,17 +77,15 @@ public abstract class AbstractBorder * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - public void paintBorder (Component c, Graphics g, - int x, int y, int width, int height) + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { - /* A previous version of Classpath had emitted a warning when - * this method was called. The warning was removed because it is - * perfectly legal for a subclass to not override the paintBorder - * method. An example would be EmptyBorder. - */ + // A previous version of Classpath had emitted a warning when + // this method was called. The warning was removed because it is + // perfectly legal for a subclass to not override the paintBorder + // method. An example would be EmptyBorder. } - /** * Measures the width of this border. * @@ -102,31 +98,29 @@ public abstract class AbstractBorder * * @see #getBorderInsets(java.awt.Component, java.awt.Insets) */ - public Insets getBorderInsets (Component c) + public Insets getBorderInsets(Component c) { - return new Insets (0, 0, 0, 0); + return new Insets(0, 0, 0, 0); } - /** * Determines the insets of this border. The implementation provided * by AbstractButton sets the <code>left</code>, <code>right</code>, * <code>top</code> and <code>bottom</code> fields of the passed * <code>insets</code> parameter to zero. * - * @param c the component whose border is to be measured. + * @param c the component whose border is to be measured * - * @return the same object that was passed for <code>insets</code>. + * @return the same object that was passed for <code>insets</code> * * @see #getBorderInsets(Component) */ - public Insets getBorderInsets (Component c, Insets insets) + public Insets getBorderInsets(Component c, Insets insets) { insets.left = insets.right = insets.top = insets.bottom = 0; return insets; } - /** * Determines whether or not this border is opaque. An opaque border * fills every pixel in its area when painting. Partially @@ -136,12 +130,11 @@ public abstract class AbstractBorder * * @return <code>false</code>. */ - public boolean isBorderOpaque () + public boolean isBorderOpaque() { return false; } - /** * Returns a rectangle that covers the specified area minus this * border. Components that wish to determine an area into which @@ -154,12 +147,11 @@ public abstract class AbstractBorder * @param width the width of the available area for the border. * @param height the height of the available area for the border. */ - public Rectangle getInteriorRectangle (Component c, - int x, int y, int width, int height) + public Rectangle getInteriorRectangle(Component c, int x, int y, int width, + int height) { return getInteriorRectangle (c, this, x, y, width, height); } - /** * Returns a rectangle that covers the specified area minus a @@ -173,8 +165,8 @@ public abstract class AbstractBorder * @param width the width of the available area for the border. * @param height the height of the available area for the border. */ - public static Rectangle getInteriorRectangle (Component c, Border b, - int x, int y, int width, int height) + public static Rectangle getInteriorRectangle(Component c, Border b, int x, + int y, int width, int height) { Insets borderInsets; diff --git a/libjava/classpath/javax/swing/border/BevelBorder.java b/libjava/classpath/javax/swing/border/BevelBorder.java index fcdc1c64675..45b758cae41 100644 --- a/libjava/classpath/javax/swing/border/BevelBorder.java +++ b/libjava/classpath/javax/swing/border/BevelBorder.java @@ -55,8 +55,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BevelBorder - extends AbstractBorder +public class BevelBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -508,11 +507,11 @@ public class BevelBorder * Paints a two-pixel bevel in four colors. * * <pre> - * @@@@@@@@@@@@ - * @..........# @ = color a - * @. X# . = color b - * @. X# X = color c - * @.XXXXXXXXX# # = color d + * ++++++++++++ + * +..........# + = color a + * +. X# . = color b + * +. X# X = color c + * +.XXXXXXXXX# # = color d * ############</pre> * * @param g the graphics for painting. diff --git a/libjava/classpath/javax/swing/border/Border.java b/libjava/classpath/javax/swing/border/Border.java index 11bddfe78b3..f4af3fb38bb 100644 --- a/libjava/classpath/javax/swing/border/Border.java +++ b/libjava/classpath/javax/swing/border/Border.java @@ -77,9 +77,8 @@ public interface Border * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - void paintBorder(Component c, Graphics g, - int x, int y, int width, int height); - + void paintBorder(Component c, Graphics g, int x, int y, int width, + int height); /** * Measures the width of this border. @@ -92,7 +91,6 @@ public interface Border */ Insets getBorderInsets(Component c); - /** * Determines whether this border fills every pixel in its area * when painting. diff --git a/libjava/classpath/javax/swing/border/CompoundBorder.java b/libjava/classpath/javax/swing/border/CompoundBorder.java index 2130a0e3447..998a9bab3bd 100644 --- a/libjava/classpath/javax/swing/border/CompoundBorder.java +++ b/libjava/classpath/javax/swing/border/CompoundBorder.java @@ -48,8 +48,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class CompoundBorder - extends AbstractBorder +public class CompoundBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -57,7 +56,6 @@ public class CompoundBorder */ static final long serialVersionUID = 9054540377030555103L; - /** * The inside border, which is painted between the bordered * Component and the outside border. It is valid for @@ -65,7 +63,6 @@ public class CompoundBorder */ protected Border insideBorder; - /** * The outside border, which is painted outside both the * bordered Component and the inside border. It is valid for @@ -73,7 +70,6 @@ public class CompoundBorder */ protected Border outsideBorder; - /** * Constructs a CompoundBorder whose inside and outside borders * are both <code>null</code>. While this does not really make @@ -83,12 +79,11 @@ public class CompoundBorder * * @see EmptyBorder */ - public CompoundBorder () + public CompoundBorder() { this (null, null); } - /** * Constructs a CompoundBorder with the specified inside and * outside borders. @@ -103,13 +98,12 @@ public class CompoundBorder * component. It is acceptable to pass <code>null</code>, in * which case no inside border is painted. */ - public CompoundBorder (Border outsideBorder, Border insideBorder) + public CompoundBorder(Border outsideBorder, Border insideBorder) { this.outsideBorder = outsideBorder; this.insideBorder = insideBorder; } - /** * Determines whether or not this border is opaque. An opaque * border fills every pixel in its area when painting. Partially @@ -119,20 +113,18 @@ public class CompoundBorder * @return <code>true</code> if both the inside and outside borders * are opaque, or <code>false</code> otherwise. */ - public boolean isBorderOpaque () + public boolean isBorderOpaque() { - /* While it would be safe to assume true for the opacity of - * a null border, this behavior would not be according to - * the API specification. Also, it is pathological to have - * null borders anyway. - */ + // While it would be safe to assume true for the opacity of + // a null border, this behavior would not be according to + // the API specification. Also, it is pathological to have + // null borders anyway. if ((insideBorder == null) || (outsideBorder == null)) return false; return insideBorder.isBorderOpaque() && outsideBorder.isBorderOpaque(); } - /** * Paints the compound border by first painting the outside border, @@ -148,9 +140,9 @@ public class CompoundBorder public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - /* If there is an outside border, paint it and reduce the - * bounding box by its insets. - */ + // If there is an outside border, paint it and reduce the + // bounding box by its insets. + // if (outsideBorder != null) { Insets outsideInsets; @@ -161,9 +153,8 @@ public class CompoundBorder x += outsideInsets.left; y += outsideInsets.top; - /* Reduce width and height by the respective extent of the - * outside border. - */ + // Reduce width and height by the respective extent of the + // outside border. width -= outsideInsets.left + outsideInsets.right; height -= outsideInsets.top + outsideInsets.bottom; } @@ -172,7 +163,6 @@ public class CompoundBorder insideBorder.paintBorder(c, g, x, y, width, height); } - /** * Changes the specified insets to the insets of this border, * which is the sum of the insets of the inside and the outside @@ -192,7 +182,7 @@ public class CompoundBorder else insets.left = insets.right = insets.top = insets.bottom = 0; - /* If there is an outside border, add it to insets. */ + // If there is an outside border, add it to insets. if (outsideBorder != null) { borderInsets = outsideBorder.getBorderInsets(c); @@ -202,7 +192,7 @@ public class CompoundBorder insets.bottom += borderInsets.bottom; } - /* If there is an inside border, add it to insets. */ + // If there is an inside border, add it to insets. if (insideBorder != null) { borderInsets = insideBorder.getBorderInsets(c); @@ -215,35 +205,31 @@ public class CompoundBorder return insets; } - /** * Determines the insets of this border, which is the sum of the * insets of the inside and the outside border. * * @param c the component in the center of this border. */ - public Insets getBorderInsets (Component c) + public Insets getBorderInsets(Component c) { - /* It is not clear why CompoundBorder does not simply inherit - * the implementation from AbstractBorder. However, we want - * to be compatible with the API specification, which overrides - * the getBorderInsets(Component) method. - */ + // It is not clear why CompoundBorder does not simply inherit + // the implementation from AbstractBorder. However, we want + // to be compatible with the API specification, which overrides + // the getBorderInsets(Component) method. return getBorderInsets (c, null); } - /** * Returns the outside border, which is painted outside both the * bordered Component and the inside border. It is valid for the * result to be <code>null</code>. */ - public Border getOutsideBorder () + public Border getOutsideBorder() { return outsideBorder; } - /** * Returns the inside border, which is painted between the bordered * Component and the outside border. It is valid for the result to @@ -254,4 +240,3 @@ public class CompoundBorder return insideBorder; } } - diff --git a/libjava/classpath/javax/swing/border/EmptyBorder.java b/libjava/classpath/javax/swing/border/EmptyBorder.java index 0f3b7b6931c..c8e9c604469 100644 --- a/libjava/classpath/javax/swing/border/EmptyBorder.java +++ b/libjava/classpath/javax/swing/border/EmptyBorder.java @@ -53,8 +53,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class EmptyBorder - extends AbstractBorder +public class EmptyBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -142,6 +141,7 @@ public class EmptyBorder public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/border/EtchedBorder.java b/libjava/classpath/javax/swing/border/EtchedBorder.java index 0bd76ff0fb6..22882b78cb1 100644 --- a/libjava/classpath/javax/swing/border/EtchedBorder.java +++ b/libjava/classpath/javax/swing/border/EtchedBorder.java @@ -56,8 +56,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class EtchedBorder - extends AbstractBorder +public class EtchedBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -199,8 +198,8 @@ public class EtchedBorder * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - public void paintBorder(Component c, Graphics g, - int x, int y, int width, int height) + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { switch (etchType) { @@ -270,16 +269,14 @@ public class EtchedBorder */ public boolean isBorderOpaque() { - /* If the colors are to be drived from the enclosed Component's - * background color, the border is guaranteed to be fully opaque - * because Color.brighten() and Color.darken() always return an - * opaque color. - */ + // If the colors are to be derived from the enclosed Component's + // background color, the border is guaranteed to be fully opaque + // because Color.brighten() and Color.darken() always return an + // opaque color. return ((highlight == null) || (highlight.getAlpha() == 255)) && ((shadow == null) || (shadow.getAlpha() == 255)); } - /** * Returns the appearance of this EtchedBorder, which is either @@ -310,8 +307,7 @@ public class EtchedBorder else return c.getBackground().brighter(); } - - + /** * Returns the color that will be used for highlighted parts when * painting the border, or <code>null</code> if that color will be @@ -359,11 +355,11 @@ public class EtchedBorder * Paints a two-pixel etching in two colors. * * <pre> - * @@@@@@@@@@@. - * @.........@. @ = color a - * @. @. . = color b - * @. @. - * @@@@@@@@@@@. + * +++++++++++. + * +.........+. + = color a + * +. +. . = color b + * +. +. + * +++++++++++. * ............</pre> * * @param g the graphics for painting. @@ -374,9 +370,8 @@ public class EtchedBorder * @param a one of the two colors. * @param b the second of the two colors. */ - private static void paintEtchedBorder(Graphics g, - int x, int y, int width, int height, - Color a, Color b) + private static void paintEtchedBorder(Graphics g, int x, int y, int width, + int height, Color a, Color b) { Color oldColor; @@ -387,11 +382,10 @@ public class EtchedBorder try { - /* To understand this code, it might be helpful to look at the - * images that are included with the JavaDoc. They are located - * in the "doc-files" subdirectory. EtchedBorder-2.png might - * be especially informative. - */ + // To understand this code, it might be helpful to look at the + // images that are included with the JavaDoc. They are located + // in the "doc-files" subdirectory. EtchedBorder-2.png might + // be especially informative. g.setColor(a); g.drawRect(0, 0, width - 1, height - 1); @@ -408,4 +402,3 @@ public class EtchedBorder } } } - diff --git a/libjava/classpath/javax/swing/border/LineBorder.java b/libjava/classpath/javax/swing/border/LineBorder.java index c34e38c5789..36abddd915d 100644 --- a/libjava/classpath/javax/swing/border/LineBorder.java +++ b/libjava/classpath/javax/swing/border/LineBorder.java @@ -50,8 +50,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class LineBorder - extends AbstractBorder +public class LineBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -71,7 +70,7 @@ public class LineBorder /** * A shared instance of a gray, one pixel thick, plain LineBorder. * The singleton object is lazily created by {@link - * #createBlackGrayBorder()} upon its first invocation. + * #createGrayLineBorder()} upon its first invocation. */ private static LineBorder grayLineBorder; @@ -213,29 +212,27 @@ public class LineBorder { g.setColor(lineColor); - /* If width and height were not adjusted, the border would - * appear one pixel too large in both directions. - */ + // If width and height were not adjusted, the border would + // appear one pixel too large in both directions. width -= 1; height -= 1; - /* Blurred, too large appearance - * ----------------------------- - * While Java 2D has introduced line strokes of arbitrary width, - * it seems desirable to keep this code independent of Java 2D. - * Therefore, multiple nested rectangles (or rounded rectangles) - * are drawn in order to simulate a line whose thickness is - * greater than one pixel. - * - * This hack causes a blurred appearance when anti-aliasing is - * on. Interestingly enough, though, the Sun JDK 1.3.1 (at least - * on MacOS X 10.1.5) shows exactly the same appearance under - * this condition. It thus seems likely that Sun does the same - * hack for simulating thick lines. For this reason, the - * blurred appearance seems acceptable -- especially since GNU - * Classpath tries to be compatible with the Sun reference - * implementation. - */ + // Blurred, too large appearance + // ----------------------------- + // While Java 2D has introduced line strokes of arbitrary width, + // it seems desirable to keep this code independent of Java 2D. + // Therefore, multiple nested rectangles (or rounded rectangles) + // are drawn in order to simulate a line whose thickness is + // greater than one pixel. + // + // This hack causes a blurred appearance when anti-aliasing is + // on. Interestingly enough, though, the Sun JDK 1.3.1 (at least + // on MacOS X 10.1.5) shows exactly the same appearance under + // this condition. It thus seems likely that Sun does the same + // hack for simulating thick lines. For this reason, the + // blurred appearance seems acceptable -- especially since GNU + // Classpath tries to be compatible with the Sun reference + // implementation. for (int i = 0; i < thickness; i++) { if (roundedCorners) @@ -340,4 +337,3 @@ public class LineBorder return (!roundedCorners) && (lineColor.getAlpha() == 255); } } - diff --git a/libjava/classpath/javax/swing/border/MatteBorder.java b/libjava/classpath/javax/swing/border/MatteBorder.java index f7ff1ca01c3..4d5b8c25360 100644 --- a/libjava/classpath/javax/swing/border/MatteBorder.java +++ b/libjava/classpath/javax/swing/border/MatteBorder.java @@ -54,8 +54,7 @@ import javax.swing.Icon; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class MatteBorder - extends EmptyBorder +public class MatteBorder extends EmptyBorder { /** * Determined using the <code>serialver</code> tool @@ -401,4 +400,3 @@ public class MatteBorder } } } - diff --git a/libjava/classpath/javax/swing/border/SoftBevelBorder.java b/libjava/classpath/javax/swing/border/SoftBevelBorder.java index 379ecb65a35..028fd00e021 100644 --- a/libjava/classpath/javax/swing/border/SoftBevelBorder.java +++ b/libjava/classpath/javax/swing/border/SoftBevelBorder.java @@ -55,8 +55,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class SoftBevelBorder - extends BevelBorder +public class SoftBevelBorder extends BevelBorder { /** * Determined using the <code>serialver</code> tool @@ -264,10 +263,10 @@ public class SoftBevelBorder * Paints a soft bevel in four colors. * * <pre> - * @@@@@@@@@@@. - * @@.........# @ = color a - * @.. # . = color b - * @. # X = color c + * +++++++++++. + * ++.........# + = color a + * +.. # . = color b + * +. # X = color c * .. X# # = color d * . ##########</pre> * @@ -326,4 +325,3 @@ public class SoftBevelBorder } } } - diff --git a/libjava/classpath/javax/swing/border/TitledBorder.java b/libjava/classpath/javax/swing/border/TitledBorder.java index ceae2b1c9e3..8d3ee13d4bb 100644 --- a/libjava/classpath/javax/swing/border/TitledBorder.java +++ b/libjava/classpath/javax/swing/border/TitledBorder.java @@ -1,5 +1,5 @@ /* TitledBorder.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -46,6 +46,9 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.AffineTransform; import javax.swing.UIManager; @@ -55,8 +58,7 @@ import javax.swing.UIManager; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class TitledBorder - extends AbstractBorder +public class TitledBorder extends AbstractBorder { /** * A value for the <code>titlePosition</code> property that vertically @@ -301,7 +303,7 @@ public class TitledBorder public TitledBorder(String title) { this(/* border */ null, - title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + title, LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -314,7 +316,7 @@ public class TitledBorder */ public TitledBorder(Border border) { - this(border, /* title */ "", DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + this(border, /* title */ "", LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -330,7 +332,7 @@ public class TitledBorder */ public TitledBorder(Border border, String title) { - this(border, title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + this(border, title, LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -504,7 +506,7 @@ public class TitledBorder public void paint(Graphics g) { if (b != null) - b.paintBorder(c, g, x, y, width - 1, height - 1); + b.paintBorder(c, g, x, y, width, height); } @@ -562,7 +564,7 @@ public class TitledBorder if (stripeHeight > 0) { paint(g, x, holeY, holeX - x, stripeHeight); // patches #2 and #3 - paint(g, holeX + holeWidth, holeY, width - (holeX + holeWidth), stripeHeight); + paint(g, holeX + holeWidth, holeY, x + width - (holeX + holeWidth), stripeHeight); } stripeHeight = height - (holeY - y + holeHeight); @@ -574,16 +576,16 @@ public class TitledBorder BorderPainter bp; int textX, textY, borderWidth, borderHeight; - borderWidth = width - (mes.borderSpacing.left + mes.borderSpacing.right); - borderHeight = height - (mes.borderSpacing.top + mes.borderSpacing.bottom); + borderWidth = width - (mes.outerSpacing.left + mes.outerSpacing.right); + borderHeight = height - (mes.outerSpacing.top + mes.outerSpacing.bottom); bp = new BorderPainter(c, getBorder(), - x + mes.borderSpacing.left, y + mes.borderSpacing.top, + x + mes.outerSpacing.left, y + mes.outerSpacing.top, borderWidth, borderHeight); switch (getRealTitleJustification(c)) { case LEFT: - textX = x + TEXT_INSET_H; + textX = x + EDGE_SPACING + TEXT_INSET_H; break; case CENTER: @@ -601,21 +603,22 @@ public class TitledBorder switch (titlePosition) { case ABOVE_TOP: - textY = y; + textY = y + EDGE_SPACING; break; case TOP: case DEFAULT_POSITION: default: - textY = y + mes.borderSpacing.top + mes.borderInsets.top - mes.textAscent; + textY = y + mes.outerSpacing.top + mes.borderInsets.top - mes.textAscent + + mes.lineHeight; break; case BELOW_TOP: - textY = y + mes.borderSpacing.top + mes.borderInsets.top + TEXT_SPACING; + textY = y + mes.outerSpacing.top + mes.borderInsets.top + TEXT_SPACING; break; case ABOVE_BOTTOM: - textY = y + height - mes.borderSpacing.bottom - mes.borderInsets.bottom + textY = y + height - mes.outerSpacing.bottom - mes.borderInsets.bottom - TEXT_SPACING - (mes.textAscent + mes.textDescent); break; @@ -640,8 +643,8 @@ public class TitledBorder g.setFont(oldFont); g.setColor(oldColor); } - bp.paintExcept(g, textX - 2, textY, - mes.textWidth + 2, mes.textAscent + mes.textDescent); + bp.paintExcept(g, textX, textY, + mes.textWidth, mes.textAscent + mes.textDescent); } } @@ -998,39 +1001,63 @@ public class TitledBorder m.trimmedText = null; } - m.textAscent = fmet.getAscent(); - m.textDescent = fmet.getDescent(); if (m.trimmedText != null) - m.textWidth = fmet.stringWidth(m.trimmedText) + 3; + { + m.textAscent = fmet.getAscent(); + m.textDescent = fmet.getDescent() + fmet.getLeading(); + + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + LineMetrics lmet = m.font.getLineMetrics(m.trimmedText, 0, + m.trimmedText.length(), frc); + m.lineHeight = (int) lmet.getStrikethroughOffset(); + + // Fallback in case that LineMetrics is not available/working. + if (m.lineHeight == 0) + m.lineHeight = (int) (0.3333 * (double) m.textAscent); + m.textWidth = fmet.stringWidth(m.trimmedText) + 3; + } + else + { + m.textAscent = 0; + m.textDescent = 0; + } - m.edgeSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, EDGE_SPACING); - m.borderSpacing = new Insets(0, 0, 0, 0); + m.innerSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, + EDGE_SPACING); + m.outerSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, + EDGE_SPACING); switch (titlePosition) { case ABOVE_TOP: - m.borderSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; + m.outerSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; break; + case TOP: + m.outerSpacing.top += m.textDescent + m.lineHeight; + m.innerSpacing.top += m.textAscent - m.lineHeight; + break; + case BELOW_TOP: - m.edgeSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; + m.innerSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; break; case ABOVE_BOTTOM: - m.edgeSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; + m.innerSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; break; case BOTTOM: - m.edgeSpacing.bottom += Math.max(m.textAscent - m.borderInsets.bottom, 0); - m.borderSpacing.bottom += m.textDescent; + m.innerSpacing.bottom += Math.max(m.textAscent - m.lineHeight, 0); + m.outerSpacing.bottom += m.textDescent + m.lineHeight; break; case BELOW_BOTTOM: - m.borderSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; + m.outerSpacing.bottom += m.textAscent + m.textDescent; break; default: - m.borderSpacing.top += m.textAscent; + m.outerSpacing.top += m.textAscent; } return m; @@ -1053,7 +1080,7 @@ public class TitledBorder * which means that the font is to be retrieved from the current * LookAndFeel. In this case, this <code>font</code> field will * contain the result of the retrieval. Therefore, it is safe - * to assume that his <code>font</code> field will never have + * to assume that this <code>font</code> field will never have * a <code>null</code> value. */ Font font; @@ -1072,6 +1099,11 @@ public class TitledBorder */ int textDescent; + /** + * The number of pixels between the base line and the height where + * a strike-through would be drawn. + */ + int lineHeight; /** * The title text after removing leading and trailing white space @@ -1088,7 +1120,7 @@ public class TitledBorder /** - * The border that constitues the interior border + * The border that constitutes the interior border * underneath the title text. */ Border border; @@ -1097,8 +1129,7 @@ public class TitledBorder /** * The distance between the TitledBorder and the interior border. */ - Insets borderSpacing; - + Insets outerSpacing; /** * The width of the interior border, as returned by @@ -1111,7 +1142,7 @@ public class TitledBorder * The distance between the interior border and the nested * Component for which this TitledBorder is a border. */ - Insets edgeSpacing; + Insets innerSpacing; /** @@ -1130,10 +1161,10 @@ public class TitledBorder { if (i == null) i = new Insets(0, 0, 0, 0); - i.left = borderSpacing.left + borderInsets.left + edgeSpacing.left; - i.right = borderSpacing.right + borderInsets.right + edgeSpacing.right; - i.top = borderSpacing.top + borderInsets.top + edgeSpacing.top; - i.bottom = borderSpacing.bottom + borderInsets.bottom + edgeSpacing.bottom; + i.left = outerSpacing.left + borderInsets.left + innerSpacing.left; + i.right = outerSpacing.right + borderInsets.right + innerSpacing.right; + i.top = outerSpacing.top + borderInsets.top + innerSpacing.top; + i.bottom = outerSpacing.bottom + borderInsets.bottom + innerSpacing.bottom; return i; } @@ -1148,7 +1179,8 @@ public class TitledBorder Insets insets; insets = getContentInsets(null); - width = Math.max(insets.left + insets.right, textWidth + 2 * TEXT_INSET_H); + width = Math.max(insets.left + insets.right, textWidth + 2 + * TEXT_INSET_H); return new Dimension(width, insets.top + insets.bottom); } } diff --git a/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java index d55346aaf2c..efb527725fa 100644 --- a/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java +++ b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java @@ -1,5 +1,5 @@ /* AbstractColorChooserPanel.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -64,7 +64,8 @@ public abstract class AbstractColorChooserPanel extends JPanel */ public AbstractColorChooserPanel() { - } // AbstractColorChooserPanel() + // Nothing to do here. + } /** * This method returns the name displayed in the tab for this chooser panel. @@ -74,6 +75,36 @@ public abstract class AbstractColorChooserPanel extends JPanel public abstract String getDisplayName(); /** + * Returns the key code for the mnemonic for this panel. This method returns + * zero to indicate no mnemonic, subclasses can override this. + * + * @return <code>0</code>, to indicate no mnemonic key code. + * + * @see #getDisplayedMnemonicIndex() + * @since 1.4 + */ + public int getMnemonic() + { + return 0; + } + + /** + * Returns the index of the character in the display name that is the + * mnemonic. This method returns <code>-1</code> to indicate no mnemonic, + * subclasses can override. + * + * @return <code>-1</code>, to indicate no mnemonic. + * + * @see #getDisplayName() + * @see #getMnemonic() + * @since 1.4 + */ + public int getDisplayedMnemonicIndex() + { + return -1; + } + + /** * This method updates the chooser panel when the JColorChooser's color has * changed. */ diff --git a/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java index 77e319c70e4..923ea531ffb 100644 --- a/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java +++ b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java @@ -53,7 +53,8 @@ public class ColorChooserComponentFactory */ private ColorChooserComponentFactory() { - } // ColorChooserComponentFactory() + // Nothing to do here. + } /** * This method returns the three default chooser panels to be used in diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java index f28af4cac7c..ff3436808ec 100644 --- a/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java +++ b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java @@ -587,6 +587,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void addLayoutComponent(String name, Component comp) { + // Nothing to do here. } /** @@ -634,6 +635,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void removeLayoutComponent(Component comp) { + // Nothing to do here. } /** @@ -786,6 +788,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void updateChooser() { + // Nothing to do here yet. } /** diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index 3b9f4c807de..ee3f220f4d5 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/javax/swing/event/EventListenerList.java @@ -119,6 +119,7 @@ public class EventListenerList */ public EventListenerList() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/event/InternalFrameAdapter.java b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java index a2878e76e79..dfa88c3d4fd 100644 --- a/libjava/classpath/javax/swing/event/InternalFrameAdapter.java +++ b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java @@ -39,75 +39,88 @@ exception statement from your version. */ package javax.swing.event; /** - * InternalFrameAdapter + * InternalFrameAdapter. + * * @author Andrew Selkirk */ public abstract class InternalFrameAdapter implements InternalFrameListener { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * InternalFrameAdapter constructor - */ - public InternalFrameAdapter() { - } // InternalFrameAdapter() - - - //------------------------------------------------------------- - // Interface: InternalFrameListener --------------------------- - //------------------------------------------------------------- + /** + * InternalFrameAdapter constructor. + */ + public InternalFrameAdapter() + { + // Nothing to do here. + } - /** - * Internal frame activated - * @param event Internal frame event - */ - public void internalFrameActivated(InternalFrameEvent event) { - } // internalFrameActivated() - - /** - * Internal frame closed - * @param event Internal frame event - */ - public void internalFrameClosed(InternalFrameEvent event) { - } // internalFrameClosed() - - /** - * Internal frame closing - * @param event Internal frame event - */ - public void internalFrameClosing(InternalFrameEvent event) { - } // internalFrameClosing() - - /** - * Internal frame deactivated - * @param event Internal frame event - */ - public void internalFrameDeactivated(InternalFrameEvent event) { - } // internalFrameDeactivated() - - /** - * Internal frame deiconified - * @param event Internal frame event - */ - public void internalFrameDeiconified(InternalFrameEvent event) { - } // internalFrameDeiconified() - - /** - * Internal frame iconified - * @param event Internal frame event - */ - public void internalFrameIconified(InternalFrameEvent event) { - } // internalFrameIconified() - - /** - * Internal frame opened - * @param event Internal frame event - */ - public void internalFrameOpened(InternalFrameEvent event) { - } // internalFrameOpened() - - -} // InternalFrameAdapter + /** + * Internal frame activated. + * + * @param event internal frame event + */ + public void internalFrameActivated(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame closed. + * + * @param event internal frame event + */ + public void internalFrameClosed(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame closing. + * + * @param event internal frame event + */ + public void internalFrameClosing(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame deactivated. + * + * @param event internal frame event + */ + public void internalFrameDeactivated(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame deiconified. + * + * @param event internal frame event + */ + public void internalFrameDeiconified(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame iconified. + * + * @param event internal frame event + */ + public void internalFrameIconified(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame opened. + * + * @param event internal frame event + */ + public void internalFrameOpened(InternalFrameEvent event) + { + // Nothing to do here. + } + +} diff --git a/libjava/classpath/javax/swing/event/ListDataListener.java b/libjava/classpath/javax/swing/event/ListDataListener.java index 7ce17d86fa6..f42777d09d6 100644 --- a/libjava/classpath/javax/swing/event/ListDataListener.java +++ b/libjava/classpath/javax/swing/event/ListDataListener.java @@ -1,5 +1,5 @@ /* ListDataListener.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,33 +37,46 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventListener; +import javax.swing.ListModel; + /** - * ListDataListener public interface + * A <code>ListDataListener</code> can register with a {@link ListModel} and + * receive notification of updates to the model. + * * @author Andrew Selkirk * @author Ronald Veldema */ -public interface ListDataListener extends EventListener { - - /** - * Contents Changed - * @param event ListDataEvent Event - */ - void contentsChanged(ListDataEvent event); +public interface ListDataListener extends EventListener +{ - /** - * Interval Added - * @param event ListDataEvent Event - */ - void intervalAdded(ListDataEvent event); + /** + * Notifies the listener that the contents of the list have changed + * in some way. This method will be called if the change cannot be + * notified via the {@link #intervalAdded(ListDataEvent)} or the + * {@link #intervalRemoved(ListDataEvent)} methods. + * + * @param event the event. + */ + void contentsChanged(ListDataEvent event); - /** - * Interval Removed - * @param event ListDataEvent Event - */ - void intervalRemoved(ListDataEvent event); + /** + * Notifies the listener that one or more items have been added to the + * list. The <code>event</code> argument can supply the indices for the + * range of items added. + * + * @param event the event. + */ + void intervalAdded(ListDataEvent event); + /** + * Notifies the listener that one or more items have been removed from + * the list. The <code>event</code> argument can supply the indices for + * the range of items removed. + * + * @param event the event. + */ + void intervalRemoved(ListDataEvent event); -} // ListDataListener +} diff --git a/libjava/classpath/javax/swing/event/MouseInputListener.java b/libjava/classpath/javax/swing/event/MouseInputListener.java index 3c3ca2347fe..3d879b9e746 100644 --- a/libjava/classpath/javax/swing/event/MouseInputListener.java +++ b/libjava/classpath/javax/swing/event/MouseInputListener.java @@ -37,16 +37,17 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** - * MouseInputListener public interface + * MouseInputListener public interface. + * * @author Andrew Selkirk */ public interface MouseInputListener extends MouseListener, - MouseMotionListener { - -} // MouseInputListener - + MouseMotionListener +{ + // This interface only pulls together MouseListener and MouseMotionListener + // without adding any methods on its own. +} diff --git a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java index 408ca957e95..7e8ff0dc2e9 100644 --- a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java +++ b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java @@ -289,10 +289,9 @@ public final class SwingPropertyChangeSupport int index; PropertyChangeListener listener; - // Check Values if they are equal - if (event.getOldValue() == null && event.getNewValue() == null || - (event.getOldValue() != null && event.getNewValue() != null && - event.getOldValue().equals(event.getNewValue()))) + // if the old and new values are non-null and equal, don't notify listeners + if (event.getOldValue() != null && event.getNewValue() != null && + event.getOldValue().equals(event.getNewValue())) return; // Process Main Listener List diff --git a/libjava/classpath/javax/swing/event/TreeModelEvent.java b/libjava/classpath/javax/swing/event/TreeModelEvent.java index a217e3b4053..8fa28a7eadb 100644 --- a/libjava/classpath/javax/swing/event/TreeModelEvent.java +++ b/libjava/classpath/javax/swing/event/TreeModelEvent.java @@ -55,12 +55,12 @@ public class TreeModelEvent extends EventObject { /** * childIndices */ - protected int[] childIndices = new int[0]; + protected int[] childIndices = null; /** * children */ - protected Object[] children = new Object[0]; + protected Object[] children = null; /** * path @@ -164,7 +164,9 @@ public class TreeModelEvent extends EventObject { * @returns String representation */ public String toString() { - return null; // TODO + return getClass() + " [Source: " + getSource() + ", TreePath: " + getTreePath() + + ", Child Indicies: " + getChildIndices() + ", Children: " + getChildren() + + ", Path: " + getPath() +"]"; } // toString() diff --git a/libjava/classpath/javax/swing/filechooser/FileFilter.java b/libjava/classpath/javax/swing/filechooser/FileFilter.java index 42770d98186..ecfa54b5814 100644 --- a/libjava/classpath/javax/swing/filechooser/FileFilter.java +++ b/libjava/classpath/javax/swing/filechooser/FileFilter.java @@ -1,5 +1,5 @@ /* FileFilter.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,41 +40,46 @@ package javax.swing.filechooser; import java.io.File; +import javax.swing.JFileChooser; + /** - * FileFilter + * The base class for filters that control the visibility of files in the + * {@link JFileChooser} component. + * + * @see JFileChooser#addChoosableFileFilter(FileFilter) + * * @author Andrew Selkirk - * @version 1.0 */ -public abstract class FileFilter { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor FileFilter - */ - public FileFilter() { - // TODO - } // FileFilter() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * accept - * @param file TODO - * @returns boolean - */ - public abstract boolean accept(File file); - - /** - * getDescription - * @returns String - */ - public abstract String getDescription(); - - -} // FileFilter +public abstract class FileFilter +{ + + /** + * Default constructor. + */ + public FileFilter() + { + // Nothing to do here. + } + + /** + * Returns <code>true</code> if the specified file matches the filter, and + * <code>false</code> otherwise. + * + * @param file the file. + * + * @returns A boolean. + */ + public abstract boolean accept(File file); + + /** + * Returns a description of the files that will be selected by the filter + * (for example, "Java source files"). This description will usually be + * displayed on the {@link JFileChooser} component, often in a combo box that + * is used to select the appropriate filter (in cases where more than one + * filter is available). + * + * @returns A description of the filter. + */ + public abstract String getDescription(); + +} diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java index ffa41ca07de..f51b745c892 100644 --- a/libjava/classpath/javax/swing/filechooser/FileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java @@ -40,21 +40,27 @@ package javax.swing.filechooser; import java.io.File; import java.io.IOException; import java.util.ArrayList; + import javax.swing.Icon; +import javax.swing.JFileChooser; /** - * DOCUMENT ME! + * The base class providing a view of the file system for use by the + * {@link JFileChooser} component. */ public abstract class FileSystemView { + /** The instance returned by {@link #getFileSystemView()}. */ + private static FileSystemView defaultFileSystemView; + /** - * DOCUMENT ME! + * Creates a new file object with the given name in the specified directory. * - * @param dir DOCUMENT ME! - * @param filename DOCUMENT ME! + * @param dir the directory (<code>null</code> permitted). + * @param filename the file name. * - * @return DOCUMENT ME! + * @return A new file object. */ public File createFileObject(File dir, String filename) { @@ -62,11 +68,11 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Creates a new file object from the specified path. * - * @param path DOCUMENT ME! + * @param path the path. * - * @return DOCUMENT ME! + * @return A new file object. */ public File createFileObject(String path) { @@ -89,13 +95,16 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Creates a new folder with a unique name in the specified directory and + * returns a {@link File} object representing the new directory. * - * @param containingDir DOCUMENT ME! + * @param containingDir the directory to contain the new folder + * (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return A {@link File} object representing the new directory. * - * @throws IOException DOCUMENT ME! + * @throws IOException if an exception occurs while creating the new + * directory. */ public abstract File createNewFolder(File containingDir) throws IOException; @@ -115,9 +124,9 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the default directory. * - * @return DOCUMENT ME! + * @return The default directory. */ public File getDefaultDirectory() { @@ -125,12 +134,16 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns an array containing the files in the given directory. The + * <code>useFileHiding</code> controls whether or not hidden files are + * included in the result. * - * @param dir DOCUMENT ME! - * @param useFileHiding DOCUMENT ME! + * @param dir the directory (if <code>null</code> + * @param useFileHiding a flag that controls whether or not hidden files are + * included in the result (pass in <code>true</code> to + * exclude hidden files). * - * @return DOCUMENT ME! + * @return The files in the given directory (possibly <code>null</code>). */ public File[] getFiles(File dir, boolean useFileHiding) { @@ -143,31 +156,34 @@ public abstract class FileSystemView for (int i = 0; i < files.length; i++) if (! files[i].isHidden()) trim.add(files[i]); - File[] value = (File[]) trim.toArray(new File[0]); + File[] value = (File[]) trim.toArray(new File[trim.size()]); return value; } /** - * DOCUMENT ME! + * Returns a default {@link FileSystemView} appropriate for the platform. * - * @return DOCUMENT ME! + * @return A default {@link FileSystemView} appropriate for the platform. */ public static FileSystemView getFileSystemView() { - if (File.separator.equals("/")) - return new UnixFileSystemView(); - - // else if (File.Separator.equals("\")) - // return new Win32FileSystemView(); - // else - // return new GenericFileSystemView(); - return null; + if (defaultFileSystemView == null) + { + if (File.separator.equals("/")) + defaultFileSystemView = new UnixFileSystemView(); + // FIXME: need to implement additional views + // else if (File.Separator.equals("\")) + // return new Win32FileSystemView(); + // else + // return new GenericFileSystemView(); + } + return defaultFileSystemView; } /** - * DOCUMENT ME! + * Returns the home directory for the current user. * - * @return DOCUMENT ME! + * @return The home directory for the current user. */ public File getHomeDirectory() { @@ -175,11 +191,12 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the parent directory for the given file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The parent directory (or <code>null</code> if there is no parent + * directory). */ public File getParentDirectory(File f) { @@ -189,9 +206,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * Returns an array containing the file system roots. On Unix-like platforms, + * this array will contain just a single item ("/"), while other platforms + * may return multiple roots. + * <p> + * This method is implemented to return <code>null</code>, subclasses must + * override this method. + * + * @return An array containing the file system roots. */ public File[] getRoots() { @@ -200,11 +222,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the name of a file as it would be displayed by the underlying + * system. This implementation returns <code>null</code>, subclasses must + * override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemDisplayName(File f) { @@ -212,11 +236,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the icon that would be displayed for the given file by the + * underlying system. This implementation returns <code>null</code>, + * subclasses must override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public Icon getSystemIcon(File f) { @@ -224,11 +250,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the type description of a file that would be displayed by the + * underlying system. This implementation returns <code>null</code>, + * subclasses must override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemTypeDescription(File f) { @@ -248,11 +276,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory represents a disk + * drive, and <code>false</code> otherwise. This default implementation + * always returns <code>false</code>. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>false</code>. */ public boolean isDrive(File dir) { @@ -260,11 +290,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if <code>f</code> is a file or directory, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return <code>true</code> if <code>f</code> is a file or directory, and + * <code>false</code> otherwise. */ public boolean isFileSystem(File f) { @@ -272,11 +304,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory is a file system root, + * and <code>false</code> otherwise. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>true</code> if the given directory is a file system root, + * and <code>false</code> otherwise. */ public boolean isFileSystemRoot(File dir) { @@ -291,11 +325,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory represents a floppy + * drive, and <code>false</code> otherwise. This default implementation + * always returns <code>false</code>. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>false</code>. */ public boolean isFloppyDrive(File dir) { @@ -303,11 +339,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given file is hidden, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>true</code> if the given file is hidden, and + * <code>false</code> otherwise. */ public boolean isHiddenFile(File f) { @@ -315,12 +353,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if <code>folder</code> is the parent of + * <code>file</code>, and <code>false</code> otherwise. * - * @param folder DOCUMENT ME! - * @param file DOCUMENT ME! + * @param folder the folder (<code>null</code> not permitted). + * @param file the file (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return <code>true</code> if <code>folder</code> is the parent of + * <code>file</code>, and <code>false</code> otherwise. */ public boolean isParent(File folder, File file) { @@ -344,11 +384,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. Here, all directories are considered + * traversable, and files are considered non-traversable. * - * @param f DOCUMENT ME! + * @param f the file or directory (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. */ public Boolean isTraversable(File f) { @@ -356,6 +399,6 @@ public abstract class FileSystemView // traversable. (No files are listed when you traverse the directory) // My best guess is that as long as it's a directory, the file is // traversable. - return new Boolean(f.isDirectory()); + return Boolean.valueOf(f.isDirectory()); } } diff --git a/libjava/classpath/javax/swing/filechooser/FileView.java b/libjava/classpath/javax/swing/filechooser/FileView.java index c431fd46127..ea1989f9353 100644 --- a/libjava/classpath/javax/swing/filechooser/FileView.java +++ b/libjava/classpath/javax/swing/filechooser/FileView.java @@ -1,5 +1,5 @@ /* FileView.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,72 +43,86 @@ import java.io.File; import javax.swing.Icon; /** - * FileView - * @author Andrew Selkirk - * @version 1.0 + * An abstract class that provides presentation information about files and + * directories. . + * + * @author Andrew Selkirk */ -public abstract class FileView { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor FileView - */ - public FileView() { - // TODO - } // FileView() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getName - * @param file TODO - * @returns String - */ - public String getName(File file) { - return null; // TODO - } // getName() - - /** - * getDescription - * @param value0 TODO - * @returns String - */ - public String getDescription(File value0) { - return null; // TODO - } // getDescription() - - /** - * getTypeDescription - * @param value0 TODO - * @returns String - */ - public String getTypeDescription(File value0) { - return null; // TODO - } // getTypeDescription() - - /** - * getIcon - * @param value0 TODO - * @returns Icon - */ - public Icon getIcon(File value0) { - return null; // TODO - } // getIcon() - - /** - * isTraversable - * @param value0 TODO - * @returns Boolean - */ - public Boolean isTraversable(File value0) { - return null; // TODO - } // isTraversable() - - -} // FileView +public abstract class FileView +{ + + /** + * Creates a new <code>FileView</code> instance. + */ + public FileView() + { + // Nothing to do here. + } + + /** + * Returns the name for the specified file. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getName(File file) + { + return null; + } + + /** + * Returns a description for the specified file. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getDescription(File file) + { + return null; + } + + /** + * Returns a description for the type of the specified file. This method + * always returns <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getTypeDescription(File file) + { + return null; + } + + /** + * Returns an {@link Icon} to represent the specified file. This method + * always returns <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public Icon getIcon(File file) + { + return null; + } + + /** + * Returns {@link Boolean#TRUE} if the given directory is traversable, and + * {@link Boolean#FALSE} if it is not. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param directory the directory. + * + * @returns Always <code>null</code>. + */ + public Boolean isTraversable(File directory) + { + return null; + } + +} diff --git a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java index f2360ec52f8..c2f65965e09 100644 --- a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java @@ -43,21 +43,31 @@ import javax.swing.Icon; /** - * DOCUMENT ME! + * A concrete implementation of {@link FileSystemView} that is appropriate for + * Unix-like systems. + * + * @see FileSystemView#getFileSystemView() */ class UnixFileSystemView extends FileSystemView { - /** DOCUMENT ME! */ + /** The default name for new folders. */ private static final String NEW_FOLDER_NAME = "NewFolder"; /** - * DOCUMENT ME! + * Creates a new folder with a unique name in the specified directory and + * returns a {@link File} object representing the new directory. The name + * of the new folder is <code>NewFolder</code> or, if a directory or file + * with that name already exists, <code>NewFolder.n</code> where + * <code>n</code> is the lowest integer greater than zero that results in + * a unique directory name. * - * @param containingDir DOCUMENT ME! + * @param containingDir the directory to contain the new folder + * (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return A {@link File} object representing the new directory. * - * @throws IOException DOCUMENT ME! + * @throws IOException if an exception occurs while creating the new + * directory. */ public File createNewFolder(File containingDir) throws IOException { @@ -82,9 +92,9 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns an array containing the file system root. * - * @return DOCUMENT ME! + * @return An array containing the file system root. */ public File[] getRoots() { @@ -92,11 +102,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the name of a file as it would be displayed by the underlying + * system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemDisplayName(File f) { @@ -105,11 +116,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the icon that would be displayed for the given file by the + * underlying system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public Icon getSystemIcon(File f) { @@ -118,11 +130,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the description of a file that would be displayed by the + * underlying system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemTypeDescription(File f) { diff --git a/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java index f6af0880ddb..07292fe24a4 100644 --- a/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java @@ -50,9 +50,7 @@ import javax.swing.ActionMap; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ActionMapUIResource - extends ActionMap - implements UIResource +public class ActionMapUIResource extends ActionMap implements UIResource { /** * Constructs a new ActionMapUIResource. diff --git a/libjava/classpath/javax/swing/plaf/BorderUIResource.java b/libjava/classpath/javax/swing/plaf/BorderUIResource.java index 4402bbb48b6..317cb09ac57 100644 --- a/libjava/classpath/javax/swing/plaf/BorderUIResource.java +++ b/libjava/classpath/javax/swing/plaf/BorderUIResource.java @@ -71,9 +71,7 @@ import javax.swing.border.TitledBorder; * @author Brian Jones (cbj@gnu.org) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BorderUIResource - extends Object - implements Border, UIResource, Serializable +public class BorderUIResource implements Border, UIResource, Serializable { /** * Verified using the <code>serialver</code> tool diff --git a/libjava/classpath/javax/swing/plaf/ButtonUI.java b/libjava/classpath/javax/swing/plaf/ButtonUI.java index 197299e0c95..6910e42989d 100644 --- a/libjava/classpath/javax/swing/plaf/ButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/ButtonUI.java @@ -46,7 +46,7 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ButtonUI - extends ComponentUI +public abstract class ButtonUI extends ComponentUI { + // This abstract class does not define any methods of its own. } diff --git a/libjava/classpath/javax/swing/plaf/ColorChooserUI.java b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java index 68ffd916d21..16091416a32 100644 --- a/libjava/classpath/javax/swing/plaf/ColorChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java @@ -46,8 +46,7 @@ package javax.swing.plaf; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ColorChooserUI - extends ComponentUI +public abstract class ColorChooserUI extends ComponentUI { /** * Constructs a ColorChooserUI. diff --git a/libjava/classpath/javax/swing/plaf/ColorUIResource.java b/libjava/classpath/javax/swing/plaf/ColorUIResource.java index 33b1676e0fd..36e10f2d186 100644 --- a/libjava/classpath/javax/swing/plaf/ColorUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ColorUIResource.java @@ -50,9 +50,7 @@ import java.awt.Color; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ColorUIResource - extends Color - implements UIResource +public class ColorUIResource extends Color implements UIResource { /** * Constructs a <code>ColorUIResource</code> using the specified diff --git a/libjava/classpath/javax/swing/plaf/ComboBoxUI.java b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java index 9498a48153a..3e81ed75a6b 100644 --- a/libjava/classpath/javax/swing/plaf/ComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java @@ -48,14 +48,14 @@ import javax.swing.JComboBox; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ComboBoxUI - extends ComponentUI +public abstract class ComboBoxUI extends ComponentUI { /** * Constructs a new <code>ComboBoxUI</code>. */ public ComboBoxUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java index e1418710fb2..cfc43e4fc78 100644 --- a/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java @@ -52,8 +52,7 @@ import javax.swing.JComponent; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ComponentInputMapUIResource - extends ComponentInputMap +public class ComponentInputMapUIResource extends ComponentInputMap implements UIResource { /** diff --git a/libjava/classpath/javax/swing/plaf/ComponentUI.java b/libjava/classpath/javax/swing/plaf/ComponentUI.java index 0e7680542f7..6a736f258a2 100644 --- a/libjava/classpath/javax/swing/plaf/ComponentUI.java +++ b/libjava/classpath/javax/swing/plaf/ComponentUI.java @@ -38,8 +38,10 @@ exception statement from your version. */ package javax.swing.plaf; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Rectangle; import javax.accessibility.Accessible; import javax.swing.JComponent; @@ -86,6 +88,7 @@ public abstract class ComponentUI */ public ComponentUI() { + // Nothing to do here. } @@ -157,6 +160,8 @@ public abstract class ComponentUI */ public void paint(Graphics g, JComponent c) { + // Nothing is done here. This method is meant to be overridden by + // subclasses. } @@ -181,13 +186,14 @@ public abstract class ComponentUI { if (c.isOpaque()) { + Color oldColor = g.getColor(); g.setColor(c.getBackground()); g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(oldColor); } paint(g, c); } - - + /** * Determines the preferred size of a component. The default * implementation returns <code>null</code>, which means that diff --git a/libjava/classpath/javax/swing/plaf/DesktopIconUI.java b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java index 2e44088cadd..676233ec2cf 100644 --- a/libjava/classpath/javax/swing/plaf/DesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java @@ -44,13 +44,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class DesktopIconUI - extends ComponentUI +public abstract class DesktopIconUI extends ComponentUI { /** * Constructs a new <code>DesktopIconUI</code>. */ public DesktopIconUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java index de553eaf4de..3d4cfc830f5 100644 --- a/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java @@ -46,14 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class DesktopPaneUI - extends ComponentUI +public abstract class DesktopPaneUI extends ComponentUI { /** * Constructs a new <code>DesktopPaneUI</code>. */ public DesktopPaneUI() { + // Nothing to do here. } } - diff --git a/libjava/classpath/javax/swing/plaf/DimensionUIResource.java b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java index 63c6838c4d1..618c220369e 100644 --- a/libjava/classpath/javax/swing/plaf/DimensionUIResource.java +++ b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java @@ -51,9 +51,7 @@ import java.awt.Dimension; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class DimensionUIResource - extends Dimension - implements UIResource +public class DimensionUIResource extends Dimension implements UIResource { /** * Constructs a new DimensionUIResource, given its width and height. diff --git a/libjava/classpath/javax/swing/plaf/FileChooserUI.java b/libjava/classpath/javax/swing/plaf/FileChooserUI.java index 8b661e399a5..e9be8f2ba49 100644 --- a/libjava/classpath/javax/swing/plaf/FileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/FileChooserUI.java @@ -53,14 +53,14 @@ import javax.swing.filechooser.FileView; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class FileChooserUI - extends ComponentUI +public abstract class FileChooserUI extends ComponentUI { /** * Constructs a new <code>FileChooserUI</code>. */ public FileChooserUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/FontUIResource.java b/libjava/classpath/javax/swing/plaf/FontUIResource.java index 1c1731048e8..c54f987fdb7 100644 --- a/libjava/classpath/javax/swing/plaf/FontUIResource.java +++ b/libjava/classpath/javax/swing/plaf/FontUIResource.java @@ -50,9 +50,7 @@ import java.awt.Font; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class FontUIResource - extends Font - implements UIResource +public class FontUIResource extends Font implements UIResource { /** * Constructs a new <code>FontUIResource</code> given diff --git a/libjava/classpath/javax/swing/plaf/IconUIResource.java b/libjava/classpath/javax/swing/plaf/IconUIResource.java index 1b09ed31f39..659c8e7bab7 100644 --- a/libjava/classpath/javax/swing/plaf/IconUIResource.java +++ b/libjava/classpath/javax/swing/plaf/IconUIResource.java @@ -53,8 +53,7 @@ import javax.swing.Icon; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class IconUIResource - implements Icon, UIResource, Serializable +public class IconUIResource implements Icon, UIResource, Serializable { /** * Verified using the <code>serialver</code> tool of Sun JDK 1.4.1_01 diff --git a/libjava/classpath/javax/swing/plaf/InputMapUIResource.java b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java index ae032e51fa8..0c5f6f9e97e 100644 --- a/libjava/classpath/javax/swing/plaf/InputMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java @@ -49,15 +49,13 @@ import javax.swing.InputMap; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class InputMapUIResource - extends InputMap - implements UIResource +public class InputMapUIResource extends InputMap implements UIResource { /** * Constructs a new <code>InputMapUIResource</code>. */ public InputMapUIResource() { + // Nothing to do here. } } - diff --git a/libjava/classpath/javax/swing/plaf/InsetsUIResource.java b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java index 755d8add1b2..d64feb44c1a 100644 --- a/libjava/classpath/javax/swing/plaf/InsetsUIResource.java +++ b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java @@ -50,8 +50,7 @@ import java.io.Serializable; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class InsetsUIResource - extends Insets +public class InsetsUIResource extends Insets implements Cloneable, UIResource, Serializable { /** diff --git a/libjava/classpath/javax/swing/plaf/InternalFrameUI.java b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java index fd1e3374c13..0b2f77caa15 100644 --- a/libjava/classpath/javax/swing/plaf/InternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class InternalFrameUI - extends ComponentUI +public abstract class InternalFrameUI extends ComponentUI { /** * Constructs a new <code>InternalFrameUI</code>. */ public InternalFrameUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/LabelUI.java b/libjava/classpath/javax/swing/plaf/LabelUI.java index 8fc1d711b0e..f4b74d59e81 100644 --- a/libjava/classpath/javax/swing/plaf/LabelUI.java +++ b/libjava/classpath/javax/swing/plaf/LabelUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class LabelUI - extends ComponentUI +public abstract class LabelUI extends ComponentUI { /** * Constructs a new <code>LabelUI</code>. */ public LabelUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ListUI.java b/libjava/classpath/javax/swing/plaf/ListUI.java index 66d5cf588bb..bdfe4b3078f 100644 --- a/libjava/classpath/javax/swing/plaf/ListUI.java +++ b/libjava/classpath/javax/swing/plaf/ListUI.java @@ -49,14 +49,14 @@ import javax.swing.JList; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ListUI - extends ComponentUI +public abstract class ListUI extends ComponentUI { /** * Constructs a new <code>ListUI</code>. */ public ListUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/MenuBarUI.java b/libjava/classpath/javax/swing/plaf/MenuBarUI.java index 8835571ac75..2c82adfa0a0 100644 --- a/libjava/classpath/javax/swing/plaf/MenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/MenuBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class MenuBarUI - extends ComponentUI +public abstract class MenuBarUI extends ComponentUI { /** * Constructs a new <code>MenuBarUI</code>. */ public MenuBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/MenuItemUI.java b/libjava/classpath/javax/swing/plaf/MenuItemUI.java index 31d73194a18..83ad52fb103 100644 --- a/libjava/classpath/javax/swing/plaf/MenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/MenuItemUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class MenuItemUI - extends ButtonUI +public abstract class MenuItemUI extends ButtonUI { /** * Constructs a new <code>MenuItemUI</code>. */ public MenuItemUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/PanelUI.java b/libjava/classpath/javax/swing/plaf/PanelUI.java index b1171b80d30..12a6f52cfa8 100644 --- a/libjava/classpath/javax/swing/plaf/PanelUI.java +++ b/libjava/classpath/javax/swing/plaf/PanelUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class PanelUI - extends ComponentUI +public abstract class PanelUI extends ComponentUI { /** * Constructs a new <code>PanelUI</code>. */ public PanelUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/PopupMenuUI.java b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java index c70ad2a4e9b..de351f2ef79 100644 --- a/libjava/classpath/javax/swing/plaf/PopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java @@ -53,14 +53,14 @@ import javax.swing.PopupFactory; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class PopupMenuUI - extends ComponentUI +public abstract class PopupMenuUI extends ComponentUI { /** * Constructs a new <code>PopupMenuUI</code>. */ public PopupMenuUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ProgressBarUI.java b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java index 79c1b95a34a..013b8c5c2e7 100644 --- a/libjava/classpath/javax/swing/plaf/ProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ProgressBarUI - extends ComponentUI +public abstract class ProgressBarUI extends ComponentUI { /** * Constructs a new <code>ProgressBarUI</code>. */ public ProgressBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/RootPaneUI.java b/libjava/classpath/javax/swing/plaf/RootPaneUI.java index ff7d0a6e78a..9637c9cf218 100644 --- a/libjava/classpath/javax/swing/plaf/RootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/RootPaneUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class RootPaneUI - extends ComponentUI +public abstract class RootPaneUI extends ComponentUI { /** * Constructs a new <code>RootPaneUI</code>. */ public RootPaneUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ScrollBarUI.java b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java index 3cad3932720..51b4bf2d853 100644 --- a/libjava/classpath/javax/swing/plaf/ScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ScrollBarUI - extends ComponentUI +public abstract class ScrollBarUI extends ComponentUI { /** * Constructs a new <code>ScrollBarUI</code>. */ public ScrollBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java index 14d2ac61ef2..8b37fed2246 100644 --- a/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ScrollPaneUI - extends ComponentUI +public abstract class ScrollPaneUI extends ComponentUI { /** * Constructs a new <code>ScrollPaneUI</code>. */ public ScrollPaneUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SeparatorUI.java b/libjava/classpath/javax/swing/plaf/SeparatorUI.java index 6855bd0357e..8a9f8cf4d49 100644 --- a/libjava/classpath/javax/swing/plaf/SeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/SeparatorUI.java @@ -54,5 +54,6 @@ public abstract class SeparatorUI */ public SeparatorUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SliderUI.java b/libjava/classpath/javax/swing/plaf/SliderUI.java index 775f19620a8..570e962aa3e 100644 --- a/libjava/classpath/javax/swing/plaf/SliderUI.java +++ b/libjava/classpath/javax/swing/plaf/SliderUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SliderUI - extends ComponentUI +public abstract class SliderUI extends ComponentUI { /** * Constructs a new <code>SliderUI</code>. */ public SliderUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SpinnerUI.java b/libjava/classpath/javax/swing/plaf/SpinnerUI.java index fb4a3b13a93..ca29ddb8e4f 100644 --- a/libjava/classpath/javax/swing/plaf/SpinnerUI.java +++ b/libjava/classpath/javax/swing/plaf/SpinnerUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SpinnerUI - extends ComponentUI +public abstract class SpinnerUI extends ComponentUI { /** * Constructs a new <code>SpinnerUI</code>. */ public SpinnerUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SplitPaneUI.java b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java index ea9af2b1716..59ededf5847 100644 --- a/libjava/classpath/javax/swing/plaf/SplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java @@ -51,14 +51,14 @@ import javax.swing.JSplitPane; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SplitPaneUI - extends ComponentUI +public abstract class SplitPaneUI extends ComponentUI { /** * Constructs a new <code>SplitPaneUI</code>. */ public SplitPaneUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java index 6ab823b50a8..01a7720148f 100644 --- a/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java @@ -51,14 +51,14 @@ import javax.swing.JTabbedPane; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TabbedPaneUI - extends ComponentUI +public abstract class TabbedPaneUI extends ComponentUI { /** * Constructs a new <code>TabbedPaneUI</code>. */ public TabbedPaneUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/TableHeaderUI.java b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java index f23ca74d7ed..34ac0e0fc55 100644 --- a/libjava/classpath/javax/swing/plaf/TableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TableHeaderUI - extends ComponentUI +public abstract class TableHeaderUI extends ComponentUI { /** * Constructs a new <code>TableHeaderUI</code>. */ public TableHeaderUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TableUI.java b/libjava/classpath/javax/swing/plaf/TableUI.java index e56bcd13160..a8c6bf909d9 100644 --- a/libjava/classpath/javax/swing/plaf/TableUI.java +++ b/libjava/classpath/javax/swing/plaf/TableUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TableUI - extends ComponentUI +public abstract class TableUI extends ComponentUI { /** * Constructs a new <code>TableUI</code>. */ public TableUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TextUI.java b/libjava/classpath/javax/swing/plaf/TextUI.java index dcabdfcdbf3..9f2737cc26f 100644 --- a/libjava/classpath/javax/swing/plaf/TextUI.java +++ b/libjava/classpath/javax/swing/plaf/TextUI.java @@ -57,14 +57,14 @@ import javax.swing.text.View; * @author Ronald Veldema (rveldema@cs.vu.nl) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TextUI - extends ComponentUI +public abstract class TextUI extends ComponentUI { /** * Constructs a new <code>TextUI</code>. */ public TextUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ToolBarUI.java b/libjava/classpath/javax/swing/plaf/ToolBarUI.java index 730cf4887db..9a26e7b59d5 100644 --- a/libjava/classpath/javax/swing/plaf/ToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ToolBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ToolBarUI - extends ComponentUI +public abstract class ToolBarUI extends ComponentUI { /** * Constructs a new <code>ToolBarUI</code>. */ public ToolBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ToolTipUI.java b/libjava/classpath/javax/swing/plaf/ToolTipUI.java index 4383d0edd11..ae2d465e51c 100644 --- a/libjava/classpath/javax/swing/plaf/ToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/ToolTipUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ToolTipUI - extends ComponentUI +public abstract class ToolTipUI extends ComponentUI { /** * Constructs a new <code>ToolTipUI</code>. */ public ToolTipUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TreeUI.java b/libjava/classpath/javax/swing/plaf/TreeUI.java index e32952de70f..308ec63e2a8 100644 --- a/libjava/classpath/javax/swing/plaf/TreeUI.java +++ b/libjava/classpath/javax/swing/plaf/TreeUI.java @@ -51,14 +51,14 @@ import javax.swing.tree.TreePath; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TreeUI - extends ComponentUI +public abstract class TreeUI extends ComponentUI { /** * Constructs a new <code>TreeUI</code>. */ public TreeUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/UIResource.java b/libjava/classpath/javax/swing/plaf/UIResource.java index 59edf566605..fe398fe5474 100644 --- a/libjava/classpath/javax/swing/plaf/UIResource.java +++ b/libjava/classpath/javax/swing/plaf/UIResource.java @@ -50,6 +50,10 @@ package javax.swing.plaf; * they are initialized or set to <code>null</code>. * * @author Brian Jones + * * @see ComponentUI */ -public interface UIResource { } +public interface UIResource +{ + // This is a marker interface and declares no methods. +} diff --git a/libjava/classpath/javax/swing/plaf/ViewportUI.java b/libjava/classpath/javax/swing/plaf/ViewportUI.java index 087938f1ed2..db514de1a4e 100644 --- a/libjava/classpath/javax/swing/plaf/ViewportUI.java +++ b/libjava/classpath/javax/swing/plaf/ViewportUI.java @@ -48,13 +48,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ViewportUI - extends ComponentUI +public abstract class ViewportUI extends ComponentUI { /** * Constructs a new <code>ViewportUI</code>. */ public ViewportUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java index 836ef223494..69d4415371f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java @@ -39,42 +39,30 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Polygon; import java.awt.Rectangle; +import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.SwingConstants; -import javax.swing.border.Border; /** - * This class draws simple arrow buttons for the Basic Look and Feel. + * A button that displays an arrow (triangle) that points {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}. This button is used by + * the {@link BasicComboBoxUI} class. + * + * @see BasicComboBoxUI#createArrowButton */ public class BasicArrowButton extends JButton implements SwingConstants { - /** The default size of the Arrow buttons. */ - private static int defaultSize = 12; - /** The Polygon that points up. */ - private static Polygon upIcon = new Polygon(new int[] { 0, 5, 9 }, - new int[] { 7, 2, 7 }, 3); - - /** The Polygon that points down. */ - private static Polygon downIcon = new Polygon(new int[] { 1, 5, 9 }, - new int[] { 3, 7, 3 }, 3); - - /** The Polygon that points left. */ - private static Polygon leftIcon = new Polygon(new int[] { 7, 3, 7 }, - new int[] { 1, 5, 9 }, 3); - - /** The Polygon that points right. */ - private static Polygon rightIcon = new Polygon(new int[] { 3, 7, 3 }, - new int[] { 1, 5, 9 }, 3); - - /** The direction to point in. */ + /** + * The direction that the arrow points. + * + * @see #getDirection() + */ protected int direction; /** @@ -89,7 +77,7 @@ public class BasicArrowButton extends JButton implements SwingConstants * edges of the button. * This is package-private to avoid an accessor method. */ - transient Color darkShadow = Color.DARK_GRAY; + transient Color darkShadow = new Color(102, 102, 102); /** * The top and left edges of the button. @@ -97,51 +85,15 @@ public class BasicArrowButton extends JButton implements SwingConstants */ transient Color highlight = Color.WHITE; - /** The border around the ArrowButton. */ - private transient Border buttonBorder = new Border() - { - public Insets getBorderInsets(Component c) - { - return new Insets(2, 2, 2, 2); - } - - public boolean isBorderOpaque() - { - return true; - } - - public void paintBorder(Component c, Graphics g, int x, int y, int w, - int h) - { - Color saved = g.getColor(); - g.setColor(highlight); - - g.drawLine(x + 1, y + 1, x + w - 1, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + h - 1); - - g.setColor(shadow); - - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - - g.drawLine(x, y + h, x + w, y + h); - g.drawLine(x + w, y, x + w, y + h); - - g.setColor(saved); - } - }; - /** - * Creates a new BasicArrowButton object. + * Creates a new <code>BasicArrowButton</code> object. * - * @param direction The direction the arrow points in. + * @param direction The direction the arrow points in (one of: + * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). */ public BasicArrowButton(int direction) { super(); - setBorder(buttonBorder); setDirection(direction); } @@ -149,7 +101,8 @@ public class BasicArrowButton extends JButton implements SwingConstants * Creates a new BasicArrowButton object with the given colors and * direction. * - * @param direction The direction to point in. + * @param direction The direction to point in (one of: + * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). * @param background The background color. * @param shadow The shadow color. * @param darkShadow The dark shadow color. @@ -166,9 +119,10 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method returns whether the focus can traverse to this component. + * Returns whether the focus can traverse to this component. This method + * always returns <code>false</code>. * - * @return Whether the focus can traverse to this component. + * @return <code>false</code>. */ public boolean isFocusTraversable() { @@ -176,7 +130,8 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method returns the direction of the arrow. + * Returns the direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). * * @return The direction of the arrow. */ @@ -186,9 +141,10 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method changes the direction of the arrow. + * Sets the direction of the arrow. * - * @param dir The new direction of the arrow. + * @param dir The new direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). */ public void setDirection(int dir) { @@ -196,7 +152,7 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method paints the arrow button. The painting is delegated to the + * Paints the arrow button. The painting is delegated to the * paintTriangle method. * * @param g The Graphics object to paint with. @@ -204,147 +160,257 @@ public class BasicArrowButton extends JButton implements SwingConstants public void paint(Graphics g) { super.paint(g); - Insets insets = getInsets(); Rectangle bounds = getBounds(); - int x = insets.left - + (bounds.width - insets.left - insets.right - defaultSize) / 2; - int y = insets.top - + (bounds.height - insets.left - insets.right - defaultSize) / 2; - paintTriangle(g, x, y, defaultSize, direction, isEnabled()); + int size = bounds.height / 4; + int x = (bounds.width - size) / 2; + int y = (bounds.height - size) / 2; + ButtonModel m = getModel(); + if (m.isArmed()) + { + x++; + y++; + } + paintTriangle(g, x, y, size, direction, isEnabled()); } + /** The preferred size for the button. */ + private static final Dimension PREFERRED_SIZE = new Dimension(16, 16); + + /** The minimum size for the button. */ + private static final Dimension MINIMUM_SIZE = new Dimension(5, 5); + + /** The maximum size for the button. */ + private static final Dimension MAXIMUM_SIZE + = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + /** - * This method returns the preferred size of the arrow button. + * Returns the preferred size of the arrow button. * - * @return The preferred size. + * @return The preferred size (always 16 x 16). */ public Dimension getPreferredSize() { - Insets insets = getInsets(); - int w = defaultSize + insets.left + insets.right; - int h = defaultSize + insets.top + insets.bottom; - - return new Dimension(w, h); + return PREFERRED_SIZE; } /** - * This method returns the minimum size of the arrow button. + * Returns the minimum size of the arrow button. * - * @return The minimum size. + * @return The minimum size (always 5 x 5). */ public Dimension getMinimumSize() { - return getPreferredSize(); + return MINIMUM_SIZE; } /** - * This method returns the maximum size of the arrow button. + * Returns the maximum size of the arrow button. * * @return The maximum size. */ public Dimension getMaximumSize() { - return getPreferredSize(); + return MAXIMUM_SIZE; } /** - * The method paints a triangle with the given size and direction at the - * given x and y coordinates. + * Paints a triangle with the given size, location and direction. It is + * difficult to explain the rationale behind the positioning of the triangle + * relative to the given (x, y) position - by trial and error we seem to + * match the behaviour of the reference implementation (which is missing a + * specification for this method). * - * @param g The Graphics object to paint with. - * @param x The x coordinate to paint at. - * @param y The y coordinate to paint at. - * @param size The size of the icon. - * @param direction The direction of the icon. - * @param isEnabled Whether it is enabled. + * @param g the graphics device. + * @param x the x-coordinate for the triangle's location. + * @param y the y-coordinate for the triangle's location. + * @param size the arrow size (depth). + * @param direction the direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. */ public void paintTriangle(Graphics g, int x, int y, int size, int direction, boolean isEnabled) { - Polygon arrow = null; - switch (direction) - { - case NORTH: - arrow = upIcon; - break; - case SOUTH: - arrow = downIcon; - break; - case EAST: - case RIGHT: - arrow = rightIcon; - break; - case WEST: - case LEFT: - arrow = leftIcon; - break; - } - - int[] xPoints = arrow.xpoints; - int[] yPoints = arrow.ypoints; - int x1; - int y1; - int x2; - int y2; - x1 = y1 = x2 = y2 = 0; - - if (size != defaultSize) - { - float scale = size * 1f / defaultSize; - for (int i = 0; i < 3; i++) - { - xPoints[i] *= scale; - yPoints[i] *= scale; - } - } - g.translate(x, y); - + Color savedColor = g.getColor(); switch (direction) { case NORTH: - x1 = xPoints[0] + 2; - y1 = yPoints[0]; - y2 = y1; - x2 = xPoints[2] - 1; - break; + paintTriangleNorth(g, x, y, size, isEnabled); + break; case SOUTH: - x1 = xPoints[1]; - y1 = yPoints[1] + 1; - x2 = xPoints[2] - 1; - y2 = yPoints[2]; - break; + paintTriangleSouth(g, x, y, size, isEnabled); + break; case LEFT: case WEST: - x1 = xPoints[0] + 1; - y1 = yPoints[0] + 1; - x2 = x1; - y2 = yPoints[2] + 1; - break; + paintTriangleWest(g, x, y, size, isEnabled); + break; case RIGHT: case EAST: - x1 = xPoints[2]; - y1 = yPoints[2] + 1; - x2 = xPoints[1] - 1; - y2 = yPoints[1] + 1; - break; + paintTriangleEast(g, x, y, size, isEnabled); + break; } - Color saved = g.getColor(); - + g.setColor(savedColor); + } + + /** + * Paints an upward-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleNorth(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 2) / 2; + int tipY = y; + int baseX1 = tipX - (size - 1); + int baseX2 = tipX + (size - 1); + int baseY = y + (size - 1); + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX1, baseY); + triangle.addPoint(baseX2, baseY); if (isEnabled) - { - g.setColor(Color.DARK_GRAY); - - if (arrow != null) - g.fillPolygon(xPoints, yPoints, 3); - } + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } else - { - g.setColor(Color.GRAY); - g.fillPolygon(xPoints, yPoints, 3); - g.setColor(Color.WHITE); - g.drawLine(x1, y1, x2, y2); - } - g.setColor(saved); - g.translate(-x, -y); + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX1 + 1, baseY + 1, baseX2 + 1, baseY + 1); + } + } + + /** + * Paints an downward-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleSouth(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 2) / 2; + int tipY = y + (size - 1); + int baseX1 = tipX - (size - 1); + int baseX2 = tipX + (size - 1); + int baseY = y; + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX1, baseY); + triangle.addPoint(baseX2, baseY); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(tipX + 1, tipY, baseX2, baseY + 1); + g.drawLine(tipX + 1, tipY + 1, baseX2 + 1, baseY + 1); + } + } + + /** + * Paints a right-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleEast(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 1); + int tipY = y + (size - 2) / 2; + int baseX = x; + int baseY1 = tipY - (size - 1); + int baseY2 = tipY + (size - 1); + + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX, baseY1); + triangle.addPoint(baseX, baseY2); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX + 1, baseY2, tipX, tipY + 1); + g.drawLine(baseX + 1, baseY2 + 1, tipX + 1, tipY + 1); + } + } + + /** + * Paints a left-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleWest(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x; + int tipY = y + (size - 2) / 2; + int baseX = x + (size - 1); + int baseY1 = tipY - (size - 1); + int baseY2 = tipY + (size - 1); + + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX, baseY1); + triangle.addPoint(baseX, baseY2); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX + 1, baseY1 + 1, baseX + 1, baseY2 + 1); + } } + } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java index e7d6e433877..cec7bec8501 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java @@ -806,9 +806,9 @@ public class BasicBorders */ public MarginBorder() { + // Nothing to do here. } - /** * Measures the width of this border. * @@ -1313,33 +1313,32 @@ public class BasicBorders * * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class SplitPaneBorder - implements Border, UIResource + public static class SplitPaneBorder implements Border, UIResource { /** * Indicates that the top edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_TOP = 1; /** * Indicates that the left edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_LEFT = 2; /** * Indicates that the bottom edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_BOTTOM = 4; /** * Indicates that the right edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_RIGHT = 8; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java index 5349f524049..1fca694519f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java @@ -55,9 +55,8 @@ import javax.swing.JComponent; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -public class BasicButtonListener - implements MouseListener, MouseMotionListener, FocusListener, - ChangeListener, PropertyChangeListener +public class BasicButtonListener implements MouseListener, MouseMotionListener, + FocusListener, ChangeListener, PropertyChangeListener { public BasicButtonListener(AbstractButton b) { @@ -66,10 +65,12 @@ public class BasicButtonListener public void propertyChange(PropertyChangeEvent e) { + // TODO: What should be done here, if anything? } protected void checkOpacity(AbstractButton b) { + // TODO: What should be done here? } public void focusGained(FocusEvent e) @@ -129,18 +130,22 @@ public class BasicButtonListener public void stateChanged(ChangeEvent e) { + // TODO: What should be done here, if anything? } public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java index 6c80f14b6fc..2d3dbd350e0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; @@ -49,13 +48,19 @@ import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.Icon; import javax.swing.InputMap; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ButtonUI; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +/** + * A UI delegate for the {@link JButton} component. + */ public class BasicButtonUI extends ButtonUI { /** @@ -72,13 +77,11 @@ public class BasicButtonUI extends ButtonUI private int textShiftOffset; - private Color focusColor; - /** * Factory method to create an instance of BasicButtonUI for a given * {@link JComponent}, which should be an {@link AbstractButton}. * - * @param c The component to create a UI got + * @param c The component. * * @return A new UI capable of drawing the component */ @@ -87,21 +90,46 @@ public class BasicButtonUI extends ButtonUI return new BasicButtonUI(); } + /** + * Returns the default gap between the button's text and icon (in pixels). + * + * @param b the button (ignored). + * + * @return The gap. + */ public int getDefaultTextIconGap(AbstractButton b) { return defaultTextIconGap; } + /** + * Sets the text shift offset to zero. + * + * @see #setTextShiftOffset() + */ protected void clearTextShiftOffset() { textShiftOffset = 0; } + /** + * Returns the text shift offset. + * + * @return The text shift offset. + * + * @see #clearTextShiftOffset() + * @see #setTextShiftOffset() + */ protected int getTextShiftOffset() { return textShiftOffset; } + /** + * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}. + * + * @see #clearTextShiftOffset() + */ protected void setTextShiftOffset() { textShiftOffset = defaultTextShiftOffset; @@ -118,23 +146,33 @@ public class BasicButtonUI extends ButtonUI return "Button."; } + /** + * Installs the default settings. + * + * @param b the button (<code>null</code> not permitted). + */ protected void installDefaults(AbstractButton b) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); String prefix = getPropertyPrefix(); - focusColor = defaults.getColor(prefix + "focus"); - b.setForeground(defaults.getColor(prefix + "foreground")); - b.setBackground(defaults.getColor(prefix + "background")); - b.setMargin(defaults.getInsets(prefix + "margin")); - b.setBorder(defaults.getBorder(prefix + "border")); - b.setIconTextGap(defaults.getInt(prefix + "textIconGap")); + LookAndFeel.installColorsAndFont(b, prefix + "background", + prefix + "foreground", prefix + "font"); + LookAndFeel.installBorder(b, prefix + "border"); + b.setMargin(UIManager.getInsets(prefix + "margin")); + b.setIconTextGap(UIManager.getInt(prefix + "textIconGap")); b.setInputMap(JComponent.WHEN_FOCUSED, - (InputMap) defaults.get(prefix + "focusInputMap")); - b.setOpaque(true); + (InputMap) UIManager.get(prefix + "focusInputMap")); + b.setRolloverEnabled(UIManager.getBoolean(prefix + "rollover")); } + /** + * Removes the defaults added by {@link #installDefaults(AbstractButton)}. + * + * @param b the button (<code>null</code> not permitted). + */ protected void uninstallDefaults(AbstractButton b) { + if (b.getFont() instanceof UIResource) + b.setFont(null); b.setForeground(null); b.setBackground(null); b.setBorder(null); @@ -144,11 +182,25 @@ public class BasicButtonUI extends ButtonUI protected BasicButtonListener listener; + /** + * Creates and returns a new instance of {@link BasicButtonListener}. This + * method provides a hook to make it easy for subclasses to install a + * different listener. + * + * @param b the button. + * + * @return A new listener. + */ protected BasicButtonListener createButtonListener(AbstractButton b) { return new BasicButtonListener(b); } + /** + * Installs listeners for the button. + * + * @param b the button (<code>null</code> not permitted). + */ protected void installListeners(AbstractButton b) { listener = createButtonListener(b); @@ -159,6 +211,11 @@ public class BasicButtonUI extends ButtonUI b.addMouseMotionListener(listener); } + /** + * Uninstalls listeners for the button. + * + * @param b the button (<code>null</code> not permitted). + */ protected void uninstallListeners(AbstractButton b) { b.removeChangeListener(listener); @@ -215,12 +272,12 @@ public class BasicButtonUI extends ButtonUI return d; } - private static Icon currentIcon(AbstractButton b) + static Icon currentIcon(AbstractButton b) { Icon i = b.getIcon(); ButtonModel model = b.getModel(); - if (model.isPressed() && b.getPressedIcon() != null) + if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled()) i = b.getPressedIcon(); else if (model.isRollover()) @@ -231,7 +288,7 @@ public class BasicButtonUI extends ButtonUI i = b.getRolloverIcon(); } - else if (b.isSelected()) + else if (b.isSelected() && b.isEnabled()) { if (b.isEnabled() && b.getSelectedIcon() != null) i = b.getSelectedIcon(); @@ -264,7 +321,10 @@ public class BasicButtonUI extends ButtonUI g.setFont(f); - SwingUtilities.calculateInnerArea(b, vr); + if (b.isBorderPainted()) + SwingUtilities.calculateInnerArea(b, vr); + else + vr = SwingUtilities.getLocalBounds(b); String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), b.getText(), currentIcon(b), @@ -279,13 +339,11 @@ public class BasicButtonUI extends ButtonUI if ((b.getModel().isArmed() && b.getModel().isPressed()) || b.isSelected()) paintButtonPressed(g, b); - else - paintButtonNormal(g, vr, c); paintIcon(g, c, ir); if (text != null) paintText(g, b, tr, text); - if (b.isFocusOwner()) + if (b.isFocusOwner() && b.isFocusPainted()) paintFocus(g, b, vr, tr, ir); } @@ -331,44 +389,25 @@ public class BasicButtonUI extends ButtonUI /** * Paints the background area of an {@link AbstractButton} in the pressed - * state. This means filling the supplied area with the {@link - * pressedBackgroundColor}. + * state. This means filling the supplied area with a darker than normal + * background. * * @param g The graphics context to paint with * @param b The button to paint the state of */ protected void paintButtonPressed(Graphics g, AbstractButton b) { - if (b.isContentAreaFilled()) - { - Rectangle area = new Rectangle(); - SwingUtilities.calculateInnerArea(b, area); - g.setColor(b.getBackground().darker()); - g.fillRect(area.x, area.y, area.width, area.height); - } - } - - /** - * Paints the background area of an {@link AbstractButton} in the normal, - * non-pressed state. This means filling the supplied area with the - * {@link normalBackgroundColor}. - * - * @param g The graphics context to paint with - * @param area The area in which to paint - * @param b The component to paint the state of - */ - private void paintButtonNormal(Graphics g, Rectangle area, JComponent b) - { - if (((AbstractButton)b).isContentAreaFilled() && b.isOpaque()) + if (b.isContentAreaFilled() && b.isOpaque()) { - g.setColor(b.getBackground()); + Rectangle area = new Rectangle(); + SwingUtilities.calculateInnerArea(b, area); + g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow")); g.fillRect(area.x, area.y, area.width, area.height); } } /** - * Paints the "text" property of an {@link AbstractButton}, using the - * {@link textColor} color. + * Paints the "text" property of an {@link AbstractButton}. * * @param g The graphics context to paint with * @param c The component to paint the state of @@ -382,8 +421,7 @@ public class BasicButtonUI extends ButtonUI } /** - * Paints the "text" property of an {@link AbstractButton}, using the - * {@link textColor} color. + * Paints the "text" property of an {@link AbstractButton}. * * @param g The graphics context to paint with * @param b The button to paint the state of @@ -401,15 +439,15 @@ public class BasicButtonUI extends ButtonUI if (b.isEnabled()) { - g.setColor(b.getForeground()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + g.setColor(b.getForeground()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); } else { - g.setColor(b.getBackground().brighter()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); - g.setColor(b.getBackground().darker()); - g.drawString(text, textRect.x + 1, textRect.y + fm.getAscent() + 1); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + String prefix = getPropertyPrefix(); + g.setColor(defaults.getColor(prefix + "disabledText")); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java index 945aea53dc3..95e0dc98257 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java @@ -53,6 +53,15 @@ import javax.swing.plaf.ComponentUI; */ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI { + + /** + * Creates a new BasicCheckBoxMenuItemUI object. + */ + public BasicCheckBoxMenuItemUI() + { + super(); + } + /** * Factory method to create a BasicCheckBoxMenuItemUI for the given {@link * JComponent}, which should be a JCheckBoxMenuItem @@ -77,18 +86,6 @@ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI } /** - * This method installs the defaults that are defined in the Basic look and - * feel for this JRadioButtonMenuItem - */ - protected void installDefaults() - { - super.installDefaults(); - - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - checkIcon = defaults.getIcon("CheckBoxMenuItem.checkIcon"); - } - - /** * DOCUMENT ME! * * @param item DOCUMENT ME! @@ -100,5 +97,7 @@ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI MenuElement[] path, MenuSelectionManager manager) { + // TODO: May not be implemented properly. + item.processMouseEvent(e, path, manager); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java index 4e6d3815453..5a872ae6368 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java @@ -47,8 +47,7 @@ import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTabbedPane; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; import javax.swing.colorchooser.AbstractColorChooserPanel; import javax.swing.colorchooser.ColorChooserComponentFactory; import javax.swing.event.ChangeEvent; @@ -243,12 +242,21 @@ public class BasicColorChooserUI extends ColorChooserUI { uninstallListeners(); uninstallDefaults(); + uninstallDefaultChoosers(); pane = null; chooser = null; } /** + * Uninstalls the default color choosers that have been installed by this UI. + */ + protected void uninstallDefaultChoosers() + { + defaultChoosers = null; + } + + /** * This method installs the preview panel for the JColorChooser. */ protected void installPreviewPanel() @@ -281,11 +289,9 @@ public class BasicColorChooserUI extends ColorChooserUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - chooser.setFont(defaults.getFont("ColorChooser.font")); - chooser.setForeground(defaults.getColor("ColorChooser.foreground")); - chooser.setBackground(defaults.getColor("ColorChooser.background")); + LookAndFeel.installColorsAndFont(chooser, "ColorChooser.background", + "ColorChooser.foreground", + "ColorChooser.font"); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java index dd867f0dc55..831dde8c336 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java @@ -45,35 +45,34 @@ import java.awt.event.FocusListener; import javax.swing.ComboBoxEditor; import javax.swing.JTextField; -import javax.swing.border.EmptyBorder; /** - * This is a component that is responsible for displaying/editting selected - * item in comboBox. By default, the JTextField is returned as - * BasicComboBoxEditor. + * An editor used by the {@link BasicComboBoxUI} class. This editor uses a + * {@link JTextField} as the editor component. * * @author Olga Rodimina */ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, FocusListener { + /** The editor component. */ protected JTextField editor; /** - * Creates a new BasicComboBoxEditor object. + * Creates a new <code>BasicComboBoxEditor</code> instance. */ public BasicComboBoxEditor() { editor = new JTextField(); - editor.setBorder(new EmptyBorder(1, 1, 1, 1)); + editor.setBorder(null); + editor.setColumns(9); } /** - * This method returns textfield that will be used by the combo box to - * display/edit currently selected item in the combo box. + * Returns the component that will be used by the combo box to display and + * edit the currently selected item in the combo box. * - * @return textfield that will be used by the combo box to display/edit - * currently selected item + * @return The editor component, which is a {@link JTextField} in this case. */ public Component getEditorComponent() { @@ -98,15 +97,18 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, } /** - * This method returns item that is currently editable. + * Returns the text from the editor component. * - * @return item in the combo box that is currently editable + * @return The text from the editor component. */ public Object getItem() { return editor.getText(); } + /** + * Selects all the text in the editor component. + */ public void selectAll() { editor.selectAll(); @@ -136,8 +138,8 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, } /** - * This method adds actionListener to the editor. If the user will edit - * currently selected item in the textfield and pressEnter, then action + * Adds an {@link ActionListener} to the editor component. If the user will + * edit currently selected item in the textfield and pressEnter, then action * will be performed. The actionPerformed of this ActionListener should * change the selected item of the comboBox to the newly editted selected * item. @@ -147,27 +149,32 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, */ public void addActionListener(ActionListener l) { - // FIXME: Need to implement + editor.addActionListener(l); } /** - * This method removes actionListener from the textfield. + * Removes the {@link ActionListener} from the editor component. * - * @param l the ActionListener to remove from the textfield. + * @param l the listener to remove. */ public void removeActionListener(ActionListener l) { - // FIXME: Need to implement + editor.removeActionListener(l); } + /** + * A subclass of {@link BasicComboBoxEditor} that implements the + * {@link UIResource} interface. + */ public static class UIResource extends BasicComboBoxEditor implements javax.swing.plaf.UIResource { /** - * Creates a new UIResource object. + * Creates a new <code>BasicComboBoxEditor.UIResource</code> instance. */ public UIResource() { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java index e4fbb8352a9..8115605b77a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java @@ -1,5 +1,5 @@ /* BasicComboBoxRenderer.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,36 +40,39 @@ package javax.swing.plaf.basic; import java.awt.Component; import java.awt.Dimension; +import java.awt.FontMetrics; import java.io.Serializable; +import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.SwingConstants; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; /** - * This class is renderer for the combo box. + * A renderer for a {@link JComboBox}. * * @author Olga Rodimina */ -public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, - Serializable +public class BasicComboBoxRenderer + extends JLabel + implements ListCellRenderer, Serializable { /** - * This border is used whenever renderer doesn't have a focus. + * A shared border instance for all renderers. */ protected static Border noFocusBorder = new EmptyBorder(0, 0, 0, 0); /** - * Creates a new BasicComboBoxRenderer object. + * Creates a new <code>BasicComboBoxRenderer</code> object. */ public BasicComboBoxRenderer() { setHorizontalAlignment(SwingConstants.LEFT); + setBorder(noFocusBorder); } /** @@ -83,7 +86,8 @@ public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, } /** - * getListCellRendererComponent + * Returns a component that has been configured to display the given + * <code>value</code>. * * @param list List of items for which to the background and foreground * colors @@ -100,43 +104,59 @@ public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, boolean cellHasFocus) { String s = value.toString(); - setText(s); + + // String maybe larger than comboBox. + FontMetrics fm = getToolkit().getFontMetrics(list.getFont()); + int strWidth = SwingUtilities.computeStringWidth(fm, s); + int cbWidth = getSize().width; + if (cbWidth != 0 && strWidth > cbWidth) + { + char[] str = s.toCharArray(); + int currWidth = 0; + int i = 0; + String postStr = "... "; + cbWidth -= SwingUtilities.computeStringWidth(fm, postStr); + while (i < str.length && currWidth < cbWidth) + { + ++i; + currWidth = SwingUtilities.computeStringWidth(fm, new String(str, 0, i)); + } + setText(new String(str, 0, i) + postStr); + } + else + setText(s); + setOpaque(true); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - if (isSelected) + if (isSelected || cellHasFocus) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); + setBackground(list.getBackground()); + setForeground(list.getForeground()); } setEnabled(list.isEnabled()); setFont(list.getFont()); - - // Use focusCellHighlightBorder when renderer has focus and - // noFocusBorder otherwise - if (cellHasFocus) - setBorder(UIManager.getBorder("List.focusCellHighlightBorder")); - else - setBorder(noFocusBorder); - return this; } + /** + * A subclass of {@link BasicComboBoxRenderer} that implements the + * {@link javax.swing.plaf.UIResource} interface. + */ public static class UIResource extends BasicComboBoxRenderer implements javax.swing.plaf.UIResource { /** - * Creates a new UIResource object. + * Creates a new <code>UIResource</code> object. */ public UIResource() { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java index 68e18a6ab01..464c8dd9f63 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -42,6 +42,8 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; @@ -69,15 +71,17 @@ import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.plaf.ComboBoxUI; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; /** - * UI Delegate for JComboBox + * A UI delegate for the {@link JComboBox} component. * * @author Olga Rodimina * @author Robert Schuster @@ -85,52 +89,54 @@ import javax.swing.plaf.ComponentUI; public class BasicComboBoxUI extends ComboBoxUI { /** - * This arrow button that is displayed in the rigth side of JComboBox. This - * button is used to hide and show combo box's list of items + * The arrow button that is displayed in the right side of JComboBox. This + * button is used to hide and show combo box's list of items. */ protected JButton arrowButton; /** - * The combo box for which this UI delegate is for + * The combo box represented by this UI delegate. */ protected JComboBox comboBox; /** - * Component that is responsible for displaying/editting selected item of - * the combo box. By default JTextField is used as an editor for the - * JComboBox + * The component that is responsible for displaying/editing the selected + * item of the combo box. + * + * @see BasicComboBoxEditor#getEditorComponent() */ protected Component editor; /** - * Listener listening to focus events occuring in the JComboBox + * A listener listening to focus events occurring in the {@link JComboBox}. */ protected FocusListener focusListener; /** - * tells whether JComboBox currently has focus + * A flag indicating whether JComboBox currently has the focus. */ protected boolean hasFocus; /** - * Listener listening to item events fired by the JComboBox + * A listener listening to item events fired by the {@link JComboBox}. */ protected ItemListener itemListener; /** - * KeyListener listening to key events that occur while JComboBox has focus + * A listener listening to key events that occur while {@link JComboBox} has + * the focus. */ protected KeyListener keyListener; /** - * MouseListener listening to mouse events occuring in the combo box + * A listener listening to mouse events occuring in the {@link JComboBox}. */ private MouseListener mouseListener; /** * List used when rendering selected item of the combo box. The selection - * and foreground colors for combo box renderer are configured from this - * list + * and foreground colors for combo box renderer are configured from this + * list. */ protected JList listBox; @@ -140,11 +146,14 @@ public class BasicComboBoxUI extends ComboBoxUI protected ListDataListener listDataListener; /** - * Popup list containing combo box's menu items + * Popup list containing the combo box's menu items. */ protected ComboPopup popup; + protected KeyListener popupKeyListener; + protected MouseListener popupMouseListener; + protected MouseMotionListener popupMouseMotionListener; /** @@ -152,30 +161,34 @@ public class BasicComboBoxUI extends ComboBoxUI */ protected PropertyChangeListener propertyChangeListener; + /** + * The button background. + * @see #installDefaults() + */ + private Color buttonBackground; + + /** + * The button shadow. + * @see #installDefaults() + */ + private Color buttonShadow; + /** - * Colors that are used to render selected item in the combo box. + * The button dark shadow. + * @see #installDefaults() */ - private Color shadow; - private Color darkShadow; - private Color highlight; - private Color lightHighlight; + private Color buttonDarkShadow; + + /** + * The button highlight. + * @see #installDefaults() + */ + private Color buttonHighlight; /* Size of the largest item in the comboBox * This is package-private to avoid an accessor method. */ - Dimension largestItemSize; - - // It seems that JComboBox doesn't have a border set explicitely. So we just - // paint the border everytime combo box is displayed. - - /* border insets for this JComboBox - * This is package-private to avoid an accessor method. */ - static final Insets borderInsets = new Insets(2, 2, 2, 2); - - // Width of the arrow button - // This is package-private to avoid an accessor method. - // FIXME: has wrong name for a constant. - static final int arrowButtonWidth = 15; + Dimension displaySize; // FIXME: This fields aren't used anywhere at this moment. protected Dimension cachedMinimumSize; @@ -183,19 +196,20 @@ public class BasicComboBoxUI extends ComboBoxUI protected boolean isMinimumSizeDirty; /** - * Creates a new BasicComboBoxUI object. + * Creates a new <code>BasicComboBoxUI</code> object. */ public BasicComboBoxUI() { + // Nothing to do here. } /** - * Factory method to create a BasicComboBoxUI for the given {@link - * JComponent}, which should be a {@link JComboBox}. + * A factory method to create a UI delegate for the given + * {@link JComponent}, which should be a {@link JComboBox}. * * @param c The {@link JComponent} a UI is being created for. * - * @return A BasicComboBoxUI for the {@link JComponent}. + * @return A UI delegate for the {@link JComponent}. */ public static ComponentUI createUI(JComponent c) { @@ -203,9 +217,11 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method installs the UI for the given JComponent. + * Installs the UI for the given {@link JComponent}. * - * @param c The JComponent to install a UI for. + * @param c the JComponent to install a UI for. + * + * @see #uninstallUI(JComponent) */ public void installUI(JComponent c) { @@ -213,20 +229,22 @@ public class BasicComboBoxUI extends ComboBoxUI if (c instanceof JComboBox) { - comboBox = (JComboBox) c; - comboBox.setOpaque(true); - comboBox.setLayout(createLayoutManager()); - installDefaults(); - installComponents(); - installListeners(); - installKeyboardActions(); + comboBox = (JComboBox) c; + comboBox.setOpaque(true); + comboBox.setLayout(createLayoutManager()); + installDefaults(); + installComponents(); + installListeners(); + installKeyboardActions(); } } /** - * This method uninstalls the UI. + * Uninstalls the UI for the given {@link JComponent}. * * @param c The JComponent that is having this UI removed. + * + * @see #installUI(JComponent) */ public void uninstallUI(JComponent c) { @@ -238,27 +256,27 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method installs the defaults that are defined in the Basic look and - * feel for this {@link JComboBox}. + * Installs the defaults that are defined in the {@link BasicLookAndFeel} + * for this {@link JComboBox}. + * + * @see #uninstallDefaults() */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - comboBox.setBackground(defaults.getColor("ComboBox.background")); - comboBox.setFont(defaults.getFont("ComboBox.font")); - comboBox.setForeground(defaults.getColor("ComboBox.foreground")); - - // Set default color that should be used to to render selected item - // of the combo box. - shadow = defaults.getColor("Button.shadow"); - darkShadow = defaults.getColor("Button.darkShadow"); - lightHighlight = defaults.getColor("Button.light"); - highlight = defaults.getColor("Button.highlight"); + LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background", + "ComboBox.foreground", "ComboBox.font"); + + // fetch the button color scheme + buttonBackground = UIManager.getColor("ComboBox.buttonBackground"); + buttonShadow = UIManager.getColor("ComboBox.buttonShadow"); + buttonDarkShadow = UIManager.getColor("ComboBox.buttonDarkShadow"); + buttonHighlight = UIManager.getColor("ComboBox.buttonHighlight"); } /** - * This method creates and installs the listeners for this UI. + * Creates and installs the listeners for this UI. + * + * @see #uninstallListeners() */ protected void installListeners() { @@ -268,6 +286,7 @@ public class BasicComboBoxUI extends ComboBoxUI focusListener = createFocusListener(); comboBox.addFocusListener(focusListener); + listBox.addFocusListener(focusListener); itemListener = createItemListener(); comboBox.addItemListener(itemListener); @@ -276,35 +295,40 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.addKeyListener(keyListener); mouseListener = createMouseListener(); - comboBox.addMouseListener(mouseListener); + arrowButton.addMouseListener(mouseListener); // install listeners that listen to combo box model listDataListener = createListDataListener(); comboBox.getModel().addListDataListener(listDataListener); - - configureArrowButton(); } /** - * This method uninstalls the defaults and sets any objects created during - * install to null + * Uninstalls the defaults and sets any objects created during + * install to <code>null</code>. + * + * @see #installDefaults() */ protected void uninstallDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - comboBox.setBackground(null); - comboBox.setFont(null); - comboBox.setForeground(null); - - shadow = null; - darkShadow = null; - lightHighlight = null; - highlight = null; + if (comboBox.getFont() instanceof UIResource) + comboBox.setFont(null); + + if (comboBox.getForeground() instanceof UIResource) + comboBox.setForeground(null); + + if (comboBox.getBackground() instanceof UIResource) + comboBox.setBackground(null); + + buttonBackground = null; + buttonShadow = null; + buttonDarkShadow = null; + buttonHighlight = null; } /** * Detaches all the listeners we attached in {@link #installListeners}. + * + * @see #installListeners() */ protected void uninstallListeners() { @@ -312,6 +336,7 @@ public class BasicComboBoxUI extends ComboBoxUI propertyChangeListener = null; comboBox.removeFocusListener(focusListener); + listBox.removeFocusListener(focusListener); focusListener = null; comboBox.removeItemListener(itemListener); @@ -320,17 +345,15 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.removeKeyListener(keyListener); keyListener = null; - comboBox.removeMouseListener(mouseListener); + arrowButton.removeMouseListener(mouseListener); mouseListener = null; comboBox.getModel().removeListDataListener(listDataListener); listDataListener = null; - - unconfigureArrowButton(); } /** - * This method creates popup that will contain list of combo box's items + * Creates the popup that will contain list of combo box's items. * * @return popup containing list of combo box's items */ @@ -340,7 +363,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Creates KeyListener to listen to key events. + * Creates a {@link KeyListener} to listen to key events. * * @return KeyListener that listens to key events. */ @@ -350,8 +373,8 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create MouseListener that will listen to mouse event occuring - * in combo box. + * Creates a {@link MouseListener} that will listen to mouse events occurring + * in the combo box. * * @return the MouseListener */ @@ -361,10 +384,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create FocusListener that will listen to changes in this + * Creates the {@link FocusListener} that will listen to changes in this * JComboBox's focus. * - * @return theFocusListener + * @return the FocusListener. */ protected FocusListener createFocusListener() { @@ -372,9 +395,9 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create ListDataListener to listen to ComboBox's data model + * Creates a {@link ListDataListener} to listen to the combo box's data model. * - * @return ListDataListener + * @return The new listener. */ protected ListDataListener createListDataListener() { @@ -382,10 +405,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates ItemListener that will listen to to the changes in + * Creates an {@link ItemListener} that will listen to the changes in * the JComboBox's selection. * - * @return the ItemListener + * @return The ItemListener */ protected ItemListener createItemListener() { @@ -393,10 +416,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates PropertyChangeListener to listen to the changes in + * Creates a {@link PropertyChangeListener} to listen to the changes in * the JComboBox's bound properties. * - * @return the PropertyChangeListener + * @return The PropertyChangeListener */ protected PropertyChangeListener createPropertyChangeListener() { @@ -404,9 +427,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method returns layout manager for the combo box. + * Creates and returns a layout manager for the combo box. Subclasses can + * override this method to provide a different layout. * - * @return layout manager for the combo box + * @return a layout manager for the combo box. */ protected LayoutManager createLayoutManager() { @@ -414,10 +438,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates component that will be responsible for rendering the + * Creates a component that will be responsible for rendering the * selected component in the combo box. * - * @return render for the combo box + * @return A renderer for the combo box. */ protected ListCellRenderer createRenderer() { @@ -425,58 +449,54 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Creates component that will be responsible for displaying/editting - * selected item in the combo box. This editor is used only when combo box - * is editable. + * Creates the component that will be responsible for displaying/editing + * the selected item in the combo box. This editor is used only when combo + * box is editable. * - * @return component that will be responsible for displaying/editting - * selected item in the combo box. + * @return A new component that will be responsible for displaying/editing + * the selected item in the combo box. */ protected ComboBoxEditor createEditor() { - return new BasicComboBoxEditor(); + return new BasicComboBoxEditor.UIResource(); } /** - * This method installs components for this JComboBox. ArrowButton, main - * part of combo box (upper part) and popup list of items are created and + * Installs the components for this JComboBox. ArrowButton, main + * part of combo box (upper part) and popup list of items are created and * configured here. */ protected void installComponents() { - // create and install arrow button - arrowButton = createArrowButton(); - - comboBox.add(arrowButton); - - // Set list that will be used by BasicComboBoxRender - // in order to determine the right colors when rendering - listBox = new JList(); - - Color background = arrowButton.getBackground(); - listBox.setBackground(background); - listBox.setSelectionBackground(background.darker()); - - Color foreground = arrowButton.getForeground(); - listBox.setForeground(foreground); - listBox.setSelectionForeground(foreground); + // create drop down list of items + popup = createPopup(); + listBox = popup.getList(); // set editor and renderer for the combo box. Editor is used // only if combo box becomes editable, otherwise renderer is used // to paint the selected item; combobox is not editable by default. comboBox.setRenderer(createRenderer()); - comboBox.setEditor(createEditor()); - editor = comboBox.getEditor().getEditorComponent(); + // create and install arrow button + arrowButton = createArrowButton(); + configureArrowButton(); + comboBox.add(arrowButton); - // create drop down list of items - popup = createPopup(); + ComboBoxEditor currentEditor = comboBox.getEditor(); + if (currentEditor == null || currentEditor instanceof UIResource) + { + currentEditor = createEditor(); + comboBox.setEditor(currentEditor); + } + editor = currentEditor.getEditorComponent(); comboBox.revalidate(); } /** - * This method uninstalls components from this JComboBox + * Uninstalls components from this {@link JComboBox}. + * + * @see #installComponents() */ protected void uninstallComponents() { @@ -490,12 +510,18 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.setRenderer(null); - comboBox.setEditor(null); - editor = null; + // if the editor is not an instanceof UIResource, it was not set by the + // UI delegate, so don't clear it... + ComboBoxEditor currentEditor = comboBox.getEditor(); + if (currentEditor instanceof UIResource) + { + comboBox.setEditor(null); + editor = null; + } } /** - * This method adds editor to the combo box + * Adds the current editor to the combo box. */ public void addEditor() { @@ -503,7 +529,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method removes editor from the combo box + * Removes the current editor from the combo box. */ public void removeEditor() { @@ -511,15 +537,17 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method configures editor for this combo box. + * Configures the editor for this combo box. */ protected void configureEditor() { + editor.setFont(comboBox.getFont()); + comboBox.getEditor().setItem(comboBox.getSelectedItem()); // FIXME: Need to implement. Set font and add listeners. } /** - * This method removes all the listeners for the editor. + * Unconfigures the editor for this combo nox. This method is not implemented. */ protected void unconfigureEditor() { @@ -527,42 +555,50 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method adds listeners to the arrow button part of the combo box. + * Configures the arrow button. + * + * @see #configureArrowButton() */ public void configureArrowButton() { - arrowButton.addMouseListener(mouseListener); + arrowButton.setEnabled(comboBox.isEnabled()); + arrowButton.setFont(comboBox.getFont()); } /** - * This method removes listeners from the arrow button part of the combo - * box. + * Unconfigures the arrow button. + * + * @see #configureArrowButton() + * + * @specnote The specification says this method is implementation specific + * and should not be used or overridden. */ public void unconfigureArrowButton() { - arrowButton.removeMouseListener(mouseListener); + // Nothing to do here yet. } /** - * This method create arrow button for this JComboBox. Arrow button is - * responsible for displaying / hiding drop down list of items when it is - * clicked. + * Creates an arrow button for this {@link JComboBox}. The arrow button is + * displayed at the right end of the combo box and is used to display/hide + * the drop down list of items. * - * @return JButton arrow button for this JComboBox. + * @return A new button. */ protected JButton createArrowButton() { - return new BasicArrowButton(BasicArrowButton.SOUTH); + return new BasicArrowButton(BasicArrowButton.SOUTH, buttonBackground, + buttonShadow, buttonDarkShadow, buttonHighlight); } /** - * This method checks if popup part of the combo box is visible on the - * screen + * Returns <code>true</code> if the popup is visible, and <code>false</code> + * otherwise. * * @param c The JComboBox to check * - * @return true if popup part of the JComboBox is visible and false - * otherwise. + * @return <code>true</code> if popup part of the JComboBox is visible and + * <code>false</code> otherwise. */ public boolean isPopupVisible(JComboBox c) { @@ -570,7 +606,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Displays/Hides JComboBox's list of items on the screen. + * Displays/hides the {@link JComboBox}'s list of items on the screen. * * @param c The combo box, for which list of items should be * displayed/hidden @@ -579,7 +615,10 @@ public class BasicComboBoxUI extends ComboBoxUI public void setPopupVisible(JComboBox c, boolean v) { if (v) - popup.show(); + { + popup.show(); + popup.getList().requestFocus(); + } else popup.hide(); } @@ -593,7 +632,7 @@ public class BasicComboBoxUI extends ComboBoxUI */ public boolean isFocusTraversable(JComboBox c) { - if (comboBox.isEditable()) + if (!comboBox.isEditable()) return true; return false; @@ -607,56 +646,28 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void paint(Graphics g, JComponent c) { - if (c instanceof JComboBox) - { - JComboBox cb = (JComboBox) c; - - paintBorder(g, comboBox.getBounds(), hasFocus); - - Rectangle rect = rectangleForCurrentValue(); - paintCurrentValueBackground(g, rect, hasFocus); - paintCurrentValue(g, rect, hasFocus); - } - } - - private void paintBorder(Graphics g, Rectangle bounds, boolean hasFocus) - { - int x = 0; - int y = 0; - int width = bounds.width; - int height = bounds.height; - - Color oldColor = g.getColor(); - - if (! arrowButton.getModel().isPressed()) - BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height, Color.gray, - Color.white, Color.gray, Color.white); - else - { - g.setColor(darkShadow); - g.drawRect(x, y, width, height); - g.setColor(shadow); - g.drawRect(x + 1, y + 1, width - 3, height - 3); - } - g.setColor(oldColor); + Rectangle rect = rectangleForCurrentValue(); + paintCurrentValueBackground(g, rect, hasFocus); + paintCurrentValue(g, rect, hasFocus); } /** - * Returns preferred size for the given menu item. + * Returns preferred size for the combo box. * * @param c comboBox for which to get preferred size * - * @return $Dimension$ preferred size for the given combo box + * @return The preferred size for the given combo box */ public Dimension getPreferredSize(JComponent c) { - // return null to indicate that combo box's layout will determin its - // preferred size - return null; + // note: overriding getMinimumSize() (for example in the MetalComboBoxUI + // class) affects the getPreferredSize() result, so it seems logical that + // this method is implemented by delegating to the getMinimumSize() method + return getMinimumSize(c); } /** - * This method returns the minimum size for this {@link JComboBox} for this + * Returns the minimum size for this {@link JComboBox} for this * look and feel. * * @param c The {@link JComponent} to find the minimum size for. @@ -665,20 +676,26 @@ public class BasicComboBoxUI extends ComboBoxUI */ public Dimension getMinimumSize(JComponent c) { - return null; + Dimension d = getDisplaySize(); + int arrowButtonWidth = d.height; + Dimension result = new Dimension(d.width + arrowButtonWidth, d.height); + return result; } + /** The value returned by the getMaximumSize() method. */ + private static final Dimension MAXIMUM_SIZE = new Dimension(32767, 32767); + /** - * This method returns the maximum size for this {@link JComboBox} for this + * Returns the maximum size for this {@link JComboBox} for this * look and feel. * * @param c The {@link JComponent} to find the maximum size for * - * @return The dimensions of the minimum size. + * @return The maximum size (<code>Dimension(32767, 32767)</code>). */ public Dimension getMaximumSize(JComponent c) { - return null; + return MAXIMUM_SIZE; } public int getAccessibleChildrenCount(JComponent c) @@ -707,7 +724,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method selects next possible item relative to the current selection + * Selects next possible item relative to the current selection * to be next selected item in the combo box. */ protected void selectNextPossibleValue() @@ -718,7 +735,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method selects previous item relative to current selection to be + * Selects previous item relative to current selection to be * next selected item. */ protected void selectPreviousPossibleValue() @@ -729,8 +746,8 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method displays combo box popup if the popup is not currently shown - * on the screen and hides it if it is currently shown + * Displays combo box popup if the popup is not currently shown + * on the screen and hides it if it is currently shown */ protected void toggleOpenClose() { @@ -738,34 +755,23 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method returns bounds in which comboBox's selected Item will be - * displayed + * Returns the bounds in which comboBox's selected item will be + * displayed. * * @return rectangle bounds in which comboBox's selected Item will be * displayed */ protected Rectangle rectangleForCurrentValue() { - Rectangle cbBounds = comboBox.getBounds(); - - // Subtract width or the arrow button and border insets - Rectangle rectForCurrentValue = new Rectangle(cbBounds.x - + borderInsets.left, - cbBounds.y - + borderInsets.top, - cbBounds.width - - arrowButtonWidth - - borderInsets.left - - borderInsets.right, - cbBounds.height - - borderInsets.top - - borderInsets.bottom); - + Rectangle cbBounds = SwingUtilities.getLocalBounds(comboBox); + Rectangle abBounds = arrowButton.getBounds(); + Rectangle rectForCurrentValue = new Rectangle(cbBounds.x, cbBounds.y, + cbBounds.width - abBounds.width, cbBounds.height); return rectForCurrentValue; } /** - * This method returns insets of the current border. + * Returns the insets of the current border. * * @return Insets representing space between combo box and its border */ @@ -775,7 +781,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method paints currently selected value in the main part of the combo + * Paints currently selected value in the main part of the combo * box (part without popup). * * @param g graphics context @@ -794,28 +800,29 @@ public class BasicComboBoxUI extends ComboBoxUI * If there is currently no selected item we will take an empty * String as replacement. */ - Component comp = comboBox.getRenderer() - .getListCellRendererComponent(listBox, - (currentValue != null ? currentValue : ""), - -1, - isPressed, - hasFocus); - if (! comboBox.isEnabled()) - comp.setEnabled(false); - - g.translate(borderInsets.left, borderInsets.top); - comp.setBounds(0, 0, bounds.width, bounds.height); - comp.paint(g); - g.translate(-borderInsets.left, -borderInsets.top); - - comboBox.revalidate(); + Component comp = comboBox.getRenderer().getListCellRendererComponent( + listBox, (currentValue != null ? currentValue : ""), -1, + isPressed, hasFocus); + if (! comboBox.isEnabled()) + { + comp.setBackground(UIManager.getLookAndFeelDefaults().getColor( + "ComboBox.disabledBackground")); + comp.setForeground(UIManager.getLookAndFeelDefaults().getColor( + "ComboBox.disabledForeground")); + comp.setEnabled(false); + } + comp.setBounds(0, 0, bounds.width, bounds.height); + comp.setFont(comboBox.getFont()); + comp.paint(g); + + comboBox.revalidate(); } else comboBox.getEditor().setItem(comboBox.getSelectedItem()); } /** - * This method paints background of part of the combo box, where currently + * Paints the background of part of the combo box, where currently * selected value is displayed. If the combo box has focus this method * should also paint focus rectangle around the combo box. * @@ -832,58 +839,102 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Returns default size for the combo box that doesn't contain any elements - * in it + * Returns the default size for the display area of a combo box that does + * not contain any elements. This method returns the width and height of + * a single space in the current font, plus a margin of 1 pixel. * - * @return Default size of the combo box with no elements in it. + * @return The default display size. + * + * @see #getDisplaySize() */ protected Dimension getDefaultSize() { - return new Dimension(6, 17); + // There is nothing in the spec to say how this method should be + // implemented...so I've done some guessing, written some Mauve tests, + // and written something that gives dimensions that are close to the + // reference implementation. + FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); + int w = fm.charWidth(' ') + 2; + int h = fm.getHeight() + 2; + return new Dimension(w, h); } /** - * Returns size of the largest item in the combo box. This size will be the - * size of the combo box, not including the arrowButton. + * Returns the size of the display area for the combo box. This size will be + * the size of the combo box, not including the arrowButton. * - * @return dimensions of the largest item in the combo box. + * @return The size of the display area for the combo box. */ - protected Dimension getLargestItemSize() + protected Dimension getDisplaySize() { - ComboBoxModel model = comboBox.getModel(); - int numItems = model.getSize(); - - // if combo box doesn't have any items then simply - // return its default size - if (numItems == 0) + if (!comboBox.isEditable()) { - largestItemSize = getDefaultSize(); - return largestItemSize; + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + // calculate result based on prototype + ListCellRenderer renderer = comboBox.getRenderer(); + Component comp = renderer.getListCellRendererComponent(listBox, + prototype, -1, false, false); + Dimension compSize = comp.getPreferredSize(); + compSize.width += 2; // add 1 pixel margin around area + compSize.height += 2; + return compSize; + } + else + { + ComboBoxModel model = comboBox.getModel(); + int numItems = model.getSize(); + + // if combo box doesn't have any items then simply + // return its default size + if (numItems == 0) + { + displaySize = getDefaultSize(); + return displaySize; + } + + Dimension size = new Dimension(0, 0); + + // ComboBox's display size should be equal to the + // size of the largest item in the combo box. + ListCellRenderer renderer = comboBox.getRenderer(); + + for (int i = 0; i < numItems; i++) + { + Object item = model.getElementAt(i); + Component comp = renderer.getListCellRendererComponent(listBox, + item, -1, false, false); + + Dimension compSize = comp.getPreferredSize(); + if (compSize.width + 2 > size.width) + size.width = compSize.width + 2; + if (compSize.height + 2 > size.height) + size.height = compSize.height + 2; + } + displaySize = size; + return displaySize; + } } - - Dimension size = new Dimension(0, 0); - - // ComboBox's display size should be equal to the - // size of the largest item in the combo box. - ListCellRenderer renderer = comboBox.getRenderer(); - - for (int i = 0; i < numItems; i++) + else // an editable combo, { - Object item = model.getElementAt(i); - String s = item.toString(); - Component comp = renderer.getListCellRendererComponent(listBox, item, - -1, false, false); - - if (comp.getPreferredSize().getWidth() > size.getWidth()) - size = comp.getPreferredSize(); + Component comp = comboBox.getEditor().getEditorComponent(); + Dimension prefSize = comp.getPreferredSize(); + int width = prefSize.width; + int height = prefSize.height + 2; + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); + width = Math.max(width, fm.stringWidth(prototype.toString()) + 2); + } + displaySize = new Dimension(width, height); + return displaySize; } - - largestItemSize = size; - return largestItemSize; } /** - * This method installs the keyboard actions for the JComboBox as specified + * Installs the keyboard actions for the {@link JComboBox} as specified * by the look and feel. */ protected void installKeyboardActions() @@ -892,7 +943,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method uninstalls the keyboard actions for the JComboBox there were + * Uninstalls the keyboard actions for the {@link JComboBox} there were * installed by in {@link #installListeners}. */ protected void uninstallKeyboardActions() @@ -901,22 +952,39 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This class is Layout Manager for this combo box. + * A {@link LayoutManager} used to position the sub-components of the + * {@link JComboBox}. + * + * @see BasicComboBoxUI#createLayoutManager() */ - public class ComboBoxLayoutManager extends Object implements LayoutManager + public class ComboBoxLayoutManager implements LayoutManager { /** * Creates a new ComboBoxLayoutManager object. */ public ComboBoxLayoutManager() { + // Nothing to do here. } + /** + * Adds a component to the layout. This method does nothing, since the + * layout manager doesn't need to track the components. + * + * @param name the name to associate the component with (ignored). + * @param comp the component (ignored). + */ public void addLayoutComponent(String name, Component comp) { // Do nothing } + /** + * Removes a component from the layout. This method does nothing, since + * the layout manager doesn't need to track the components. + * + * @param comp the component. + */ public void removeLayoutComponent(Component comp) { // Do nothing @@ -925,43 +993,30 @@ public class BasicComboBoxUI extends ComboBoxUI /** * Returns preferred layout size of the JComboBox. * - * @param parent Container for which preferred size should be calculated + * @param parent the Container for which the preferred size should be + * calculated. * - * @return preferred size for the given container + * @return The preferred size for the given container */ public Dimension preferredLayoutSize(Container parent) { - Dimension d = new Dimension(0, 0); - - if (largestItemSize == null) - largestItemSize = getLargestItemSize(); - - // add size for the area that will display selected item - d.width += largestItemSize.getWidth(); - d.height += largestItemSize.getHeight(); - - // add size of the arrow button - d.width += arrowButtonWidth; - - // add width and height of the border - d.width += borderInsets.left + borderInsets.right; - d.height += borderInsets.left + borderInsets.right; - - // Add combo box's insets - Insets insets = parent.getInsets(); - d.width += insets.left + insets.right; - d.width += insets.left + insets.right; - - return d; + return getPreferredSize((JComponent) parent); } + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum size. + */ public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } /** - * This method layouts out the components in the container. It puts arrow + * Arranges the components in the container. It puts arrow * button right end part of the comboBox. If the comboBox is editable * then editor is placed to the left of arrow button, starting from the * beginning. @@ -972,21 +1027,19 @@ public class BasicComboBoxUI extends ComboBoxUI { // Position editor component to the left of arrow button if combo box is // editable - int editorWidth = comboBox.getBounds().width - arrowButtonWidth - 2; + int arrowSize = comboBox.getHeight(); + int editorWidth = comboBox.getBounds().width - arrowSize; if (comboBox.isEditable()) - editor.setBounds(borderInsets.left, borderInsets.top, editorWidth, - comboBox.getBounds().height - borderInsets.left - - borderInsets.top); - - arrowButton.setBounds(editorWidth, 2, arrowButtonWidth, - comboBox.getBounds().height - 4); + editor.setBounds(0, 0, editorWidth, comboBox.getBounds().height); + + arrowButton.setBounds(editorWidth, 0, arrowSize, arrowSize); comboBox.revalidate(); } } /** - * This class handles focus changes occuring in the combo box. This class is + * Handles focus changes occuring in the combo box. This class is * responsible for repainting combo box whenever focus is gained or lost * and also for hiding popup list of items whenever combo box loses its * focus. @@ -998,11 +1051,12 @@ public class BasicComboBoxUI extends ComboBoxUI */ public FocusHandler() { + // Nothing to do here. } /** - * This mehtod is invoked when combo box gains focus. It repaints main - * part of combo box accordingally. + * Invoked when combo box gains focus. It repaints main + * part of combo box accordingly. * * @param e the FocusEvent */ @@ -1013,22 +1067,22 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method is invoked when combo box loses focus It repaint main part - * of combo box accordingally and hides popup list of items. + * Invoked when the combo box loses focus. It repaints the main part + * of the combo box accordingly and hides the popup list of items. * * @param e the FocusEvent */ public void focusLost(FocusEvent e) { hasFocus = false; + setPopupVisible(comboBox, false); comboBox.repaint(); - popup.hide(); } } /** - * This class handles ItemEvent fired by the JComboBox when its selected - * item changes. + * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its + * selected item changes. */ public class ItemHandler extends Object implements ItemListener { @@ -1037,16 +1091,19 @@ public class BasicComboBoxUI extends ComboBoxUI */ public ItemHandler() { + // Nothing to do here. } /** - * This method is invoked when selected item becomes deselected or when + * Invoked when selected item becomes deselected or when * new item becomes selected. * * @param e the ItemEvent representing item's state change. */ public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) + comboBox.getEditor().setItem(e.getItem()); comboBox.repaint(); } } @@ -1058,11 +1115,11 @@ public class BasicComboBoxUI extends ComboBoxUI { public KeyHandler() { + // Nothing to do here. } - /* - * This method is invoked whenever key is pressed while JComboBox is in - * focus. + /** + * Invoked whenever key is pressed while JComboBox is in focus. */ public void keyPressed(KeyEvent e) { @@ -1072,7 +1129,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This class handles to the changes occuring in the JComboBox's data model + * Handles the changes occurring in the JComboBox's data model. */ public class ListDataHandler extends Object implements ListDataListener { @@ -1081,10 +1138,11 @@ public class BasicComboBoxUI extends ComboBoxUI */ public ListDataHandler() { + // Nothing to do here. } /** - * This method is invoked content's of JComboBox's data model are changed + * Invoked if the content's of JComboBox's data model are changed. * * @param e ListDataEvent describing the change. */ @@ -1094,36 +1152,27 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method is invoked when items were added to the JComboBox's data - * model. + * Invoked when items are added to the JComboBox's data model. * * @param e ListDataEvent describing the change. */ public void intervalAdded(ListDataEvent e) { - // must determine if the size of the combo box should change - int start = e.getIndex0(); - int end = e.getIndex1(); - ComboBoxModel model = comboBox.getModel(); ListCellRenderer renderer = comboBox.getRenderer(); - if (largestItemSize == null) - largestItemSize = new Dimension(0, 0); + if (displaySize == null) + displaySize = getDisplaySize(); + if (displaySize.width < getDefaultSize().width) + displaySize.width = getDefaultSize().width; + if (displaySize.height < getDefaultSize().height) + displaySize.height = getDefaultSize().height; - for (int i = start; i < end; i++) - { - Object item = model.getElementAt(i); - Component comp = renderer.getListCellRendererComponent(new JList(), - item, -1, - false, false); - if (comp.getPreferredSize().getWidth() > largestItemSize.getWidth()) - largestItemSize = comp.getPreferredSize(); - } + comboBox.repaint(); } /** - * This method is invoked when items were removed from the JComboBox's + * Invoked when items are removed from the JComboBox's * data model. * * @param e ListDataEvent describing the change. @@ -1131,23 +1180,29 @@ public class BasicComboBoxUI extends ComboBoxUI public void intervalRemoved(ListDataEvent e) { // recalculate display size of the JComboBox. - largestItemSize = getLargestItemSize(); + displaySize = getDisplaySize(); comboBox.repaint(); } } /** - * This class handles PropertyChangeEvents fired by JComboBox. + * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}. */ public class PropertyChangeHandler extends Object implements PropertyChangeListener { + /** + * Creates a new instance. + */ public PropertyChangeHandler() { + // Nothing to do here. } /** - * This method is invoked whenever bound property of JComboBox changes. + * Invoked whenever bound property of JComboBox changes. + * + * @param e the event. */ public void propertyChange(PropertyChangeEvent e) { @@ -1185,59 +1240,36 @@ public class BasicComboBoxUI extends ComboBoxUI if ((ComboBoxModel) e.getNewValue() != null) comboBox.getModel().addListDataListener(listDataListener); } + else if (e.getPropertyName().equals("font")) + { + Font font = (Font) e.getNewValue(); + editor.setFont(font); + listBox.setFont(font); + arrowButton.setFont(font); + comboBox.revalidate(); + comboBox.repaint(); + } // FIXME: Need to handle changes in other bound properties. } } /** - * MouseHandler listens to mouse events occuring in the combo box. This - * class is responsible for repainting this JComboBox whenever the mouse is - * being pressed or released over it. + * A handler for mouse events occurring in the combo box. An instance of + * this class is returned by the <code>createMouseListener()</code> method. */ private class MouseHandler extends MouseAdapter { /** - * This method is invoked when mouse is pressed over the combo box. It - * repaints the combo box accordinglly + * Invoked when mouse is pressed over the combo box. It toggles the + * visibility of the popup list. * - * @param e the MouseEvent + * @param e the event */ public void mousePressed(MouseEvent e) { if (comboBox.isEnabled()) - { - if (e.getSource() instanceof JComboBox) - { - arrowButton.getModel().setPressed(true); - arrowButton.getModel().setArmed(true); - } - - comboBox.repaint(); - - if (e.getSource() instanceof BasicArrowButton) - toggleOpenClose(); - } - } - - /** - * This method is invoked when mouse is released over the combo box. It - * repaints the combo box accordinglly - * - * @param e the MouseEvent - */ - public void mouseReleased(MouseEvent e) - { - if (comboBox.isEnabled()) - { - if (e.getSource() instanceof JComboBox) - { - arrowButton.getModel().setPressed(false); - arrowButton.getModel().setArmed(false); - } - - comboBox.repaint(); - } + toggleOpenClose(); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java index 73aac8d4e65..73979bb89c1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; @@ -55,6 +56,7 @@ import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.BorderFactory; import javax.swing.ComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -178,8 +180,8 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup // to display number of rows equal to 'maximumRowCount' property int popupHeight = getPopupHeightForRowCount(comboBox.getMaximumRowCount()); - list.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); - super.setPopupSize(cbBounds.width, popupHeight); + scroller.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); + pack(); // Highlight selected item in the combo box's drop down list if (comboBox.getSelectedIndex() != -1) @@ -478,6 +480,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected void configureScroller() { + scroller.setBorder(null); scroller.getViewport().setView(list); scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } @@ -488,6 +491,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected void configurePopup() { + setBorder(BorderFactory.createLineBorder(Color.BLACK)); // initialize list that will be used to display combo box's items this.list = createList(); ((JLabel) list.getCellRenderer()).setHorizontalAlignment(SwingConstants.LEFT); @@ -714,7 +718,11 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup protected void updateListBoxSelectionForEvent(MouseEvent anEvent, boolean shouldScroll) { - // FIXME: Need to implement + // TODO: We need to handle the shouldScroll parameter somehow. + int index = list.locationToIndex(anEvent.getPoint()); + // Check for valid index. + if (index >= 0) + list.setSelectedIndex(index); } /** @@ -732,6 +740,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected InvocationMouseHandler() { + // Nothing to do here. } /** @@ -744,7 +753,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup public void mousePressed(MouseEvent e) { if (comboBox.isEnabled()) - togglePopup(); + togglePopup(); } /** @@ -768,15 +777,15 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup // then change selection and close popup if (! (releasedComponent instanceof JComboBox)) { - // List model contains the item over which mouse is released, - // since it is updated every time the mouse is moved over a different - // item in the list. Now that the mouse is released we need to - // update model of the combo box as well. - comboBox.setSelectedIndex(list.getSelectedIndex()); - - if (isAutoScrolling) - stopAutoScrolling(); - hide(); + // List model contains the item over which mouse is released, + // since it is updated every time the mouse is moved over a different + // item in the list. Now that the mouse is released we need to + // update model of the combo box as well. + comboBox.setSelectedIndex(list.getSelectedIndex()); + + if (isAutoScrolling) + stopAutoScrolling(); + hide(); } } } @@ -792,6 +801,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected InvocationMouseMotionHandler() { + // Nothing to do here. } /** @@ -868,6 +878,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected ItemHandler() { + // Nothing to do here. } /** @@ -877,6 +888,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ public void itemStateChanged(ItemEvent e) { + // TODO: What should be done here? } } @@ -890,16 +902,20 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListMouseHandler() { + // Nothing to do here. } public void mousePressed(MouseEvent e) { + // TODO: What should be do here? } public void mouseReleased(MouseEvent anEvent) { int index = list.locationToIndex(anEvent.getPoint()); - comboBox.setSelectedIndex(index); + // Check for valid index. + if (index >= 0) + comboBox.setSelectedIndex(index); hide(); } } @@ -913,15 +929,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListMouseMotionHandler() { + // Nothing to do here. } public void mouseMoved(MouseEvent anEvent) { - // Highlight list cells over which the mouse is located. - // This changes list model, but has no effect on combo box's data model - int index = list.locationToIndex(anEvent.getPoint()); - list.setSelectedIndex(index); - list.repaint(); + updateListBoxSelectionForEvent(anEvent, false); } } @@ -934,6 +947,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected PropertyChangeHandler() { + // Nothing to do here. } public void propertyChange(PropertyChangeEvent e) @@ -1009,18 +1023,22 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { public ListDataHandler() { + // Nothing to do here. } public void contentsChanged(ListDataEvent e) { + // Nothing to do here. } public void intervalAdded(ListDataEvent e) { + // Nothing to do here. } public void intervalRemoved(ListDataEvent e) { + // Nothing to do here. } } @@ -1032,10 +1050,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListSelectionHandler() { + // Nothing to do here. } public void valueChanged(ListSelectionEvent e) { + // Nothing to do here. } } @@ -1046,10 +1066,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { public InvocationKeyHandler() { + // Nothing to do here. } public void keyReleased(KeyEvent e) { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java index 561b497f1c3..3f8730249da 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java @@ -365,6 +365,7 @@ public class BasicDesktopIconUI extends DesktopIconUI */ public BasicDesktopIconUI() { + // Nothing to do here. } /** @@ -585,6 +586,7 @@ public class BasicDesktopIconUI extends DesktopIconUI } catch (PropertyVetoException pve) { + // We do nothing if the attempt has been vetoed. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java index b59261b17f3..4116858da9d 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.beans.PropertyVetoException; @@ -49,10 +50,10 @@ import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.KeyStroke; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.DesktopPaneUI; +import javax.swing.plaf.UIResource; /** * This class is the UI delegate for JDesktopPane for the Basic look and feel. @@ -74,13 +75,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempts has been vetoed. + } } } @@ -112,13 +114,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setMaximum(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setMaximum(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempts has been vetoed. + } } } @@ -150,13 +153,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setIcon(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setIcon(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } @@ -236,16 +240,17 @@ public class BasicDesktopPaneUI extends DesktopPaneUI JInternalFrame frame = desktop.getSelectedFrame(); if (frame != null) { - try - { - if (frame.isIcon()) - frame.setIcon(false); - else if (frame.isMaximum()) - frame.setMaximum(false); - } - catch (PropertyVetoException pve) - { - } + try + { + if (frame.isIcon()) + frame.setIcon(false); + else if (frame.isMaximum()) + frame.setMaximum(false); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } @@ -304,6 +309,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI */ public BasicDesktopPaneUI() { + // Nothing to do here. } /** @@ -361,9 +367,9 @@ public class BasicDesktopPaneUI extends DesktopPaneUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - desktop.setBackground(defaults.getColor("desktop")); + Color bg = desktop.getBackground(); + if (bg == null || bg instanceof UIResource) + desktop.setBackground(UIManager.getColor("desktop")); } /** @@ -381,7 +387,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI protected void installKeyboardActions() { // FIXME: create actions and keystrokes. - registerKeyboardAction(); + registerKeyboardActions(); } /** @@ -405,7 +411,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI * This method registers the actions to the appropriate Action and Input * maps. */ - protected void registerKeyboardAction() + protected void registerKeyboardActions() { // FIXME: Do the binding. // XXX: the gtk windows tend to intercept a lot of the diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java index f74e9229893..9c639656545 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -48,7 +48,6 @@ import java.awt.Point; import java.awt.Polygon; import java.awt.Window; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; @@ -60,9 +59,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Hashtable; + import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JButton; @@ -92,25 +91,31 @@ import javax.swing.plaf.FileChooserUI; /** - * DOCUMENT ME! + * A UI delegate for the {@link JFileChooser} component under the + * {@link BasicLookAndFeel}. */ public class BasicFileChooserUI extends FileChooserUI { /** - * DOCUMENT ME! + * A file filter that accepts all files. */ protected class AcceptAllFileFilter extends FileFilter { + /** + * Creates a new instance. + */ public AcceptAllFileFilter() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Returns <code>true</code> always, as all files are accepted by this + * filter. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return Always <code>true</code>. */ public boolean accept(File f) { @@ -118,9 +123,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a description for this filter. * - * @return DOCUMENT ME! + * @return A description for the file filter. */ public String getDescription() { @@ -129,7 +134,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Handles a user action to approve the dialog selection. + * + * @see BasicFileChooserUI#getApproveSelectionAction() */ protected class ApproveSelectionAction extends AbstractAction { @@ -138,50 +145,55 @@ public class BasicFileChooserUI extends FileChooserUI */ protected ApproveSelectionAction() { + // Nothing to do here. } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Sets the current selection and closes the dialog. + * + * @param e the action event. */ public void actionPerformed(ActionEvent e) { - Object obj = filelist.getSelectedValue(); + Object obj = new String(parentPath + entry.getText()); if (obj != null) { - File f = filechooser.getFileSystemView().createFileObject(obj - .toString()); - if (filechooser.isTraversable(f) && - filechooser.getFileSelectionMode() == JFileChooser.FILES_ONLY) + File f = filechooser.getFileSystemView().createFileObject( + obj.toString()); + if (filechooser.isTraversable(f) + && filechooser.isDirectorySelectionEnabled()) filechooser.setCurrentDirectory(f); - else - { - filechooser.setSelectedFile(f); - filechooser.approveSelection(); - closeDialog(); - } + else + { + filechooser.setSelectedFile(f); + filechooser.approveSelection(); + closeDialog(); + } } } } /** - * DOCUMENT ME! + * Provides presentation information about files and directories. */ protected class BasicFileView extends FileView { - /** DOCUMENT ME! */ + /** Storage for cached icons. */ protected Hashtable iconCache = new Hashtable(); + /** + * Creates a new instance. + */ public BasicFileView() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Adds an icon to the cache, associating it with the given file/directory. * - * @param f DOCUMENT ME! - * @param i DOCUMENT ME! + * @param f the file/directory. + * @param i the icon. */ public void cacheIcon(File f, Icon i) { @@ -189,7 +201,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Clears the icon cache. */ public void clearIconCache() { @@ -197,11 +209,12 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Retrieves the icon associated with the specified file/directory, if + * there is one. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The cached icon (or <code>null</code>). */ public Icon getCachedIcon(File f) { @@ -209,11 +222,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a description of the given file/directory. In this + * implementation, the description is the same as the name returned by + * {@link #getName(File)}. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return A description of the given file/directory. */ public String getDescription(File f) { @@ -221,11 +236,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns an icon appropriate for the given file or directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return An icon. */ public Icon getIcon(File f) { @@ -241,11 +256,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the name for the given file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The name of the file/directory. */ public String getName(File f) { @@ -253,11 +268,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a localised description for the type of file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return A type description for the given file/directory. */ public String getTypeDescription(File f) { @@ -268,34 +283,38 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns {@link Boolean#TRUE} if the given file/directory is hidden, + * and {@link Boolean#FALSE} otherwise. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return {@link Boolean#TRUE} or {@link Boolean#FALSE}. */ public Boolean isHidden(File f) { - return new Boolean(filechooser.getFileSystemView().isHiddenFile(f)); + return Boolean.valueOf(filechooser.getFileSystemView().isHiddenFile(f)); } } /** - * DOCUMENT ME! + * Handles an action to cancel the file chooser. + * + * @see BasicFileChooserUI#getCancelSelectionAction() */ protected class CancelSelectionAction extends AbstractAction { /** - * Creates a new CancelSelectionAction object. + * Creates a new <code>CancelSelectionAction</code> object. */ protected CancelSelectionAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Cancels the selection and closes the dialog. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -305,21 +324,25 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action to handle changes to the parent directory (for example, via + * a click on the "up folder" button). + * + * @see BasicFileChooserUI#getChangeToParentDirectoryAction() */ protected class ChangeToParentDirectoryAction extends AbstractAction { /** - * Creates a new ChangeToParentDirectoryAction object. + * Creates a new <code>ChangeToParentDirectoryAction</code> object. */ protected ChangeToParentDirectoryAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Handles the action event. * - * @param e DOCUMENT ME! + * @param e the action event. */ public void actionPerformed(ActionEvent e) { @@ -330,11 +353,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * A mouse listener that handles double-click events. + * + * @see BasicFileChooserUI#createDoubleClickListener(JFileChooser, JList) */ protected class DoubleClickListener extends MouseAdapter { - /** DOCUMENT ME! */ + /** A timer. */ private Timer timer = null; /** DOCUMENT ME! */ @@ -358,54 +383,57 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Handles a mouse click event. + * + * @param e the event. */ public void mouseClicked(MouseEvent e) { if (list.getSelectedValue() == null) - return; + return; FileSystemView fsv = filechooser.getFileSystemView(); if (timer.isRunning() && list.getSelectedValue().toString().equals(lastSelected.toString())) { - File f = fsv.createFileObject(lastSelected.toString()); - timer.stop(); - if (filechooser.isTraversable(f)) - { - filechooser.setCurrentDirectory(f); - filechooser.rescanCurrentDirectory(); - } - else - { - filechooser.setSelectedFile(f); - filechooser.approveSelection(); - closeDialog(); - } + File f = fsv.createFileObject(lastSelected.toString()); + timer.stop(); + if (filechooser.isTraversable(f)) + { + filechooser.setCurrentDirectory(f); + filechooser.rescanCurrentDirectory(); + } + else + { + filechooser.setSelectedFile(f); + filechooser.approveSelection(); + closeDialog(); + } } else { - File f = fsv.createFileObject(list.getSelectedValue().toString()); - if (filechooser.isTraversable(f)) - { - setDirectorySelected(true); - setDirectory(f); - } - else - { - setDirectorySelected(false); - setDirectory(null); - } - lastSelected = list.getSelectedValue().toString(); - timer.restart(); + String path = list.getSelectedValue().toString(); + File f = fsv.createFileObject(path); + if (filechooser.isTraversable(f)) + { + setDirectorySelected(true); + setDirectory(f); + } + else + { + setDirectorySelected(false); + setDirectory(null); + } + lastSelected = path; + parentPath = path.substring(0, path.lastIndexOf("/") + 1); + entry.setText(path.substring(path.lastIndexOf("/") + 1)); + timer.restart(); } } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Handles a mouse entered event (NOT IMPLEMENTED). + * + * @param e the mouse event. */ public void mouseEntered(MouseEvent e) { @@ -414,21 +442,26 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action that changes the file chooser to display the user's home + * directory. + * + * @see BasicFileChooserUI#getGoHomeAction() */ protected class GoHomeAction extends AbstractAction { /** - * Creates a new GoHomeAction object. + * Creates a new <code>GoHomeAction</code> object. */ protected GoHomeAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Sets the directory to the user's home directory, and repaints the + * file chooser component. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -440,21 +473,24 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action that handles the creation of a new folder/directory. + * + * @see BasicFileChooserUI#getNewFolderAction() */ protected class NewFolderAction extends AbstractAction { /** - * Creates a new NewFolderAction object. + * Creates a new <code>NewFolderAction</code> object. */ protected NewFolderAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Handles the event by creating a new folder. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -473,15 +509,18 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * A listener for selection events in the file list. + * + * @see BasicFileChooserUI#createListSelectionListener(JFileChooser) */ protected class SelectionListener implements ListSelectionListener { /** - * Creates a new SelectionListener object. + * Creates a new <code>SelectionListener</code> object. */ protected SelectionListener() { + // Nothing to do here. } /** @@ -504,6 +543,8 @@ public class BasicFileChooserUI extends FileChooserUI /** * DOCUMENT ME! + * + * @see BasicFileChooserUI#getUpdateAction() */ protected class UpdateAction extends AbstractAction { @@ -512,28 +553,30 @@ public class BasicFileChooserUI extends FileChooserUI */ protected UpdateAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * NOT YET IMPLEMENTED. * - * @param e DOCUMENT ME! + * @param e the action event. */ public void actionPerformed(ActionEvent e) { + // FIXME: implement this } } - /** DOCUMENT ME! */ + /** The localised mnemonic for the cancel button. */ protected int cancelButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the cancel button. */ protected String cancelButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the cancel button. */ protected String cancelButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing a computer. */ protected Icon computerIcon = new Icon() { public int getIconHeight() @@ -548,10 +591,11 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** An icon for the "details view" button. */ protected Icon detailsViewIcon = new Icon() { public int getIconHeight() @@ -580,7 +624,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon representing a directory. */ protected Icon directoryIcon = new Icon() { public int getIconHeight() @@ -619,16 +663,16 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** The localised Mnemonic for the open button. */ protected int directoryOpenButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the open button. */ protected String directoryOpenButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the open button. */ protected String directoryOpenButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing a file. */ protected Icon fileIcon = new Icon() { public int getIconHeight() @@ -668,7 +712,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon representing a floppy drive. */ protected Icon floppyDriveIcon = new Icon() { public int getIconHeight() @@ -683,10 +727,11 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** An icon representing a hard drive. */ protected Icon hardDriveIcon = new Icon() { public int getIconHeight() @@ -701,19 +746,20 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "help" button. */ protected int helpButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "help" button. */ protected String helpButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the help button. */ protected String helpButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing the user's home folder. */ protected Icon homeFolderIcon = new Icon() { public int getIconHeight() @@ -753,7 +799,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon for the "list view" button. */ protected Icon listViewIcon = new Icon() { public int getIconHeight() @@ -795,37 +841,37 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon for the "new folder" button. */ protected Icon newFolderIcon = directoryIcon; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "open" button. */ protected int openButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "open" button. */ protected String openButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the "open" button. */ protected String openButtonToolTipText; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "save" button. */ protected int saveButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "save" button. */ protected String saveButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the save button. */ protected String saveButtonToolTipText; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "update" button. */ protected int updateButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "update" button. */ protected String updateButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the "update" button. */ protected String updateButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon for the "up folder" button. */ protected Icon upFolderIcon = new Icon() { public int getIconHeight() @@ -876,77 +922,84 @@ public class BasicFileChooserUI extends FileChooserUI // -- begin private, but package local since used in inner classes -- + /** The file chooser component represented by this UI delegate. */ JFileChooser filechooser; - /** DOCUMENT ME! */ + /** The file list. */ JList filelist; - /** DOCUMENT ME! */ + /** The combo box used to display/select file filters. */ JComboBox filters; - /** DOCUMENT ME! */ + /** The model for the directory list. */ BasicDirectoryModel model; - /** DOCUMENT ME! */ + /** The file filter for all files. */ FileFilter acceptAll = new AcceptAllFileFilter(); - /** DOCUMENT ME! */ + /** The default file view. */ FileView fv = new BasicFileView(); - /** DOCUMENT ME! */ + /** The icon size. */ static final int ICON_SIZE = 24; - /** DOCUMENT ME! */ + /** A combo box for display/selection of parent directories. */ JComboBox parents; - /** DOCUMENT ME! */ + /** The current file name. */ String filename; - /** DOCUMENT ME! */ + /** The accept (open/save) button. */ JButton accept; - /** DOCUMENT ME! */ + /** The cancel button. */ JButton cancel; - /** DOCUMENT ME! */ + /** The button to move up to the parent directory. */ JButton upFolderButton; - /** DOCUMENT ME! */ + /** The button to create a new directory. */ JButton newFolderButton; - /** DOCUMENT ME! */ + /** The button to move to the user's home directory. */ JButton homeFolderButton; - /** DOCUMENT ME! */ + /** An optional accessory panel. */ JPanel accessoryPanel; - /** DOCUMENT ME! */ + /** A property change listener. */ PropertyChangeListener propertyChangeListener; - /** DOCUMENT ME! */ + /** The text describing the filter for "all files". */ String acceptAllFileFilterText; - /** DOCUMENT ME! */ + /** The text describing a directory type. */ String dirDescText; - /** DOCUMENT ME! */ + /** The text describing a file type. */ String fileDescText; - /** DOCUMENT ME! */ + /** Is a directory selected? */ boolean dirSelected = false; - /** DOCUMENT ME! */ + /** The current directory. */ File currDir = null; + // FIXME: describe what is contained in the bottom panel + /** The bottom panel. */ JPanel bottomPanel; - - /** DOCUMENT ME! */ + + /** The close panel. */ JPanel closePanel; + /** Text box that displays file name */ + JTextField entry; + + /** Current parent path */ + String parentPath; + // -- end private -- - private class ListLabelRenderer - extends JLabel - implements ListCellRenderer + private class ListLabelRenderer extends JLabel implements ListCellRenderer { /** DOCUMENT ME! */ final Color selected = new Color(153, 204, 255); @@ -988,45 +1041,8 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Closes the dialog. */ - public class CBLabelRenderer extends JLabel implements ListCellRenderer - { - /** - * Creates a new CBLabelRenderer object. - */ - public CBLabelRenderer() - { - super(); - setOpaque(true); - } - - /** - * DOCUMENT ME! - * - * @param list DOCUMENT ME! - * @param value DOCUMENT ME! - * @param index DOCUMENT ME! - * @param isSelected DOCUMENT ME! - * @param cellHasFocus DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public Component getListCellRendererComponent(JList list, Object value, - int index, - boolean isSelected, - boolean cellHasFocus) - { - setHorizontalAlignment(SwingConstants.LEFT); - setIcon(directoryIcon); - setText(value.toString()); - setForeground(Color.BLACK); - setBackground(Color.WHITE); - - return this; - } - } - void closeDialog() { Window owner = SwingUtilities.windowForComponent(filechooser); @@ -1035,9 +1051,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * Creates a new BasicFileChooserUI object. + * Creates a new <code>BasicFileChooserUI</code> object. * - * @param b DOCUMENT ME! + * @param b the file chooser component. */ public BasicFileChooserUI(JFileChooser b) { @@ -1045,11 +1061,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a UI delegate for the given component. * - * @param c DOCUMENT ME! + * @param c the component (should be a {@link JFileChooser}). * - * @return DOCUMENT ME! + * @return A new UI delegate. */ public static ComponentUI createUI(JComponent c) { @@ -1057,28 +1073,32 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param c DOCUMENT ME! + * Installs the UI for the specified component. + * + * @param c the component (should be a {@link JFileChooser}). */ public void installUI(JComponent c) { if (c instanceof JFileChooser) { - JFileChooser fc = (JFileChooser) c; - fc.resetChoosableFileFilters(); - createModel(); - clearIconCache(); - installDefaults(fc); - installComponents(fc); - installListeners(fc); + JFileChooser fc = (JFileChooser) c; + fc.resetChoosableFileFilters(); + createModel(); + clearIconCache(); + installDefaults(fc); + installComponents(fc); + installListeners(fc); + + Object path = filechooser.getCurrentDirectory(); + if (path != null) + parentPath = path.toString().substring(path.toString().lastIndexOf("/")); } } /** - * DOCUMENT ME! - * - * @param c DOCUMENT ME! + * Uninstalls this UI from the given component. + * + * @param c the component (should be a {@link JFileChooser}). */ public void uninstallUI(JComponent c) { @@ -1185,16 +1205,16 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and install the subcomponents for the file chooser. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void installComponents(JFileChooser fc) { JLabel look = new JLabel("Look In:"); parents = new JComboBox(); - parents.setRenderer(new CBLabelRenderer()); + parents.setRenderer(new BasicComboBoxRenderer()); boxEntries(); look.setLabelFor(parents); JPanel parentsPanel = new JPanel(); @@ -1226,9 +1246,9 @@ public class BasicFileChooserUI extends FileChooserUI buttonPanel.add(detailsViewButton); JPanel topPanel = new JPanel(); - topPanel.setLayout(new java.awt.FlowLayout()); + parentsPanel.add(buttonPanel); + topPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); topPanel.add(parentsPanel); - topPanel.add(buttonPanel); accessoryPanel = new JPanel(); if (filechooser.getAccessory() != null) @@ -1260,7 +1280,7 @@ public class BasicFileChooserUI extends FileChooserUI JLabel fileNameLabel = new JLabel("File Name:"); JLabel fileTypesLabel = new JLabel("Files of Type:"); - JTextField entry = new JTextField(); + entry = new JTextField(); filters = new JComboBox(); filterEntries(); @@ -1309,9 +1329,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the components from the file chooser. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void uninstallComponents(JFileChooser fc) { @@ -1327,9 +1347,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the listeners required by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installListeners(JFileChooser fc) { @@ -1349,9 +1369,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the listeners previously installed by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallListeners(JFileChooser fc) { @@ -1360,9 +1380,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the defaults for this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installDefaults(JFileChooser fc) { @@ -1371,9 +1391,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the defaults previously added by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallDefaults(JFileChooser fc) { @@ -1382,9 +1402,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the icons for this UI delegate (NOT YET IMPLEMENTED). * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installIcons(JFileChooser fc) { @@ -1392,9 +1412,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the icons previously added by this UI delegate (NOT YET + * IMPLEMENTED). * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallIcons(JFileChooser fc) { @@ -1402,9 +1423,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the strings used by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installStrings(JFileChooser fc) { @@ -1432,9 +1453,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the strings previously added by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallStrings(JFileChooser fc) { @@ -1460,7 +1481,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new directory model. */ protected void createModel() { @@ -1468,9 +1489,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the directory model. * - * @return DOCUMENT ME! + * @return The directory model. */ public BasicDirectoryModel getModel() { @@ -1478,115 +1499,131 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param fc DOCUMENT ME! - * - * @return DOCUMENT ME! + * Creates a listener to handle changes to the properties of the given + * file chooser component. + * + * @param fc the file chooser component. + * + * @return A new listener. */ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent e) { - public void propertyChange(PropertyChangeEvent e) - { - // FIXME: Multiple file selection waiting on JList multiple selection bug. - if (e.getPropertyName().equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) - { - if (filechooser.getSelectedFile() == null) - setFileName(null); - else - setFileName(filechooser.getSelectedFile().toString()); - int index = -1; - File file = filechooser.getSelectedFile(); - for (index = 0; index < model.getSize(); index++) - if (((File) model.getElementAt(index)).equals(file)) - break; - if (index == -1) - return; - filelist.setSelectedIndex(index); - filelist.ensureIndexIsVisible(index); - filelist.revalidate(); - filelist.repaint(); - } - else if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) - { - filelist.clearSelection(); - filelist.revalidate(); - filelist.repaint(); - setDirectorySelected(false); - setDirectory(filechooser.getCurrentDirectory()); - boxEntries(); - } - else if (e.getPropertyName().equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) - filterEntries(); - else if (e.getPropertyName().equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) - { - Window owner = SwingUtilities.windowForComponent(filechooser); - if (owner instanceof JDialog) - ((JDialog) owner).setTitle(getDialogTitle(filechooser)); - accept.setText(getApproveButtonText(filechooser)); - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - } - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) - accept.setText(getApproveButtonText(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) - { - if (filechooser.getControlButtonsAreShown()) - { - GridBagConstraints c = new GridBagConstraints(); - c.gridy = 1; - bottomPanel.add(filters, c); - - c.fill = GridBagConstraints.BOTH; - c.gridy = 2; - c.anchor = GridBagConstraints.EAST; - bottomPanel.add(closePanel, c); - bottomPanel.revalidate(); - bottomPanel.repaint(); - bottomPanel.doLayout(); - } - else - bottomPanel.remove(closePanel); - } - else if (e.getPropertyName().equals(JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) - { - if (filechooser.isAcceptAllFileFilterUsed()) - filechooser.addChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - else - filechooser.removeChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - } - else if (e.getPropertyName().equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) - { - JComponent old = (JComponent) e.getOldValue(); - if (old != null) - getAccessoryPanel().remove(old); - JComponent newval = (JComponent) e.getNewValue(); - if (newval != null) - getAccessoryPanel().add(newval); - } - if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) - rescanCurrentDirectory(filechooser); - - filechooser.revalidate(); - filechooser.repaint(); - } - }; + // FIXME: Multiple file selection waiting on JList multiple selection + // bug. + if (e.getPropertyName().equals( + JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) + { + if (filechooser.getSelectedFile() == null) + setFileName(null); + else + setFileName(filechooser.getSelectedFile().toString()); + int index = -1; + File file = filechooser.getSelectedFile(); + for (index = 0; index < model.getSize(); index++) + if (((File) model.getElementAt(index)).equals(file)) + break; + if (index == -1) + return; + filelist.setSelectedIndex(index); + filelist.ensureIndexIsVisible(index); + filelist.revalidate(); + filelist.repaint(); + } + else if (e.getPropertyName().equals( + JFileChooser.DIRECTORY_CHANGED_PROPERTY)) + { + filelist.clearSelection(); + filelist.revalidate(); + filelist.repaint(); + setDirectorySelected(false); + setDirectory(filechooser.getCurrentDirectory()); + boxEntries(); + } + else if (e.getPropertyName().equals( + JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + filterEntries(); + else if (e.getPropertyName().equals( + JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) + { + Window owner = SwingUtilities.windowForComponent(filechooser); + if (owner instanceof JDialog) + ((JDialog) owner).setTitle(getDialogTitle(filechooser)); + accept.setText(getApproveButtonText(filechooser)); + accept.setToolTipText(getApproveButtonToolTipText(filechooser)); + accept.setMnemonic(getApproveButtonMnemonic(filechooser)); + } + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) + accept.setText(getApproveButtonText(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) + accept.setToolTipText(getApproveButtonToolTipText(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) + accept.setMnemonic(getApproveButtonMnemonic(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) + { + if (filechooser.getControlButtonsAreShown()) + { + GridBagConstraints c = new GridBagConstraints(); + c.gridy = 1; + bottomPanel.add(filters, c); + + c.fill = GridBagConstraints.BOTH; + c.gridy = 2; + c.anchor = GridBagConstraints.EAST; + bottomPanel.add(closePanel, c); + bottomPanel.revalidate(); + bottomPanel.repaint(); + bottomPanel.doLayout(); + } + else + bottomPanel.remove(closePanel); + } + else if (e.getPropertyName().equals( + JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) + { + if (filechooser.isAcceptAllFileFilterUsed()) + filechooser.addChoosableFileFilter(getAcceptAllFileFilter(filechooser)); + else + filechooser.removeChoosableFileFilter(getAcceptAllFileFilter(filechooser)); + } + else if (e.getPropertyName().equals( + JFileChooser.ACCESSORY_CHANGED_PROPERTY)) + { + JComponent old = (JComponent) e.getOldValue(); + if (old != null) + getAccessoryPanel().remove(old); + JComponent newval = (JComponent) e.getNewValue(); + if (newval != null) + getAccessoryPanel().add(newval); + } + if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) + rescanCurrentDirectory(filechooser); + + filechooser.revalidate(); + filechooser.repaint(); + } + }; } /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * Returns the current file name. + * + * @return The current file name. */ public String getFileName() { @@ -1594,9 +1631,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the current directory name. * - * @return DOCUMENT ME! + * @return The directory name. + * + * @see #setDirectoryName(String) */ public String getDirectoryName() { @@ -1605,9 +1644,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the file name. * - * @param filename DOCUMENT ME! + * @param filename the file name. + * + * @see #getFileName() */ public void setFileName(String filename) { @@ -1615,9 +1656,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the directory name (NOT IMPLEMENTED). * - * @param dirname DOCUMENT ME! + * @param dirname the directory name. + * + * @see #getDirectoryName() */ public void setDirectoryName(String dirname) { @@ -1625,9 +1668,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Rescans the current directory. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void rescanCurrentDirectory(JFileChooser fc) { @@ -1636,10 +1679,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * NOT YET IMPLEMENTED. * - * @param fc DOCUMENT ME! - * @param f DOCUMENT ME! + * @param fc the file chooser. + * @param f the file. */ public void ensureFileIsVisible(JFileChooser fc, File f) { @@ -1647,9 +1690,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the {@link JFileChooser} component that this UI delegate + * represents. * - * @return DOCUMENT ME! + * @return The component represented by this UI delegate. */ public JFileChooser getFileChooser() { @@ -1657,9 +1701,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the optional accessory panel. * - * @return DOCUMENT ME! + * @return The optional accessory panel. */ public JPanel getAccessoryPanel() { @@ -1667,11 +1711,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns an approve (open or save) button for the dialog. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. * - * @return DOCUMENT ME! + * @return The button. */ public JButton getApproveButton(JFileChooser fc) { @@ -1682,11 +1726,14 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the tool tip text for the approve (open/save) button. This first + * checks the file chooser to see if a value has been explicitly set - if + * not, a default value appropriate for the type of file chooser is + * returned. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. * - * @return DOCUMENT ME! + * @return The tool tip text. */ public String getApproveButtonToolTipText(JFileChooser fc) { @@ -1699,7 +1746,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Clears the icon cache. */ public void clearIconCache() { @@ -1708,11 +1755,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new listener to handle selections in the file list. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return A new instance of {@link SelectionListener}. */ public ListSelectionListener createListSelectionListener(JFileChooser fc) { @@ -1720,12 +1767,12 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new listener to handle double-click events. * - * @param fc DOCUMENT ME! - * @param list DOCUMENT ME! + * @param fc the file chooser component. + * @param list the list. * - * @return DOCUMENT ME! + * @return A new instance of {@link DoubleClickListener}. */ protected MouseListener createDoubleClickListener(JFileChooser fc, JList list) { @@ -1733,9 +1780,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns <code>true</code> if a directory is selected, and + * <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. */ protected boolean isDirectorySelected() { @@ -1743,9 +1791,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the flag that indicates whether the current directory is selected. * - * @param selected DOCUMENT ME! + * @param selected the new flag value. */ protected void setDirectorySelected(boolean selected) { @@ -1753,9 +1801,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the current directory. * - * @return DOCUMENT ME! + * @return The current directory. */ protected File getDirectory() { @@ -1763,9 +1811,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the current directory. * - * @param f DOCUMENT ME! + * @param f the directory. */ protected void setDirectory(File f) { @@ -1773,11 +1821,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the "accept all" file filter. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return The "accept all" file filter. */ public FileFilter getAcceptAllFileFilter(JFileChooser fc) { @@ -1785,25 +1833,29 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the file view for the file chooser. This returns either the + * file view that has been explicitly set for the {@link JFileChooser}, or + * a default file view. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return The file view. + * + * @see JFileChooser#getFileView() */ public FileView getFileView(JFileChooser fc) { - if (fc.getFileView() != null) - return fc.getFileView(); return fv; } /** - * DOCUMENT ME! + * Returns the dialog title. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The dialog title. + * + * @see JFileChooser#getDialogTitle() */ public String getDialogTitle(JFileChooser fc) { @@ -1828,11 +1880,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the approve button mnemonic. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The approve button mnemonic. + * + * @see JFileChooser#getApproveButtonMnemonic() */ public int getApproveButtonMnemonic(JFileChooser fc) { @@ -1845,11 +1899,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the approve button text. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The approve button text. + * + * @see JFileChooser#getApproveButtonText() */ public String getApproveButtonText(JFileChooser fc) { @@ -1862,9 +1918,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "new folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link GoHomeAction}. */ public Action getNewFolderAction() { @@ -1872,9 +1929,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "home folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link GoHomeAction}. */ public Action getGoHomeAction() { @@ -1882,9 +1940,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "up folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link ChangeToParentDirectoryAction}. */ public Action getChangeToParentDirectoryAction() { @@ -1892,9 +1951,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "approve" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link ApproveSelectionAction}. */ public Action getApproveSelectionAction() { @@ -1902,9 +1962,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "cancel" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link CancelSelectionAction}. */ public Action getCancelSelectionAction() { @@ -1912,9 +1973,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new instance of {@link UpdateAction}. * - * @return DOCUMENT ME! + * @return An action. */ public Action getUpdateAction() { diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java index 3abd76fe2d3..9c7f1c4c5d1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java @@ -1,5 +1,5 @@ /* BasicFormattedTextFieldUI.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; /** @@ -48,6 +49,7 @@ public class BasicFormattedTextFieldUI extends BasicTextFieldUI { public BasicFormattedTextFieldUI() { + // Nothing to do here. } public static ComponentUI createUI(JComponent c) @@ -55,6 +57,11 @@ public class BasicFormattedTextFieldUI extends BasicTextFieldUI return new BasicFormattedTextFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "FormattedTextField" + */ protected String getPropertyPrefix() { return "FormattedTextField"; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java index 757ac47c903..068de345bec 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java @@ -71,6 +71,7 @@ public class BasicGraphicsUtils */ public BasicGraphicsUtils() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java index 56a67b02935..6debd649509 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java @@ -41,7 +41,6 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; -import java.awt.Polygon; import java.io.Serializable; import javax.swing.Icon; @@ -241,34 +240,33 @@ public class BasicIconFactory implements Serializable { return new DummyIcon(); } + + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a + * sub menu. + * + * @return The icon. + */ public static Icon getMenuArrowIcon() { return new Icon() { public int getIconHeight() { - return 12; + return 8; } - public int getIconWidth() { - return 12; + return 4; } - public void paintIcon(Component c, Graphics g, int x, int y) { - g.translate(x, y); - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.fillPolygon(new Polygon(new int[] { 3, 9, 3 }, - new int[] { 2, 6, 10 }, - 3)); - + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); g.setColor(saved); - g.translate(-x, -y); } }; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java index cc262948ded..73d3e6173d3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java @@ -84,6 +84,14 @@ public class BasicInternalFrameTitlePane extends JComponent public class CloseAction extends AbstractAction { /** + * Creates a new action. + */ + public CloseAction() + { + super("Close"); + } + + /** * This method is called when something closes the JInternalFrame. * * @param e The ActionEvent. @@ -92,13 +100,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isClosable()) { - try - { - frame.setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -113,6 +122,14 @@ public class BasicInternalFrameTitlePane extends JComponent public class IconifyAction extends AbstractAction { /** + * Creates a new action. + */ + public IconifyAction() + { + super("Minimize"); + } + + /** * This method is called when the user wants to iconify the * JInternalFrame. * @@ -122,13 +139,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isIconifiable() && ! frame.isIcon()) { - try - { - frame.setIcon(true); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setIcon(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -143,6 +161,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class MaximizeAction extends AbstractAction { /** + * Creates a new action. + */ + public MaximizeAction() + { + super("Maximize"); + } + /** * This method is called when the user wants to maximize the * JInternalFrame. * @@ -152,13 +177,14 @@ public class BasicInternalFrameTitlePane extends JComponent { try { - if (frame.isMaximizable() && ! frame.isMaximum()) - frame.setMaximum(true); - else if (frame.isMaximum()) - frame.setMaximum(false); + if (frame.isMaximizable() && ! frame.isMaximum()) + frame.setMaximum(true); + else if (frame.isMaximum()) + frame.setMaximum(false); } catch (PropertyVetoException pve) { + // We do nothing if the attempt has been vetoed. } } } @@ -173,6 +199,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class MoveAction extends AbstractAction { /** + * Creates a new action. + */ + public MoveAction() + { + super("Move"); + } + /** * This method is called when the user wants to drag the JInternalFrame. * * @param e The ActionEvent. @@ -194,6 +227,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class RestoreAction extends AbstractAction { /** + * Creates a new action. + */ + public RestoreAction() + { + super("Restore"); + } + /** * This method is called when the user wants to restore the * JInternalFrame. * @@ -203,13 +243,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isMaximum()) { - try - { - frame.setMaximum(false); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setMaximum(false); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -224,6 +265,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class SizeAction extends AbstractAction { /** + * Creates a new action. + */ + public SizeAction() + { + super("Size"); + } + /** * This method is called when the user wants to resize the JInternalFrame. * * @param e The ActionEvent. @@ -377,24 +425,26 @@ public class BasicInternalFrameTitlePane extends JComponent int loc = width + insets.left - 1; int top = insets.top + 1; - int buttonWidth = height - 2; int buttonHeight = height - 4; if (closeButton.isVisible()) { - loc -= buttonWidth + 2; - closeButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = closeIcon.getIconWidth(); + loc -= buttonWidth + 2; + closeButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (maxButton.isVisible()) { - loc -= buttonWidth + 2; - maxButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = maxIcon.getIconWidth(); + loc -= buttonWidth + 2; + maxButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (iconButton.isVisible()) { - loc -= buttonWidth + 2; - iconButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = iconIcon.getIconWidth(); + loc -= buttonWidth + 2; + iconButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (title != null) @@ -435,6 +485,7 @@ public class BasicInternalFrameTitlePane extends JComponent */ public void removeLayoutComponent(Component c) { + // Nothing to do here. } } @@ -466,6 +517,7 @@ public class BasicInternalFrameTitlePane extends JComponent // These buttons cannot be given focus. return false; } + } /** The action command for the Close action. */ @@ -522,6 +574,9 @@ public class BasicInternalFrameTitlePane extends JComponent /** The icon displayed in the iconify button. */ protected Icon iconIcon = BasicIconFactory.createEmptyFrameIcon(); + /** The icon displayed in the close button. */ + protected Icon closeIcon; + /** The JInternalFrame that this TitlePane is used in. */ protected JInternalFrame frame; @@ -645,7 +700,7 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void installListeners() { - propertyChangeListener = new PropertyChangeHandler(); + propertyChangeListener = createPropertyChangeListener(); frame.addPropertyChangeListener(propertyChangeListener); } @@ -663,14 +718,17 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void installDefaults() { - // FIXME: move icons to defaults. UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - setFont(defaults.getFont("InternalFrame.titleFont")); + title.setFont(defaults.getFont("InternalFrame.titleFont")); selectedTextColor = defaults.getColor("InternalFrame.activeTitleForeground"); selectedTitleColor = defaults.getColor("InternalFrame.activeTitleBackground"); notSelectedTextColor = defaults.getColor("InternalFrame.inactiveTitleForeground"); notSelectedTitleColor = defaults.getColor("InternalFrame.inactiveTitleBackground"); + + closeIcon = UIManager.getIcon("InternalFrame.closeIcon"); + iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon"); + maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon"); } /** @@ -683,6 +741,10 @@ public class BasicInternalFrameTitlePane extends JComponent selectedTitleColor = null; notSelectedTextColor = null; notSelectedTitleColor = null; + + closeIcon = null; + iconIcon = null; + maxIcon = null; } /** @@ -691,12 +753,15 @@ public class BasicInternalFrameTitlePane extends JComponent protected void createButtons() { closeButton = new PaneButton(closeAction); + closeButton.setText(null); if (!frame.isClosable()) closeButton.setVisible(false); iconButton = new PaneButton(iconifyAction); + iconButton.setText(null); if (!frame.isIconifiable()) iconButton.setVisible(false); maxButton = new PaneButton(maximizeAction); + maxButton.setText(null); if (!frame.isMaximizable()) maxButton.setVisible(false); } @@ -706,15 +771,12 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void setButtonIcons() { - Icon icon = UIManager.getIcon("InternalFrame.closeIcon"); - if (icon != null) - closeButton.setIcon(icon); - icon = UIManager.getIcon("InternalFrame.iconifyIcon"); - if (icon != null) - iconButton.setIcon(icon); - icon = UIManager.getIcon("InternalFrame.maximizeIcon"); - if (icon != null) - maxButton.setIcon(icon); + if (closeIcon != null && closeButton != null) + closeButton.setIcon(closeIcon); + if (iconIcon != null && iconButton != null) + iconButton.setIcon(iconIcon); + if (maxIcon != null && maxButton != null) + maxButton.setIcon(maxIcon); } /** @@ -816,11 +878,12 @@ public class BasicInternalFrameTitlePane extends JComponent public void paintComponent(Graphics g) { paintTitleBackground(g); - Font f = g.getFont(); - FontMetrics fm = g.getFontMetrics(f); if (frame.getTitle() != null && title != null) { Color saved = g.getColor(); + Font f = title.getFont(); + g.setFont(f); + FontMetrics fm = g.getFontMetrics(f); if (frame.isSelected()) g.setColor(selectedTextColor); else diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index 8f76ea0cc19..d9dadda688a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -56,20 +56,17 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; -import javax.swing.BorderFactory; import javax.swing.DefaultDesktopManager; import javax.swing.DesktopManager; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.AbstractBorder; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; import javax.swing.event.InternalFrameEvent; import javax.swing.event.InternalFrameListener; import javax.swing.event.MouseInputAdapter; @@ -202,67 +199,66 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void mouseDragged(MouseEvent e) { - // If the frame is maximized, there is nothing that + // If the frame is maximized, there is nothing that // can be dragged around. if (frame.isMaximum()) - return; + return; DesktopManager dm = getDesktopManager(); Rectangle b = frame.getBounds(); Dimension min = frame.getMinimumSize(); if (min == null) - min = new Dimension(0, 0); + min = new Dimension(0, 0); Insets insets = frame.getInsets(); int x = e.getX(); int y = e.getY(); if (e.getSource() == frame && frame.isResizable()) { - switch (direction) - { - case NORTH: - cacheRect.setBounds(b.x, - Math.min(b.y + y, b.y + b.height - - min.height), b.width, b.height - - y); - break; - case NORTH_EAST: - cacheRect.setBounds(b.x, - Math.min(b.y + y, b.y + b.height - - min.height), x, b.height - y); - break; - case EAST: - cacheRect.setBounds(b.x, b.y, x, b.height); - break; - case SOUTH_EAST: - cacheRect.setBounds(b.x, b.y, x, y); - break; - case SOUTH: - cacheRect.setBounds(b.x, b.y, b.width, y); - break; - case SOUTH_WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - b.y, b.width - x, y); - break; - case WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - b.y, b.width - x, b.height); - break; - case NORTH_WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - Math.min(b.y + y, b.y + b.height - - min.height), b.width - x, - b.height - y); - break; - } - dm.resizeFrame(frame, cacheRect.x, cacheRect.y, - Math.max(min.width, cacheRect.width), - Math.max(min.height, cacheRect.height)); + switch (direction) + { + case NORTH: + cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height + - min.height), + b.width, b.height - y); + break; + case NORTH_EAST: + cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height + - min.height), x, + b.height - y); + break; + case EAST: + cacheRect.setBounds(b.x, b.y, x, b.height); + break; + case SOUTH_EAST: + cacheRect.setBounds(b.x, b.y, x, y); + break; + case SOUTH: + cacheRect.setBounds(b.x, b.y, b.width, y); + break; + case SOUTH_WEST: + cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), + b.y, b.width - x, y); + break; + case WEST: + cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), + b.y, b.width - x, b.height); + break; + case NORTH_WEST: + cacheRect.setBounds( + Math.min(b.x + x, b.x + b.width - min.width), + Math.min(b.y + y, b.y + b.height - min.height), + b.width - x, b.height - y); + break; + } + dm.resizeFrame(frame, cacheRect.x, cacheRect.y, + Math.max(min.width, cacheRect.width), + Math.max(min.height, cacheRect.height)); } else if (e.getSource() == titlePane) { - Rectangle fBounds = frame.getBounds(); + Rectangle fBounds = frame.getBounds(); - dm.dragFrame(frame, e.getX() - xOffset + b.x, - e.getY() - yOffset + b.y); + dm.dragFrame(frame, e.getX() - xOffset + b.x, e.getY() - yOffset + + b.y); } } @@ -304,17 +300,17 @@ public class BasicInternalFrameUI extends InternalFrameUI if (e.getSource() == frame && frame.isResizable()) { - direction = sectionOfClick(x, y); - dm.beginResizingFrame(frame, direction); + direction = sectionOfClick(x, y); + dm.beginResizingFrame(frame, direction); } else if (e.getSource() == titlePane) { - Rectangle tBounds = titlePane.getBounds(); + Rectangle tBounds = titlePane.getBounds(); - xOffset = e.getX() - tBounds.x + insets.left; - yOffset = e.getY() - tBounds.y + insets.top; + xOffset = e.getX() - tBounds.x + insets.left; + yOffset = e.getY() - tBounds.y + insets.top; - dm.beginDraggingFrame(frame); + dm.beginDraggingFrame(frame); } } @@ -329,9 +325,9 @@ public class BasicInternalFrameUI extends InternalFrameUI xOffset = 0; yOffset = 0; if (e.getSource() == frame && frame.isResizable()) - dm.endResizingFrame(frame); + dm.endResizingFrame(frame); else if (e.getSource() == titlePane) - dm.endDraggingFrame(frame); + dm.endDraggingFrame(frame); } /** @@ -348,21 +344,21 @@ public class BasicInternalFrameUI extends InternalFrameUI Insets insets = frame.getInsets(); Rectangle b = frame.getBounds(); if (x < insets.left && y < insets.top) - return NORTH_WEST; + return NORTH_WEST; else if (x > b.width - insets.right && y < insets.top) - return NORTH_EAST; + return NORTH_EAST; else if (x > b.width - insets.right && y > b.height - insets.bottom) - return SOUTH_EAST; + return SOUTH_EAST; else if (x < insets.left && y > b.height - insets.bottom) - return SOUTH_WEST; + return SOUTH_WEST; else if (y < insets.top) - return NORTH; + return NORTH; else if (x < insets.left) - return WEST; + return WEST; else if (y > b.height - insets.bottom) - return SOUTH; + return SOUTH; else if (x > b.width - insets.right) - return EAST; + return EAST; return -1; } @@ -377,8 +373,9 @@ public class BasicInternalFrameUI extends InternalFrameUI { /** * This method is called when the JDesktopPane is hidden. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentHidden(ComponentEvent e) { @@ -387,8 +384,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is moved. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentMoved(ComponentEvent e) { @@ -397,22 +395,23 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is resized. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentResized(ComponentEvent e) { if (frame.isMaximum()) { - JDesktopPane pane = (JDesktopPane) e.getSource(); - Insets insets = pane.getInsets(); - Rectangle bounds = pane.getBounds(); - - frame.setBounds(bounds.x + insets.left, bounds.y + insets.top, - bounds.width - insets.left - insets.right, - bounds.height - insets.top - insets.bottom); - frame.revalidate(); - frame.repaint(); + JDesktopPane pane = (JDesktopPane) e.getSource(); + Insets insets = pane.getInsets(); + Rectangle bounds = pane.getBounds(); + + frame.setBounds(bounds.x + insets.left, bounds.y + insets.top, + bounds.width - insets.left - insets.right, + bounds.height - insets.top - insets.bottom); + frame.revalidate(); + frame.repaint(); } // Sun also resizes the icons. but it doesn't seem to do anything. @@ -420,8 +419,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is shown. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentShown(ComponentEvent e) { @@ -435,21 +435,25 @@ public class BasicInternalFrameUI extends InternalFrameUI public class InternalFrameLayout implements LayoutManager { /** - * This method is called when the given Component is added to the + * This method is called when the given Component is added to the * JInternalFrame. - * - * @param name The name of the Component. - * @param c The Component added. + * + * @param name + * The name of the Component. + * @param c + * The Component added. */ public void addLayoutComponent(String name, Component c) { + // Nothing to do here. } /** * This method is used to set the bounds of the children of the * JInternalFrame. - * - * @param c The Container to lay out. + * + * @param c + * The Container to lay out. */ public void layoutContainer(Container c) { @@ -468,38 +472,38 @@ public class BasicInternalFrameUI extends InternalFrameUI if (northPane != null) { - Dimension nDims = northPane.getPreferredSize(); - nh = Math.min(nDims.height, dims.height); + Dimension nDims = northPane.getPreferredSize(); + nh = Math.min(nDims.height, dims.height); - northPane.setBounds(insets.left, insets.top, dims.width, nh); + northPane.setBounds(insets.left, insets.top, dims.width, nh); } if (southPane != null) { - Dimension sDims = southPane.getPreferredSize(); - sh = Math.min(sDims.height, dims.height - nh); + Dimension sDims = southPane.getPreferredSize(); + sh = Math.min(sDims.height, dims.height - nh); - southPane.setBounds(insets.left, insets.top + dims.height - sh, - dims.width, sh); + southPane.setBounds(insets.left, insets.top + dims.height - sh, + dims.width, sh); } int remHeight = dims.height - sh - nh; if (westPane != null) { - Dimension wDims = westPane.getPreferredSize(); - ww = Math.min(dims.width, wDims.width); + Dimension wDims = westPane.getPreferredSize(); + ww = Math.min(dims.width, wDims.width); - westPane.setBounds(insets.left, insets.top + nh, ww, remHeight); + westPane.setBounds(insets.left, insets.top + nh, ww, remHeight); } if (eastPane != null) { - Dimension eDims = eastPane.getPreferredSize(); - ew = Math.min(eDims.width, dims.width - ww); + Dimension eDims = eastPane.getPreferredSize(); + ew = Math.min(eDims.width, dims.width - ww); - eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh, - ew, remHeight); + eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh, + ew, remHeight); } int remWidth = dims.width - ww - ew; @@ -510,9 +514,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the minimum layout size. - * - * @param c The Container to find a minimum layout size for. - * + * + * @param c + * The Container to find a minimum layout size for. * @return The minimum dimensions for the JInternalFrame. */ public Dimension minimumLayoutSize(Container c) @@ -522,9 +526,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the maximum layout size. - * - * @param c The Container to find a maximum layout size for. - * + * + * @param c + * The Container to find a maximum layout size for. * @return The maximum dimensions for the JInternalFrame. */ public Dimension maximumLayoutSize(Container c) @@ -534,9 +538,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * Th8is method returns the preferred layout size. - * - * @param c The Container to find a preferred layout size for. - * + * + * @param c + * The Container to find a preferred layout size for. * @return The preferred dimensions for the JInternalFrame. */ public Dimension preferredLayoutSize(Container c) @@ -546,10 +550,11 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * DOCUMENT ME! - * - * @param c DOCUMENT ME! - * @param min DOCUMENT ME! - * + * + * @param c + * DOCUMENT ME! + * @param min + * DOCUMENT ME! * @return DOCUMENT ME! */ private Dimension getSize(Container c, boolean min) @@ -558,7 +563,7 @@ public class BasicInternalFrameUI extends InternalFrameUI Dimension contentDims = frame.getContentPane().getPreferredSize(); if (min) - contentDims.width = contentDims.height = 0; + contentDims.width = contentDims.height = 0; int nWidth = 0; int nHeight = 0; int sWidth = 0; @@ -571,42 +576,42 @@ public class BasicInternalFrameUI extends InternalFrameUI if (northPane != null) { - dims = northPane.getPreferredSize(); - if (dims != null) - { - nWidth = dims.width; - nHeight = dims.height; - } + dims = northPane.getPreferredSize(); + if (dims != null) + { + nWidth = dims.width; + nHeight = dims.height; + } } if (southPane != null) { - dims = southPane.getPreferredSize(); - if (dims != null) - { - sWidth = dims.width; - sHeight = dims.height; - } + dims = southPane.getPreferredSize(); + if (dims != null) + { + sWidth = dims.width; + sHeight = dims.height; + } } if (eastPane != null) { - dims = eastPane.getPreferredSize(); - if (dims != null) - { - sWidth = dims.width; - sHeight = dims.height; - } + dims = eastPane.getPreferredSize(); + if (dims != null) + { + sWidth = dims.width; + sHeight = dims.height; + } } if (westPane != null) { - dims = westPane.getPreferredSize(); - if (dims != null) - { - wWidth = dims.width; - wHeight = dims.height; - } + dims = westPane.getPreferredSize(); + if (dims != null) + { + wWidth = dims.width; + wHeight = dims.height; + } } int width = Math.max(sWidth, nWidth); @@ -630,6 +635,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void removeLayoutComponent(Component c) { + // Nothing to do here. } } @@ -657,8 +663,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse enters the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseEntered(MouseEvent e) { @@ -667,8 +674,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is clicked on the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseClicked(MouseEvent e) { @@ -677,8 +685,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is dragged in the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseDragged(MouseEvent e) { @@ -687,8 +696,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse exits the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseExited(MouseEvent e) { @@ -697,8 +707,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is moved in the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseMoved(MouseEvent e) { @@ -706,9 +717,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method is called when the mouse is pressed in the glass pane. - * - * @param e The MouseEvent. + * This method is called when the mouse is pressed in the glass pane. + * + * @param e + * The MouseEvent. */ public void mousePressed(MouseEvent e) { @@ -717,9 +729,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method is called when the mouse is released in the glass pane. - * - * @param e The MouseEvent. + * This method is called when the mouse is released in the glass pane. + * + * @param e + * The MouseEvent. */ public void mouseReleased(MouseEvent e) { @@ -727,10 +740,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method acquires a candidate component to dispatch the MouseEvent - * to. - * - * @param me The MouseEvent to acquire a component for. + * This method acquires a candidate component to dispatch the MouseEvent to. + * + * @param me + * The MouseEvent to acquire a component for. */ private void acquireComponentForMouseEvent(MouseEvent me) { @@ -738,134 +751,137 @@ public class BasicInternalFrameUI extends InternalFrameUI int y = me.getY(); // Find the candidate which should receive this event. - Component parent = frame.getContentPane(); + Component parent = frame.getLayeredPane(); if (parent == null) - return; + return; Component candidate = null; Point p = me.getPoint(); while (candidate == null && parent != null) { - candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); - if (candidate == null) - { - p = SwingUtilities.convertPoint(parent, p.x, p.y, - parent.getParent()); - parent = parent.getParent(); - } + candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); + if (candidate == null) + { + p = SwingUtilities.convertPoint(parent, p.x, p.y, + parent.getParent()); + parent = parent.getParent(); + } } // If the only candidate we found was the native container itself, - // don't dispatch any event at all. We only care about the lightweight + // don't dispatch any event at all. We only care about the lightweight // children here. if (candidate == frame.getContentPane()) - candidate = null; + candidate = null; // If our candidate is new, inform the old target we're leaving. if (lastComponentEntered != null && lastComponentEntered.isShowing() && lastComponentEntered != candidate) { - Point tp = SwingUtilities.convertPoint(frame.getContentPane(), x, y, - lastComponentEntered); - MouseEvent exited = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_EXITED, - me.getWhen(), me.getModifiersEx(), - tp.x, tp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); + Point tp = SwingUtilities.convertPoint(frame.getContentPane(), x, y, + lastComponentEntered); + MouseEvent exited = new MouseEvent(lastComponentEntered, + MouseEvent.MOUSE_EXITED, + me.getWhen(), me.getModifiersEx(), + tp.x, tp.y, me.getClickCount(), + me.isPopupTrigger(), + me.getButton()); tempComponent = lastComponentEntered; - lastComponentEntered = null; - tempComponent.dispatchEvent(exited); + lastComponentEntered = null; + tempComponent.dispatchEvent(exited); } // If we have a candidate, maybe enter it. if (candidate != null) { - mouseEventTarget = candidate; - if (candidate.isLightweight() && candidate.isShowing() - && candidate != frame.getContentPane() - && candidate != lastComponentEntered) - { - lastComponentEntered = mouseEventTarget; - Point cp = SwingUtilities.convertPoint(frame.getContentPane(), - x, y, lastComponentEntered); - MouseEvent entered = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_ENTERED, - me.getWhen(), - me.getModifiersEx(), cp.x, - cp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); - lastComponentEntered.dispatchEvent(entered); - } + mouseEventTarget = candidate; + if (candidate.isLightweight() && candidate.isShowing() + && candidate != frame.getContentPane() + && candidate != lastComponentEntered) + { + lastComponentEntered = mouseEventTarget; + Point cp = SwingUtilities.convertPoint(frame.getContentPane(), x, + y, lastComponentEntered); + MouseEvent entered = new MouseEvent(lastComponentEntered, + MouseEvent.MOUSE_ENTERED, + me.getWhen(), + me.getModifiersEx(), cp.x, + cp.y, me.getClickCount(), + me.isPopupTrigger(), + me.getButton()); + lastComponentEntered.dispatchEvent(entered); + } } if (me.getID() == MouseEvent.MOUSE_RELEASED || me.getID() == MouseEvent.MOUSE_PRESSED && pressCount > 0 || me.getID() == MouseEvent.MOUSE_DRAGGED) - // If any of the following events occur while a button is held down, - // they should be dispatched to the same component to which the - // original MOUSE_PRESSED event was dispatched: - // - MOUSE_RELEASED - // - MOUSE_PRESSED: another button pressed while the first is held down - // - MOUSE_DRAGGED - mouseEventTarget = pressedComponent; + // If any of the following events occur while a button is held down, + // they should be dispatched to the same component to which the + // original MOUSE_PRESSED event was dispatched: + // - MOUSE_RELEASED + // - MOUSE_PRESSED: another button pressed while the first is held down + // - MOUSE_DRAGGED + mouseEventTarget = pressedComponent; else if (me.getID() == MouseEvent.MOUSE_CLICKED) { - // Don't dispatch CLICKED events whose target is not the same as the - // target for the original PRESSED event. - if (candidate != pressedComponent) - mouseEventTarget = null; - else if (pressCount == 0) - pressedComponent = null; + // Don't dispatch CLICKED events whose target is not the same as the + // target for the original PRESSED event. + if (candidate != pressedComponent) + mouseEventTarget = null; + else if (pressCount == 0) + pressedComponent = null; } } /** - * This is a helper method that dispatches the GlassPane MouseEvents to - * the proper component. - * - * @param e The AWTEvent to be dispatched. Usually an instance of - * MouseEvent. + * This is a helper method that dispatches the GlassPane MouseEvents to the + * proper component. + * + * @param e + * The AWTEvent to be dispatched. Usually an instance of + * MouseEvent. */ private void handleEvent(AWTEvent e) { if (e instanceof MouseEvent) { - MouseEvent me = SwingUtilities.convertMouseEvent(frame.getRootPane() - .getGlassPane(), - (MouseEvent) e, - frame.getRootPane() - .getGlassPane()); - - acquireComponentForMouseEvent(me); - - // Avoid dispatching ENTERED and EXITED events twice. - if (mouseEventTarget != null && mouseEventTarget.isShowing() - && e.getID() != MouseEvent.MOUSE_ENTERED - && e.getID() != MouseEvent.MOUSE_EXITED) - { - MouseEvent newEvt = SwingUtilities.convertMouseEvent(frame - .getContentPane(), - me, - mouseEventTarget); - mouseEventTarget.dispatchEvent(newEvt); - - switch (e.getID()) - { - case MouseEvent.MOUSE_PRESSED: - if (pressCount++ == 0) - pressedComponent = mouseEventTarget; - break; - case MouseEvent.MOUSE_RELEASED: - // Clear our memory of the original PRESSED event, only if - // we're not expecting a CLICKED event after this. If - // there is a CLICKED event after this, it will do clean up. - if (--pressCount == 0 - && mouseEventTarget != pressedComponent) - pressedComponent = null; - break; - } - } + MouseEvent me = (MouseEvent) e; + acquireComponentForMouseEvent(me); + + //If there is no target, return + if (mouseEventTarget == null) + return; + + //Avoid re-dispatching to ourselves and causing an infinite loop + if (mouseEventTarget.equals(frame.getGlassPane())) + return; + + // Avoid dispatching ENTERED and EXITED events twice. + if (mouseEventTarget.isShowing() + && e.getID() != MouseEvent.MOUSE_ENTERED + && e.getID() != MouseEvent.MOUSE_EXITED) + { + MouseEvent newEvt = SwingUtilities.convertMouseEvent( + frame.getGlassPane(), + me, + mouseEventTarget); + mouseEventTarget.dispatchEvent(newEvt); + + switch (e.getID()) + { + case MouseEvent.MOUSE_PRESSED: + if (pressCount++ == 0) + pressedComponent = mouseEventTarget; + break; + case MouseEvent.MOUSE_RELEASED: + // Clear our memory of the original PRESSED event, only if + // we're not expecting a CLICKED event after this. If + // there is a CLICKED event after this, it will do clean up. + if (--pressCount == 0 && mouseEventTarget != pressedComponent) + pressedComponent = null; + break; + } + } } } } @@ -874,17 +890,18 @@ public class BasicInternalFrameUI extends InternalFrameUI * This helper class listens for PropertyChangeEvents from the * JInternalFrame. */ - public class InternalFramePropertyChangeListener - implements PropertyChangeListener, VetoableChangeListener + public class InternalFramePropertyChangeListener implements + PropertyChangeListener, VetoableChangeListener { /** - * This method is called when one of the JInternalFrame's properties - * change. This method is to allow JInternalFrame to veto an attempt - * to close the internal frame. This allows JInternalFrame to honour - * its defaultCloseOperation if that is DO_NOTHING_ON_CLOSE. + * This method is called when one of the JInternalFrame's properties change. + * This method is to allow JInternalFrame to veto an attempt to close the + * internal frame. This allows JInternalFrame to honour its + * defaultCloseOperation if that is DO_NOTHING_ON_CLOSE. */ - public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException + public void vetoableChange(PropertyChangeEvent e) + throws PropertyVetoException { if (e.getPropertyName().equals(JInternalFrame.IS_CLOSED_PROPERTY)) { @@ -892,75 +909,78 @@ public class BasicInternalFrameUI extends InternalFrameUI { frame.setVisible(false); frame.getDesktopPane().repaint(); - throw new PropertyVetoException ("close operation is HIDE_ON_CLOSE\n", e); + throw new PropertyVetoException( + "close operation is HIDE_ON_CLOSE\n", + e); } else if (frame.getDefaultCloseOperation() == JInternalFrame.DISPOSE_ON_CLOSE) closeFrame(frame); else - throw new PropertyVetoException ("close operation is DO_NOTHING_ON_CLOSE\n", e); + throw new PropertyVetoException( + "close operation is DO_NOTHING_ON_CLOSE\n", + e); } } - + /** - * This method is called when one of the JInternalFrame's properties - * change. - * - * @param evt The PropertyChangeEvent. + * This method is called when one of the JInternalFrame's properties change. + * + * @param evt + * The PropertyChangeEvent. */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) { - if (frame.isMaximum()) - maximizeFrame(frame); - else - minimizeFrame(frame); + if (frame.isMaximum()) + maximizeFrame(frame); + else + minimizeFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.IS_ICON_PROPERTY)) { - if (frame.isIcon()) - iconifyFrame(frame); - else - deiconifyFrame(frame); + if (frame.isIcon()) + iconifyFrame(frame); + else + deiconifyFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)) { - if (frame.isSelected()) - activateFrame(frame); - else - getDesktopManager().deactivateFrame(frame); + if (frame.isSelected()) + activateFrame(frame); + else + deactivateFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.ROOT_PANE_PROPERTY) - || evt.getPropertyName().equals(JInternalFrame.GLASS_PANE_PROPERTY)) + || evt.getPropertyName().equals( + JInternalFrame.GLASS_PANE_PROPERTY)) { - Component old = (Component) evt.getOldValue(); - old.removeMouseListener(glassPaneDispatcher); - old.removeMouseMotionListener(glassPaneDispatcher); + Component old = (Component) evt.getOldValue(); + old.removeMouseListener(glassPaneDispatcher); + old.removeMouseMotionListener(glassPaneDispatcher); - Component newPane = (Component) evt.getNewValue(); - newPane.addMouseListener(glassPaneDispatcher); - newPane.addMouseMotionListener(glassPaneDispatcher); + Component newPane = (Component) evt.getNewValue(); + newPane.addMouseListener(glassPaneDispatcher); + newPane.addMouseMotionListener(glassPaneDispatcher); - frame.revalidate(); + frame.revalidate(); } - /* FIXME: need to add ancestor properties to JComponents. - else if (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) - { - if (desktopPane != null) - desktopPane.removeComponentListener(componentListener); - desktopPane = frame.getDesktopPane(); - if (desktopPane != null) - desktopPane.addComponentListener(componentListener); - } - */ + /* + * FIXME: need to add ancestor properties to JComponents. else if + * (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) { if + * (desktopPane != null) + * desktopPane.removeComponentListener(componentListener); desktopPane = + * frame.getDesktopPane(); if (desktopPane != null) + * desktopPane.addComponentListener(componentListener); } + */ } } /** * This helper class is the border for the JInternalFrame. */ - private class InternalFrameBorder extends AbstractBorder - implements UIResource + private class InternalFrameBorder extends AbstractBorder implements + UIResource { /** The width of the border. */ private static final int bSize = 5; @@ -970,7 +990,7 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns whether the border is opaque. - * + * * @return Whether the border is opaque. */ public boolean isBorderOpaque() @@ -980,9 +1000,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the insets of the border. - * - * @param c The Component to find border insets for. - * + * + * @param c + * The Component to find border insets for. * @return The border insets. */ public Insets getBorderInsets(Component c) @@ -992,13 +1012,19 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method paints the border. - * - * @param c The Component that owns the border. - * @param g The Graphics object to paint with. - * @param x The x coordinate to paint at. - * @param y The y coordinate to paint at. - * @param width The width of the Component. - * @param height The height of the Component. + * + * @param c + * The Component that owns the border. + * @param g + * The Graphics object to paint with. + * @param x + * The x coordinate to paint at. + * @param y + * The y coordinate to paint at. + * @param width + * The width of the Component. + * @param height + * The height of the Component. */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) @@ -1111,6 +1137,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public BasicInternalFrameUI(JInternalFrame b) { + // Nothing to do here. } /** @@ -1135,21 +1162,21 @@ public class BasicInternalFrameUI extends InternalFrameUI { if (c instanceof JInternalFrame) { - frame = (JInternalFrame) c; + frame = (JInternalFrame) c; - internalFrameLayout = createLayoutManager(); - frame.setLayout(internalFrameLayout); + internalFrameLayout = createLayoutManager(); + frame.setLayout(internalFrameLayout); - ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); - frame.getRootPane().getGlassPane().setVisible(true); + ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); + frame.getRootPane().getGlassPane().setVisible(true); - installDefaults(); - installListeners(); - installComponents(); - installKeyboardActions(); + installDefaults(); + installListeners(); + installComponents(); + installKeyboardActions(); - frame.setOpaque(true); - frame.invalidate(); + frame.setOpaque(true); + frame.invalidate(); } } @@ -1177,10 +1204,8 @@ public class BasicInternalFrameUI extends InternalFrameUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - Border border = defaults.getBorder("InternalFrame.border"); - frame.setBorder(border); - frame.setFrameIcon(defaults.getIcon("InternalFrame.icon")); + LookAndFeel.installBorder(frame, "InternalFrame.border"); + frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon")); // InternalFrames are invisible by default. frame.setVisible(false); } @@ -1343,14 +1368,14 @@ public class BasicInternalFrameUI extends InternalFrameUI { if (currentPane != null) { - deinstallMouseHandlers(currentPane); - frame.remove(currentPane); + deinstallMouseHandlers(currentPane); + frame.remove(currentPane); } if (newPane != null) { - installMouseHandlers(newPane); - frame.add(newPane); + installMouseHandlers(newPane); + frame.add(newPane); } } @@ -1678,6 +1703,16 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** + * This is a convenience method that deactivates the JInternalFrame. + * + * @param f the JInternalFrame to deactivate + */ + protected void deactivateFrame(JInternalFrame f) + { + getDesktopManager().deactivateFrame(f); + } + + /** * This method returns a new ComponentListener for the JDesktopPane. * * @return A new ComponentListener. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index bb9ce6cb1d9..c8f677fa0a0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -50,9 +50,8 @@ import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.LabelUI; @@ -60,9 +59,7 @@ import javax.swing.plaf.LabelUI; * This is the Basic Look and Feel class for the JLabel. One BasicLabelUI * object is used to paint all JLabels that utilize the Basic Look and Feel. */ -public class BasicLabelUI - extends LabelUI - implements PropertyChangeListener +public class BasicLabelUI extends LabelUI implements PropertyChangeListener { /** The labelUI that is shared by all labels. */ protected static BasicLabelUI labelUI; @@ -345,11 +342,8 @@ public class BasicLabelUI */ protected void installDefaults(JLabel c) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - c.setForeground(defaults.getColor("Label.foreground")); - c.setBackground(defaults.getColor("Label.background")); - c.setFont(defaults.getFont("Label.font")); + LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", + "Label.font"); //XXX: There are properties we don't use called disabledForeground //and disabledShadow. } @@ -417,8 +411,6 @@ public class BasicLabelUI */ public void propertyChange(PropertyChangeEvent e) { - JLabel c = (JLabel) e.getSource(); - c.revalidate(); - c.repaint(); + // What to do here? } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 841bd670f67..33932991473 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -38,31 +38,35 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.CellRendererPane; +import javax.swing.DefaultListSelectionModel; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JViewport; +import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; @@ -70,7 +74,9 @@ import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.ListUI; /** @@ -125,8 +131,9 @@ public class BasicListUI extends ListUI * Helper method to repaint the focused cell's * lost or acquired focus state. */ - void repaintCellFocus() + protected void repaintCellFocus() { + // TODO: Implement this properly. } } @@ -183,141 +190,231 @@ public class BasicListUI extends ListUI */ public void valueChanged(ListSelectionEvent e) { + int index1 = e.getFirstIndex(); + int index2 = e.getLastIndex(); + Rectangle damaged = getCellBounds(list, index1, index2); + list.repaint(damaged); } } /** - * A helper class which listens for {@link KeyEvents}s - * from the {@link JList}. + * This class is used to mimmic the behaviour of the JDK when registering + * keyboard actions. It is the same as the private class used in JComponent + * for the same reason. This class receives an action event and dispatches + * it to the true receiver after altering the actionCommand property of the + * event. */ - private class KeyHandler extends KeyAdapter + private static class ActionListenerProxy + extends AbstractAction { - public KeyHandler() + ActionListener target; + String bindingCommandName; + + public ActionListenerProxy(ActionListener li, + String cmd) { + target = li; + bindingCommandName = cmd; } - - public void keyPressed( KeyEvent evt ) + + public void actionPerformed(ActionEvent e) + { + ActionEvent derivedEvent = new ActionEvent(e.getSource(), + e.getID(), + bindingCommandName, + e.getModifiers()); + target.actionPerformed(derivedEvent); + } + } + + class ListAction extends AbstractAction + { + public void actionPerformed (ActionEvent e) { - int lead = BasicListUI.this.list.getLeadSelectionIndex(); - int max = BasicListUI.this.list.getModel().getSize() - 1; + int lead = list.getLeadSelectionIndex(); + int max = list.getModel().getSize() - 1; + DefaultListSelectionModel selModel = (DefaultListSelectionModel)list.getSelectionModel(); + String command = e.getActionCommand(); // Do nothing if list is empty if (max == -1) return; - - // Process the key event. Bindings can be found in - // javax.swing.plaf.basic.BasicLookAndFeel.java - if ((evt.getKeyCode() == KeyEvent.VK_DOWN) - || (evt.getKeyCode() == KeyEvent.VK_KP_DOWN)) + + if (command.equals("selectNextRow")) { - if (evt.getModifiers() == 0) - { - BasicListUI.this.list.clearSelection(); - BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max)); - } - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) + selectNextIndex(); + } + else if (command.equals("selectPreviousRow")) + { + selectPreviousIndex(); + } + else if (command.equals("clearSelection")) + { + list.clearSelection(); + } + else if (command.equals("selectAll")) + { + list.setSelectionInterval(0, max); + // this next line is to restore the lead selection index to the old + // position, because select-all should not change the lead index + list.addSelectionInterval(lead, lead); + } + else if (command.equals("selectLastRow")) + { + list.setSelectedIndex(list.getModel().getSize() - 1); + } + else if (command.equals("selectLastRowChangeLead")) + { + selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1); + } + else if (command.equals("scrollDownExtendSelection")) + { + int target; + if (lead == list.getLastVisibleIndex()) { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.min(lead+1,max)); + target = Math.min + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } + else + target = list.getLastVisibleIndex(); + selModel.setLeadSelectionIndex(target); } - else if ((evt.getKeyCode() == KeyEvent.VK_UP) - || (evt.getKeyCode() == KeyEvent.VK_KP_UP)) + else if (command.equals("scrollDownChangeLead")) { - if (evt.getModifiers() == 0) + int target; + if (lead == list.getLastVisibleIndex()) { - BasicListUI.this.list.clearSelection(); - BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0)); + target = Math.min + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) + else + target = list.getLastVisibleIndex(); + selModel.moveLeadSelectionIndex(target); + } + else if (command.equals("scrollUpExtendSelection")) + { + int target; + if (lead == list.getFirstVisibleIndex()) { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.max(lead-1,0)); + target = Math.max + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } + else + target = list.getFirstVisibleIndex(); + selModel.setLeadSelectionIndex(target); } - else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP) + else if (command.equals("scrollUpChangeLead")) { int target; - if (lead == BasicListUI.this.list.getFirstVisibleIndex()) + if (lead == list.getFirstVisibleIndex()) { target = Math.max - (0, lead - (BasicListUI.this.list.getLastVisibleIndex() - - BasicListUI.this.list.getFirstVisibleIndex() + 1)); + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } else + target = list.getFirstVisibleIndex(); + selModel.moveLeadSelectionIndex(target); + } + else if (command.equals("selectNextRowExtendSelection")) + { + selModel.setLeadSelectionIndex(Math.min(lead + 1,max)); + } + else if (command.equals("selectFirstRow")) + { + list.setSelectedIndex(0); + } + else if (command.equals("selectFirstRowChangeLead")) + { + selModel.moveLeadSelectionIndex(0); + } + else if (command.equals("selectFirstRowExtendSelection")) + { + selModel.setLeadSelectionIndex(0); + } + else if (command.equals("selectPreviousRowExtendSelection")) + { + selModel.setLeadSelectionIndex(Math.max(0,lead - 1)); + } + else if (command.equals("scrollUp")) + { + int target; + if (lead == list.getFirstVisibleIndex()) { - target = BasicListUI.this.list.getFirstVisibleIndex(); + target = Math.max + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } - if (evt.getModifiers() == 0) - BasicListUI.this.list.setSelectedIndex(target); - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(target); + else + target = list.getFirstVisibleIndex(); + list.setSelectedIndex(target); } - else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN) + else if (command.equals("selectLastRowExtendSelection")) + { + selModel.setLeadSelectionIndex(list.getModel().getSize() - 1); + } + else if (command.equals("scrollDown")) { int target; - if (lead == BasicListUI.this.list.getLastVisibleIndex()) + if (lead == list.getLastVisibleIndex()) { target = Math.min - (max, lead + (BasicListUI.this.list.getLastVisibleIndex() - - BasicListUI.this.list.getFirstVisibleIndex() + 1)); + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } else + target = list.getLastVisibleIndex(); + list.setSelectedIndex(target); + } + else if (command.equals("selectNextRowChangeLead")) + { + if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + selectNextIndex(); + else + { + selModel.moveLeadSelectionIndex(Math.min(max, lead + 1)); + } + } + else if (command.equals("selectPreviousRowChangeLead")) + { + if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + selectPreviousIndex(); + else { - target = BasicListUI.this.list.getLastVisibleIndex(); + selModel.moveLeadSelectionIndex(Math.max(0, lead - 1)); } - if (evt.getModifiers() == 0) - BasicListUI.this.list.setSelectedIndex(target); - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(target); - } - else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH - && (evt.getModifiers() == InputEvent.CTRL_MASK)) + } + else if (command.equals("addToSelection")) { - BasicListUI.this.list.clearSelection(); + list.addSelectionInterval(lead, lead); } - else if ((evt.getKeyCode() == KeyEvent.VK_HOME) - || evt.getKeyCode() == KeyEvent.VK_END) + else if (command.equals("extendTo")) { - if (evt.getModifiers() != 0 && - evt.getModifiers() != InputEvent.SHIFT_MASK) - return; - // index is either 0 for HOME, or last cell for END - int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max; - - if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode() - == ListSelectionModel.SINGLE_SELECTION)) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_INTERVAL_SELECTION) - BasicListUI.this.list.setSelectionInterval - (BasicListUI.this.list.getAnchorSelectionIndex(), index); - else - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(index); + selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(), + lead); } - else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode() - == KeyEvent.VK_SLASH) && (evt.getModifiers() == - InputEvent.CTRL_MASK)) + else if (command.equals("toggleAndAnchor")) { - BasicListUI.this.list.setSelectionInterval(0, max); - // this next line is to restore the lead selection index to the old - // position, because select-all should not change the lead index - BasicListUI.this.list.addSelectionInterval(lead, lead); + if (!list.isSelectedIndex(lead)) + list.addSelectionInterval(lead, lead); + else + list.removeSelectionInterval(lead, lead); + selModel.setAnchorSelectionIndex(lead); } - else if (evt.getKeyCode() == KeyEvent.VK_SPACE && - (evt.getModifiers() == InputEvent.CTRL_MASK)) + else { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.min(lead+1,max)); + // DEBUG: uncomment the following line to print out + // key bindings that aren't implemented yet + + // System.out.println ("not implemented: "+e.getActionCommand()); } - - BasicListUI.this.list.ensureIndexIsVisible - (BasicListUI.this.list.getLeadSelectionIndex()); + + list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } } - + /** * A helper class which listens for {@link MouseEvent}s * from the {@link JList}. @@ -333,48 +430,46 @@ public class BasicListUI extends ListUI public void mouseClicked(MouseEvent event) { Point click = event.getPoint(); - int index = BasicListUI.this.locationToIndex(list, click); + int index = locationToIndex(list, click); if (index == -1) return; if (event.isShiftDown()) { - if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_SELECTION) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.getSelectionMode() == + if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) + list.setSelectedIndex(index); + else if (list.getSelectionMode() == ListSelectionModel.SINGLE_INTERVAL_SELECTION) // COMPAT: the IBM VM is compatible with the following line of code. // However, compliance with Sun's VM would correspond to replacing // getAnchorSelectionIndex() with getLeadSelectionIndex().This is // both unnatural and contradictory to the way they handle other // similar UI interactions. - BasicListUI.this.list.setSelectionInterval - (BasicListUI.this.list.getAnchorSelectionIndex(), index); + list.setSelectionInterval(list.getAnchorSelectionIndex(), index); else // COMPAT: both Sun and IBM are compatible instead with: - // BasicListUI.this.list.setSelectionInterval - // (BasicListUI.this.list.getLeadSelectionIndex(),index); + // list.setSelectionInterval + // (list.getLeadSelectionIndex(),index); // Note that for IBM this is contradictory to what they did in // the above situation for SINGLE_INTERVAL_SELECTION. // The most natural thing to do is the following: - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(index); + if (list.isSelectedIndex(list.getAnchorSelectionIndex())) + list.getSelectionModel().setLeadSelectionIndex(index); + else + list.addSelectionInterval(list.getAnchorSelectionIndex(), index); } else if (event.isControlDown()) { - if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_SELECTION) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.isSelectedIndex(index)) - BasicListUI.this.list.removeSelectionInterval(index,index); + if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) + list.setSelectedIndex(index); + else if (list.isSelectedIndex(index)) + list.removeSelectionInterval(index,index); else - BasicListUI.this.list.addSelectionInterval(index,index); + list.addSelectionInterval(index,index); } else - BasicListUI.this.list.setSelectedIndex(index); + list.setSelectedIndex(index); - BasicListUI.this.list.ensureIndexIsVisible - (BasicListUI.this.list.getLeadSelectionIndex()); + list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } /** @@ -385,6 +480,7 @@ public class BasicListUI extends ListUI */ public void mousePressed(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -395,6 +491,7 @@ public class BasicListUI extends ListUI */ public void mouseReleased(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -405,6 +502,7 @@ public class BasicListUI extends ListUI */ public void mouseEntered(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -415,6 +513,7 @@ public class BasicListUI extends ListUI */ public void mouseExited(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -425,6 +524,7 @@ public class BasicListUI extends ListUI */ public void mouseDragged(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -435,6 +535,7 @@ public class BasicListUI extends ListUI */ public void mouseMoved(MouseEvent event) { + // TODO: What should be done here, if anything? } } @@ -459,11 +560,61 @@ public class BasicListUI extends ListUI if (e.getNewValue() != null && e.getNewValue() instanceof ListModel) ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener); } + // Update the updateLayoutStateNeeded flag. + if (e.getPropertyName().equals("model")) + updateLayoutStateNeeded += modelChanged; + else if (e.getPropertyName().equals("selectionModel")) + updateLayoutStateNeeded += selectionModelChanged; + else if (e.getPropertyName().equals("font")) + updateLayoutStateNeeded += fontChanged; + else if (e.getPropertyName().equals("fixedCellWidth")) + updateLayoutStateNeeded += fixedCellWidthChanged; + else if (e.getPropertyName().equals("fixedCellHeight")) + updateLayoutStateNeeded += fixedCellHeightChanged; + else if (e.getPropertyName().equals("prototypeCellValue")) + updateLayoutStateNeeded += prototypeCellValueChanged; + else if (e.getPropertyName().equals("cellRenderer")) + updateLayoutStateNeeded += cellRendererChanged; BasicListUI.this.damageLayout(); } } /** + * A constant to indicate that the model has changed. + */ + protected static final int modelChanged = 1; + + /** + * A constant to indicate that the selection model has changed. + */ + protected static final int selectionModelChanged = 2; + + /** + * A constant to indicate that the font has changed. + */ + protected static final int fontChanged = 4; + + /** + * A constant to indicate that the fixedCellWidth has changed. + */ + protected static final int fixedCellWidthChanged = 8; + + /** + * A constant to indicate that the fixedCellHeight has changed. + */ + protected static final int fixedCellHeightChanged = 16; + + /** + * A constant to indicate that the prototypeCellValue has changed. + */ + protected static final int prototypeCellValueChanged = 32; + + /** + * A constant to indicate that the cellRenderer has changed. + */ + protected static final int cellRendererChanged = 64; + + /** * Creates a new BasicListUI for the component. * * @param c The component to create a UI for @@ -487,9 +638,6 @@ public class BasicListUI extends ListUI /** The mouse listener listening to the list. */ protected MouseInputListener mouseInputListener; - /** The key listener listening to the list */ - private KeyHandler keyListener; - /** The property change listener listening to the list. */ protected PropertyChangeListener propertyChangeListener; @@ -501,7 +649,11 @@ public class BasicListUI extends ListUI /** Saved reference to the list this UI was created for. */ protected JList list; - /** The height of a single cell in the list. */ + /** + * The height of a single cell in the list. This field is used when the + * fixedCellHeight property of the list is set. Otherwise this field is + * set to <code>-1</code> and {@link #cellHeights} is used instead. + */ protected int cellHeight; /** The width of a single cell in the list. */ @@ -509,14 +661,25 @@ public class BasicListUI extends ListUI /** * An array of varying heights of cells in the list, in cases where each - * cell might have a different height. + * cell might have a different height. This field is used when the + * <code>fixedCellHeight</code> property of the list is not set. Otherwise + * this field is <code>null</code> and {@link #cellHeight} is used. */ protected int[] cellHeights; /** - * A simple counter. When nonzero, indicates that the UI class is out of + * A bitmask that indicates which properties of the JList have changed. + * When nonzero, indicates that the UI class is out of * date with respect to the underlying list, and must recalculate the * list layout before painting or performing size calculations. + * + * @see #modelChanged + * @see #selectionModelChanged + * @see #fontChanged + * @see #fixedCellWidthChanged + * @see #fixedCellHeightChanged + * @see #prototypeCellValueChanged + * @see #cellRendererChanged */ protected int updateLayoutStateNeeded; @@ -524,6 +687,9 @@ public class BasicListUI extends ListUI * The {@link CellRendererPane} that is used for painting. */ protected CellRendererPane rendererPane; + + /** The action bound to KeyStrokes. */ + ListAction action; /** * Calculate the height of a particular row. If there is a fixed {@link @@ -611,19 +777,51 @@ public class BasicListUI extends ListUI * @param y0 The Y coordinate to calculate the row number for * * @return The row number containing the specified Y value, or <code>-1</code> - * if the specified Y coordinate is invalid + * if the list model is empty + * + * @specnote This method is specified to return -1 for an invalid Y + * coordinate. However, some simple tests show that the behaviour + * is to return the index of the last list element for an Y + * coordinate that lies outside of the list bounds (even for + * negative indices). <code>-1</code> + * is only returned if the list model is empty. */ protected int convertYToRow(int y0) { - for (int row = 0; row < cellHeights.length; ++row) - { - int h = getRowHeight(row); + if (list.getModel().getSize() == 0) + return -1; + + // When y0 < 0, then the JDK returns the maximum row index of the list. So + // do we. + if (y0 < 0) + return list.getModel().getSize() - 1; + + // Update the layout if necessary. + maybeUpdateLayoutState(); + + int index = list.getModel().getSize() - 1;; - if (y0 < h) - return row; - y0 -= h; + // If a fixed cell height is set, then we can work more efficient. + if (cellHeight > 0) + { + index = Math.max(y0 / cellHeight, index); + } + // If we have no fixed cell height, we must add up each cell height up + // to y0. + else + { + int h = 0; + for (int row = 0; row < cellHeights.length; ++row) + { + h += cellHeights[row]; + if (y0 < h) + { + index = row; + break; + } + } } - return -1; + return index; } /** @@ -638,29 +836,47 @@ public class BasicListUI extends ListUI cellWidth = -1; if (cellHeights == null || cellHeights.length != nrows) cellHeights = new int[nrows]; - if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1) + ListCellRenderer rend = list.getCellRenderer(); + // Update the cellHeight(s) fields. + int fixedCellHeight = list.getFixedCellHeight(); + if (fixedCellHeight > 0) + { + cellHeight = fixedCellHeight; + cellHeights = null; + } + else { - ListCellRenderer rend = list.getCellRenderer(); + cellHeight = -1; for (int i = 0; i < nrows; ++i) { - Component flyweight = rend.getListCellRendererComponent(list, - list.getModel() - .getElementAt(i), - 0, false, - false); + Component flyweight = + rend.getListCellRendererComponent(list, + list.getModel().getElementAt(i), + i, list.isSelectedIndex(i), + list.getSelectionModel().getAnchorSelectionIndex() == i); Dimension dim = flyweight.getPreferredSize(); cellHeights[i] = dim.height; - // compute average cell height (little hack here) - cellHeight = (cellHeight * i + cellHeights[i]) / (i + 1); - cellWidth = Math.max(cellWidth, dim.width); - if (list.getLayoutOrientation() == JList.VERTICAL) - cellWidth = Math.max(cellWidth, list.getSize().width); } } + + // Update the cellWidth field. + int fixedCellWidth = list.getFixedCellWidth(); + if (fixedCellWidth > 0) + cellWidth = fixedCellWidth; else { - cellHeight = list.getFixedCellHeight(); - cellWidth = list.getFixedCellWidth(); + for (int i = 0; i < nrows; ++i) + { + Component flyweight = + rend.getListCellRendererComponent(list, + list.getModel().getElementAt(i), + i, list.isSelectedIndex(i), + list.getSelectionModel().getAnchorSelectionIndex() == i); + Dimension dim = flyweight.getPreferredSize(); + cellWidth = Math.max(cellWidth, dim.width); + } + if (list.getLayoutOrientation() == JList.VERTICAL) + cellWidth = Math.max(cellWidth, list.getSize().width); } } @@ -694,13 +910,6 @@ public class BasicListUI extends ListUI */ public BasicListUI() { - focusListener = new FocusHandler(); - listDataListener = new ListDataHandler(); - listSelectionListener = new ListSelectionHandler(); - mouseInputListener = new MouseInputHandler(); - keyListener = new KeyHandler(); - propertyChangeListener = new PropertyChangeHandler(); - componentListener = new ComponentHandler(); updateLayoutStateNeeded = 1; rendererPane = new CellRendererPane(); } @@ -713,11 +922,10 @@ public class BasicListUI extends ListUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - list.setForeground(defaults.getColor("List.foreground")); - list.setBackground(defaults.getColor("List.background")); - list.setSelectionForeground(defaults.getColor("List.selectionForeground")); - list.setSelectionBackground(defaults.getColor("List.selectionBackground")); + LookAndFeel.installColorsAndFont(list, "List.background", + "List.foreground", "List.font"); + list.setSelectionForeground(UIManager.getColor("List.selectionForeground")); + list.setSelectionBackground(UIManager.getColor("List.selectionBackground")); list.setOpaque(true); } @@ -727,7 +935,6 @@ public class BasicListUI extends ListUI */ protected void uninstallDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); list.setForeground(null); list.setBackground(null); list.setSelectionForeground(null); @@ -742,14 +949,28 @@ public class BasicListUI extends ListUI */ protected void installListeners() { + if (focusListener == null) + focusListener = createFocusListener(); list.addFocusListener(focusListener); + if (listDataListener == null) + listDataListener = createListDataListener(); list.getModel().addListDataListener(listDataListener); + if (listSelectionListener == null) + listSelectionListener = createListSelectionListener(); list.addListSelectionListener(listSelectionListener); + if (mouseInputListener == null) + mouseInputListener = createMouseInputListener(); list.addMouseListener(mouseInputListener); - list.addKeyListener(keyListener); list.addMouseMotionListener(mouseInputListener); + if (propertyChangeListener == null) + propertyChangeListener = createPropertyChangeListener(); list.addPropertyChangeListener(propertyChangeListener); + + // FIXME: Are these two really needed? At least they are not documented. + //keyListener = new KeyHandler(); + componentListener = new ComponentHandler(); list.addComponentListener(componentListener); + //list.addKeyListener(keyListener); } /** @@ -761,16 +982,41 @@ public class BasicListUI extends ListUI list.getModel().removeListDataListener(listDataListener); list.removeListSelectionListener(listSelectionListener); list.removeMouseListener(mouseInputListener); - list.removeKeyListener(keyListener); + //list.removeKeyListener(keyListener); list.removeMouseMotionListener(mouseInputListener); list.removePropertyChangeListener(propertyChangeListener); } - + /** * Installs keyboard actions for this UI in the {@link JList}. */ protected void installKeyboardActions() { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + InputMap focusInputMap = (InputMap)defaults.get("List.focusInputMap"); + InputMapUIResource parentInputMap = new InputMapUIResource(); + // FIXME: The JDK uses a LazyActionMap for parentActionMap + ActionMap parentActionMap = new ActionMapUIResource(); + action = new ListAction(); + Object keys[] = focusInputMap.allKeys(); + // Register key bindings in the UI InputMap-ActionMap pair + for (int i = 0; i < keys.length; i++) + { + KeyStroke stroke = (KeyStroke)keys[i]; + String actionString = (String) focusInputMap.get(stroke); + parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), + stroke.getModifiers()), + actionString); + + parentActionMap.put (actionString, + new ActionListenerProxy(action, actionString)); + } + // Register the new InputMap-ActionMap as the parents of the list's + // InputMap and ActionMap + parentInputMap.setParent(list.getInputMap().getParent()); + parentActionMap.setParent(list.getActionMap().getParent()); + list.getInputMap().setParent(parentInputMap); + list.getActionMap().setParent(parentActionMap); } /** @@ -778,6 +1024,7 @@ public class BasicListUI extends ListUI */ protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } /** @@ -855,22 +1102,6 @@ public class BasicListUI extends ListUI } /** - * Paints the packground of the list using the background color - * of the specified component. - * - * @param g The graphics context to paint in - * @param c The component to paint the background of - */ - private void paintBackground(Graphics g, JComponent c) - { - Dimension size = getPreferredSize(c); - Color save = g.getColor(); - g.setColor(c.getBackground()); - g.fillRect(0, 0, size.width, size.height); - g.setColor(save); - } - - /** * Paints a single cell in the list. * * @param g The graphics context to paint in @@ -892,14 +1123,12 @@ public class BasicListUI extends ListUI Component comp = rend.getListCellRendererComponent(list, data.getElementAt(row), 0, isSel, hasFocus); - //comp.setBounds(new Rectangle(0, 0, bounds.width, bounds.height)); - //comp.paint(g); rendererPane.paintComponent(g, comp, list, bounds); } /** - * Paints the list by calling {@link #paintBackground} and then repeatedly - * calling {@link #paintCell} for each visible cell in the list. + * Paints the list by repeatedly calling {@link #paintCell} for each visible + * cell in the list. * * @param g The graphics context to paint with * @param c Ignored; uses the saved {@link JList} reference @@ -916,9 +1145,12 @@ public class BasicListUI extends ListUI ListSelectionModel sel = list.getSelectionModel(); int lead = sel.getLeadSelectionIndex(); Rectangle clip = g.getClipBounds(); - paintBackground(g, list); - for (int row = 0; row < nrows; ++row) + int startIndex = list.locationToIndex(new Point(clip.x, clip.y)); + int endIndex = list.locationToIndex(new Point(clip.x + clip.width, + clip.y + clip.height)); + + for (int row = startIndex; row <= endIndex; ++row) { Rectangle bounds = getCellBounds(list, row, row); if (bounds.intersects(clip)) @@ -927,13 +1159,15 @@ public class BasicListUI extends ListUI } /** - * Computes the index of a list cell given a point within the list. + * Computes the index of a list cell given a point within the list. If the + * location lies outside the bounds of the list, the greatest index in the + * list model is returned. * * @param list the list which on which the computation is based on * @param location the coordinates * * @return the index of the list item that is located at the given - * coordinates or <code>null</code> if the location is invalid + * coordinates or <code>-1</code> if the list model is empty */ public int locationToIndex(JList list, Point location) { @@ -983,7 +1217,6 @@ public class BasicListUI extends ListUI int numberOfItems2 = list.getModel().getSize(); int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; - Dimension listDim2 = list.getSize(); int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); int gridY2 = Math.min(location.y / cellHeight, visibleRows2); index = gridY2 + gridX2 * visibleRows2; @@ -1045,4 +1278,82 @@ public class BasicListUI extends ListUI } return loc; } + + /** + * Creates and returns the focus listener for this UI. + * + * @return the focus listener for this UI + */ + protected FocusListener createFocusListener() + { + return new FocusHandler(); + } + + /** + * Creates and returns the list data listener for this UI. + * + * @return the list data listener for this UI + */ + protected ListDataListener createListDataListener() + { + return new ListDataHandler(); + } + + /** + * Creates and returns the list selection listener for this UI. + * + * @return the list selection listener for this UI + */ + protected ListSelectionListener createListSelectionListener() + { + return new ListSelectionHandler(); + } + + /** + * Creates and returns the mouse input listener for this UI. + * + * @return the mouse input listener for this UI + */ + protected MouseInputListener createMouseInputListener() + { + return new MouseInputHandler(); + } + + /** + * Creates and returns the property change listener for this UI. + * + * @return the property change listener for this UI + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new PropertyChangeHandler(); + } + + /** + * Selects the next list item and force it to be visible. + */ + protected void selectNextIndex() + { + int index = list.getSelectionModel().getLeadSelectionIndex(); + if (index < list.getModel().getSize() - 1) + { + index++; + list.setSelectedIndex(index); + } + list.ensureIndexIsVisible(index); + } + + /** + * Selects the previous list item and force it to be visible. + */ + protected void selectPreviousIndex() + { + int index = list.getSelectionModel().getLeadSelectionIndex(); + if (index > 0) + { + index--; + list.setSelectedIndex(index); + } + list.ensureIndexIsVisible(index); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index d35ac9eb926..8ebe650350c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -273,7 +273,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Button.foreground", new ColorUIResource(Color.BLACK), "Button.highlight", new ColorUIResource(Color.WHITE), "Button.light", new ColorUIResource(Color.LIGHT_GRAY), - "Button.margin", new InsetsUIResource(2, 2, 2, 2), + "Button.margin", new InsetsUIResource(2, 14, 2, 14), "Button.shadow", new ColorUIResource(Color.GRAY), "Button.textIconGap", new Integer(4), "Button.textShiftOffset", new Integer(0), @@ -362,16 +362,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel "HOME", "homePassThrough", "END", "endPassThrough" }), - "ComboBox.background", new ColorUIResource(light), + "ComboBox.background", new ColorUIResource(Color.white), "ComboBox.buttonBackground", new ColorUIResource(light), - "ComboBox.buttonDarkShadow", new ColorUIResource(shadow), + "ComboBox.buttonDarkShadow", new ColorUIResource(darkShadow), "ComboBox.buttonHighlight", new ColorUIResource(highLight), "ComboBox.buttonShadow", new ColorUIResource(shadow), "ComboBox.disabledBackground", new ColorUIResource(light), "ComboBox.disabledForeground", new ColorUIResource(Color.gray), "ComboBox.font", new FontUIResource("SansSerif", Font.PLAIN, 12), "ComboBox.foreground", new ColorUIResource(Color.black), - "ComboBox.selectionBackground", new ColorUIResource(Color.black), + "ComboBox.selectionBackground", new ColorUIResource(0, 0, 128), "ComboBox.selectionForeground", new ColorUIResource(Color.white), "Desktop.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "KP_LEFT", "left", @@ -397,7 +397,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "DesktopIcon.border", new BorderUIResource.CompoundBorderUIResource(null, null), "EditorPane.background", new ColorUIResource(Color.white), - "EditorPane.border", new BasicBorders.MarginBorder(), + "EditorPane.border", BasicBorders.getMarginBorder(), "EditorPane.caretBlinkRate", new Integer(500), "EditorPane.caretForeground", new ColorUIResource(Color.black), "EditorPane.font", new FontUIResource("Serif", Font.PLAIN, 12), @@ -466,6 +466,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "FocusManagerClassName", "TODO", "FormattedTextField.background", new ColorUIResource(light), "FormattedTextField.caretForeground", new ColorUIResource(Color.black), + "FormattedTextField.font", + new FontUIResource("SansSerif", Font.PLAIN, 12), "FormattedTextField.foreground", new ColorUIResource(Color.black), "FormattedTextField.inactiveBackground", new ColorUIResource(light), "FormattedTextField.inactiveForeground", new ColorUIResource(Color.gray), @@ -528,30 +530,74 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Label.disabledShadow", new ColorUIResource(shadow), "Label.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Label.foreground", new ColorUIResource(darkShadow), - "List.background", new ColorUIResource(light), + "List.background", new ColorUIResource(Color.white), "List.border", new BasicBorders.MarginBorder(), "List.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "PAGE_UP", "scrollUp", - "ctrl \\", "clearSelection", - "PAGE_DOWN", "scrollDown", - "shift PAGE_DOWN","scrollDownExtendSelection", + "ctrl DOWN", "selectNextRowChangeLead", + "shift UP", "selectPreviousRowExtendSelection", + "ctrl RIGHT", "selectNextColumnChangeLead", + "shift ctrl LEFT", "selectPreviousColumnExtendSelection", + "shift KP_UP", "selectPreviousRowChangeLead", + "DOWN", "selectNextRow", + "ctrl UP", "selectPreviousRowChangeLead", + "ctrl LEFT", "selectPreviousColumnChangeLead", + "CUT", "cut", "END", "selectLastRow", - "HOME", "selectFirstRow", - "shift END", "selectLastRowExtendSelection", + "shift PAGE_UP","scrollUpExtendSelection", + "KP_UP", "selectPreviousRow", + "shift ctrl UP", "selectPreviousRowExtendSelection", + "ctrl HOME", "selectFirstRowChangeLead", + "shift LEFT", "selectPreviousColumnExtendSelection", + "ctrl END", "selectLastRowChangeLead", + "ctrl PAGE_DOWN", "scrollDownChangeLead", + "shift ctrl RIGHT", "selectNextColumnExtendSelection", + "LEFT", "selectPreviousColumn", + "ctrl PAGE_UP", "scrollUpChangeLead", + "KP_LEFT", "selectPreviousColumn", + "shift KP_RIGHT", "selectNextColumnExtendSelection", + "SPACE", "addToSelection", + "ctrl SPACE", "toggleAndAnchor", + "shift SPACE", "extendTo", + "shift ctrl SPACE", "moveSelectionTo", + "shift ctrl DOWN", "selectNextRowExtendSelection", + "ctrl BACK_SLASH", "clearSelection", "shift HOME", "selectFirstRowExtendSelection", - "UP", "selectPreviousRow", - "ctrl /", "selectAll", - "ctrl A", "selectAll", - "DOWN", "selectNextRow", - "shift UP", "selectPreviousRowExtendSelection", - "ctrl SPACE", "selectNextRowExtendSelection", + "RIGHT", "selectNextColumn", + "shift ctrl PAGE_UP", "scrollUpExtendSelection", "shift DOWN", "selectNextRowExtendSelection", - "KP_UP", "selectPreviousRow", - "shift PAGE_UP","scrollUpExtendSelection", - "KP_DOWN", "selectNextRow" + "PAGE_DOWN", "scrollDown", + "shift ctrl KP_UP", "selectPreviousRowExtendSelection", + "shift KP_LEFT", "selectPreviousColumnExtendSelection", + "ctrl X", "cut", + "shift ctrl PAGE_DOWN", "scrollDownExtendSelection", + "ctrl SLASH", "selectAll", + "ctrl C", "copy", + "ctrl KP_RIGHT", "selectNextColumnChangeLead", + "shift END", "selectLastRowExtendSelection", + "shift ctrl KP_DOWN", "selectNextRowExtendSelection", + "ctrl KP_LEFT", "selectPreviousColumnChangeLead", + "HOME", "selectFirstRow", + "ctrl V", "paste", + "KP_DOWN", "selectNextRow", + "ctrl KP_DOWN", "selectNextRowChangeLead", + "shift RIGHT", "selectNextColumnExtendSelection", + "ctrl A", "selectAll", + "shift ctrl END", "selectLastRowExtendSelection", + "COPY", "copy", + "ctrl KP_UP", "selectPreviousRowChangeLead", + "shift ctrl KP_LEFT", "selectPreviousColumnExtendSelection", + "shift KP_DOWN", "selectNextRowExtendSelection", + "UP", "selectPreviousRow", + "shift ctrl HOME", "selectFirstRowExtendSelection", + "shift PAGE_DOWN", "scrollDownExtendSelection", + "KP_RIGHT", "selectNextColumn", + "shift ctrl KP_RIGHT", "selectNextColumnExtendSelection", + "PAGE_UP", "scrollUp", + "PASTE", "paste" }), - "List.foreground", new ColorUIResource(darkShadow), - "List.selectionBackground", new ColorUIResource(Color.black), + "List.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "List.foreground", new ColorUIResource(Color.black), + "List.selectionBackground", new ColorUIResource(0, 0, 128), "List.selectionForeground", new ColorUIResource(Color.white), "List.focusCellHighlightBorder", new BorderUIResource. @@ -601,7 +647,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel "MenuItem.background", new ColorUIResource(light), "MenuItem.border", new BasicBorders.MarginBorder(), "MenuItem.borderPainted", Boolean.FALSE, - "MenuItem.checkIcon", BasicIconFactory.getMenuItemCheckIcon(), "MenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12), "MenuItem.foreground", new ColorUIResource(darkShadow), "MenuItem.margin", new InsetsUIResource(2, 2, 2, 2), @@ -624,7 +669,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel "OptionPane.messageAreaBorder", new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0), "OptionPane.messageForeground", new ColorUIResource(darkShadow), - "OptionPane.minimumSize", new DimensionUIResource(262, 90), + "OptionPane.minimumSize", + new DimensionUIResource(BasicOptionPaneUI.MinimumWidth, + BasicOptionPaneUI.MinimumHeight), "OptionPane.noButtonText", "No", "OptionPane.okButtonText", "OK", // XXX Don't use gif @@ -660,16 +707,17 @@ public abstract class BasicLookAndFeel extends LookAndFeel "PopupMenu.border", new BorderUIResource.BevelBorderUIResource(0), "PopupMenu.font", new FontUIResource("Dialog", Font.PLAIN, 12), "PopupMenu.foreground", new ColorUIResource(darkShadow), - "ProgressBar.background", new ColorUIResource(light), - "ProgressBar.border", new BorderUIResource.LineBorderUIResource(Color.darkGray), + "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY), + "ProgressBar.border", + new BorderUIResource.LineBorderUIResource(Color.GREEN, 2), "ProgressBar.cellLength", new Integer(1), "ProgressBar.cellSpacing", new Integer(0), "ProgressBar.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "ProgressBar.foreground", new ColorUIResource(Color.black), - "ProgressBar.selectionBackground", new ColorUIResource(Color.black), - "ProgressBar.selectionForeground", new ColorUIResource(light), - "ProgressBar.repaintInterval", new Integer(250), - "ProgressBar.cycleTime", new Integer(6000), + "ProgressBar.foreground", new ColorUIResource(0, 0, 128), + "ProgressBar.selectionBackground", new ColorUIResource(0, 0, 128), + "ProgressBar.selectionForeground", new ColorUIResource(Color.LIGHT_GRAY), + "ProgressBar.repaintInterval", new Integer(50), + "ProgressBar.cycleTime", new Integer(3000), "RadioButton.background", new ColorUIResource(light), "RadioButton.border", new BorderUIResource.CompoundBorderUIResource(null, null), @@ -742,6 +790,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ScrollBar.thumbShadow", new ColorUIResource(shadow), "ScrollBar.track", new ColorUIResource(light), "ScrollBar.trackHighlight", new ColorUIResource(shadow), + "ScrollBar.width", new Integer(16), "ScrollPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "PAGE_UP", "scrollUp", "KP_LEFT", "unitScrollLeft", @@ -846,6 +895,24 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TabbedPane.tabRunOverlay", new Integer(2), "TabbedPane.textIconGap", new Integer(4), "Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { + "ctrl DOWN", "selectNextRowChangeLead", + "ctrl RIGHT", "selectNextColumnChangeLead", + "ctrl UP", "selectPreviousRowChangeLead", + "ctrl LEFT", "selectPreviousColumnChangeLead", + "CUT", "cut", + "SPACE", "addToSelection", + "ctrl SPACE", "toggleAndAnchor", + "shift SPACE", "extendTo", + "shift ctrl SPACE", "moveSelectionTo", + "ctrl X", "cut", + "ctrl C", "copy", + "ctrl KP_RIGHT", "selectNextColumnChangeLead", + "ctrl KP_LEFT", "selectPreviousColumnChangeLead", + "ctrl V", "paste", + "ctrl KP_DOWN", "selectNextRowChangeLead", + "COPY", "copy", + "ctrl KP_UP", "selectPreviousRowChangeLead", + "PASTE", "paste", "shift PAGE_DOWN","scrollDownExtendSelection", "PAGE_DOWN", "scrollDownChangeSelection", "END", "selectLastColumn", @@ -896,25 +963,26 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ctrl SLASH", "selectAll", "ctrl shift KP_DOWN", "selectNextRowExtendSelection", }), - "Table.background", new ColorUIResource(light), - "Table.focusCellBackground", new ColorUIResource(light), - "Table.focusCellForeground", new ColorUIResource(darkShadow), + "Table.background", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "Table.focusCellBackground", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "Table.focusCellForeground", new ColorUIResource(new ColorUIResource(0, 0, 0)), "Table.focusCellHighlightBorder", new BorderUIResource.LineBorderUIResource( new ColorUIResource(255, 255, 0)), "Table.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "Table.foreground", new ColorUIResource(darkShadow), - "Table.gridColor", new ColorUIResource(Color.gray), + "Table.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)), + "Table.gridColor", new ColorUIResource(new ColorUIResource(128, 128, 128)), "Table.scrollPaneBorder", new BorderUIResource.BevelBorderUIResource(0), - "Table.selectionBackground", new ColorUIResource(Color.black), - "Table.selectionForeground", new ColorUIResource(Color.white), - "TableHeader.background", new ColorUIResource(light), + "Table.selectionBackground", new ColorUIResource(new ColorUIResource(0, 0, 128)), + "Table.selectionForeground", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "TableHeader.background", new ColorUIResource(new ColorUIResource(192, 192, 192)), "TableHeader.cellBorder", new BorderUIResource.BevelBorderUIResource(0), "TableHeader.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "TableHeader.foreground", new ColorUIResource(darkShadow), + "TableHeader.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)), "TextArea.background", new ColorUIResource(light), - "TextArea.border", new BasicBorders.MarginBorder(), + "TextArea.border", + new BorderUIResource(BasicBorders.getMarginBorder()), "TextArea.caretBlinkRate", new Integer(500), "TextArea.caretForeground", new ColorUIResource(Color.black), "TextArea.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12), @@ -945,8 +1013,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.font", new FontUIResource("SansSerif", Font.PLAIN, 12), "TextField.foreground", new ColorUIResource(Color.black), "TextField.highlight", new ColorUIResource(highLight), - "TextField.inactiveBackground", new ColorUIResource(light), - "TextField.inactiveForeground", new ColorUIResource(Color.gray), + "TextField.inactiveBackground", new ColorUIResource(Color.LIGHT_GRAY), + "TextField.inactiveForeground", new ColorUIResource(Color.GRAY), "TextField.light", new ColorUIResource(highLight), "TextField.highlight", new ColorUIResource(light), "TextField.keyBindings", new JTextComponent.KeyBinding[] { @@ -964,7 +1032,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.selectionBackground", new ColorUIResource(Color.black), "TextField.selectionForeground", new ColorUIResource(Color.white), "TextPane.background", new ColorUIResource(Color.white), - "TextPane.border", new BasicBorders.MarginBorder(), + "TextPane.border", BasicBorders.getMarginBorder(), "TextPane.caretBlinkRate", new Integer(500), "TextPane.caretForeground", new ColorUIResource(Color.black), "TextPane.font", new FontUIResource("Serif", Font.PLAIN, 12), @@ -1036,7 +1104,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Tree.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "ESCAPE", "cancel" }), - "Tree.background", new ColorUIResource(light), + "Tree.background", new ColorUIResource(new Color(255, 255, 255)), "Tree.changeSelectionWithFocus", Boolean.TRUE, // "Tree.closedIcon", new IconUIResource(new ImageIcon("icons/TreeClosed.png")), // "Tree.collapsedIcon", new IconUIResource(new ImageIcon("icons/TreeCollapsed.png")), @@ -1086,20 +1154,20 @@ public abstract class BasicLookAndFeel extends LookAndFeel "PAGE_UP", "scrollUpChangeSelection", "ctrl PAGE_DOWN", "scrollDownChangeLead" }), - "Tree.font", new FontUIResource(new Font("Helvetica", Font.PLAIN, 12)), + "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Tree.foreground", new ColorUIResource(Color.black), "Tree.hash", new ColorUIResource(new Color(128, 128, 128)), "Tree.leftChildIndent", new Integer(7), "Tree.rightChildIndent", new Integer(13), - "Tree.rowHeight", new Integer(20), // FIXME + "Tree.rowHeight", new Integer(16), "Tree.scrollsOnExpand", Boolean.TRUE, "Tree.selectionBackground", new ColorUIResource(Color.black), - "Tree.nonSelectionBackground", new ColorUIResource(new Color(239, 235, 231)), + "Tree.nonSelectionBackground", new ColorUIResource(new Color(255, 255, 255)), "Tree.selectionBorderColor", new ColorUIResource(Color.black), "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(Color.black), "Tree.selectionForeground", new ColorUIResource(new Color(255, 255, 255)), - "Tree.textBackground", new ColorUIResource(new Color(255, 255, 255)), - "Tree.textForeground", new ColorUIResource(Color.black), + "Tree.textBackground", new ColorUIResource(new Color(192, 192, 192)), + "Tree.textForeground", new ColorUIResource(new Color(0, 0, 0)), "Viewport.background", new ColorUIResource(light), "Viewport.foreground", new ColorUIResource(Color.black), "Viewport.font", new FontUIResource("Dialog", Font.PLAIN, 12) diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java index 95f6b84fb7c..daa9b0d6b63 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -41,16 +41,19 @@ package javax.swing.plaf.basic; import java.awt.Dimension; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.BoxLayout; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.JMenuBar; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; +import javax.swing.MenuElement; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuBarUI; @@ -63,12 +66,15 @@ public class BasicMenuBarUI extends MenuBarUI /*ContainerListener that listens to the ContainerEvents fired from menu bar*/ protected ContainerListener containerListener; - + /*Property change listeners that listener to PropertyChangeEvent from menu bar*/ protected PropertyChangeListener propertyChangeListener; /* menu bar for which this UI delegate is for*/ protected JMenuBar menuBar; + + /* MouseListener that listens to the mouseEvents fired from menu bar*/ + private MouseInputListener mouseListener; /** * Creates a new BasicMenuBarUI object. @@ -78,6 +84,7 @@ public class BasicMenuBarUI extends MenuBarUI changeListener = createChangeListener(); containerListener = createContainerListener(); propertyChangeListener = new PropertyChangeHandler(); + mouseListener = new MouseInputHandler(); } /** @@ -159,12 +166,9 @@ public class BasicMenuBarUI extends MenuBarUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuBar.setBackground(defaults.getColor("MenuBar.background")); - menuBar.setBorder(defaults.getBorder("MenuBar.border")); - menuBar.setFont(defaults.getFont("MenuBar.font")); - menuBar.setForeground(defaults.getColor("MenuBar.foreground")); + LookAndFeel.installBorder(menuBar, "MenuBar.border"); + LookAndFeel.installColorsAndFont(menuBar, "MenuBar.background", + "MenuBar.foreground", "MenuBar.font"); menuBar.setOpaque(true); } @@ -183,6 +187,7 @@ public class BasicMenuBarUI extends MenuBarUI { menuBar.addContainerListener(containerListener); menuBar.addPropertyChangeListener(propertyChangeListener); + menuBar.addMouseListener(mouseListener); } /** @@ -229,6 +234,7 @@ public class BasicMenuBarUI extends MenuBarUI { menuBar.removeContainerListener(containerListener); menuBar.removePropertyChangeListener(propertyChangeListener); + menuBar.removeMouseListener(mouseListener); } /** @@ -250,6 +256,7 @@ public class BasicMenuBarUI extends MenuBarUI { public void stateChanged(ChangeEvent event) { + // TODO: What should be done here, if anything? } } @@ -301,4 +308,84 @@ public class BasicMenuBarUI extends MenuBarUI menuBar.repaint(); } } + + private class MouseInputHandler implements MouseInputListener + { + /** + * Handles mouse clicked event + * + * @param e Mouse event + */ + public void mouseClicked(MouseEvent e) + { + MenuElement[] me = menuBar.getSubElements(); + + for (int i = 0; i < me.length; i++) + { + JMenu menu = menuBar.getMenu(i); + if (menu != null) + menu.setSelected(false); + } + } + + /** + * Handles mouse pressed event + * + * @param e Mouse event + */ + public void mousePressed(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse released event + * + * @param e Mouse event + */ + public void mouseReleased(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse exited event + * + * @param e Mouse event + */ + public void mouseExited(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse dragged event + * + * @param e Mouse event + */ + public void mouseDragged(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse moved event + * + * @param e Mouse event + */ + public void mouseMoved(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse entered event + * + * @param e Mouse event + */ + public void mouseEntered(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index 8aa1193bcc4..2a3556a5db9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -7,7 +7,7 @@ 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 @@ -46,18 +46,27 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.ButtonModel; import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.SwingConstants; @@ -69,6 +78,8 @@ import javax.swing.event.MenuDragMouseListener; import javax.swing.event.MenuKeyEvent; import javax.swing.event.MenuKeyListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentInputMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuItemUI; @@ -109,7 +120,7 @@ public class BasicMenuItemUI extends MenuItemUI * Number of spaces between icon and text. */ protected int defaultTextIconGap = 4; - + /** * Color of the text when menu item is disabled */ @@ -156,16 +167,69 @@ public class BasicMenuItemUI extends MenuItemUI private String acceleratorDelimiter; /** - * PropertyChangeListener to listen for property changes in the menu item + * ItemListener to listen for item changes in the menu item */ - private PropertyChangeListener propertyChangeListener; + private ItemListener itemListener; /** * Number of spaces between accelerator and menu item's label. */ - private int defaultAcceleratorLabelGap = 4; + private int defaultAcceleratorLabelGap = 10; /** + * The gap between different menus on the MenuBar. + */ + private int MenuGap = 10; + + /** A PropertyChangeListener to make UI updates after property changes **/ + PropertyChangeHandler propertyChangeListener; + + /** + * A class to handle PropertChangeEvents for the JMenuItem + * @author Anthony Balkissoon abalkiss at redhat dot com. + */ + class PropertyChangeHandler implements PropertyChangeListener + { + /** + * This method is called when a property of the menuItem is changed. + * Currently it is only used to update the accelerator key bindings. + * + * @param e + * the PropertyChangeEvent + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName() == "accelerator") + { + InputMap map = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); + if (map != null) + map.remove((KeyStroke)e.getOldValue()); + else + map = new ComponentInputMapUIResource(menuItem); + map.put((KeyStroke)e.getNewValue(), "doClick"); + } + } + } + + /** + * A class to handle accelerator keys. This is the Action we will + * perform when the accelerator key for this JMenuItem is pressed. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ + class ClickAction extends AbstractAction + { + /** + * This is what is done when the accelerator key for the JMenuItem is + * pressed. + */ + public void actionPerformed(ActionEvent event) + { + doClick(MenuSelectionManager.defaultManager()); + } + } + + /** * Creates a new BasicMenuItemUI object. */ public BasicMenuItemUI() @@ -173,14 +237,15 @@ public class BasicMenuItemUI extends MenuItemUI mouseInputListener = createMouseInputListener(menuItem); menuDragMouseListener = createMenuDragMouseListener(menuItem); menuKeyListener = createMenuKeyListener(menuItem); + itemListener = new ItemHandler(); propertyChangeListener = new PropertyChangeHandler(); } /** * Create MenuDragMouseListener to listen for mouse dragged events. - * - * @param c menu item to listen to - * + * + * @param c + * menu item to listen to * @return The MenuDragMouseListener */ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) @@ -189,11 +254,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Creates MenuKeyListener to listen to key events occuring when menu item - * is visible on the screen. - * - * @param c menu item to listen to - * + * Creates MenuKeyListener to listen to key events occuring when menu item is + * visible on the screen. + * + * @param c + * menu item to listen to * @return The MenuKeyListener */ protected MenuKeyListener createMenuKeyListener(JComponent c) @@ -203,9 +268,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Handles mouse input events occuring for this menu item - * - * @param c menu item to listen to - * + * + * @param c + * menu item to listen to * @return The MouseInputListener */ protected MouseInputListener createMouseInputListener(JComponent c) @@ -216,9 +281,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Factory method to create a BasicMenuItemUI for the given {@link * JComponent}, which should be a {@link JMenuItem}. - * - * @param c The {@link JComponent} a UI is being created for. - * + * + * @param c + * The {@link JComponent} a UI is being created for. * @return A BasicMenuItemUI for the {@link JComponent}. */ public static ComponentUI createUI(JComponent c) @@ -228,8 +293,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Programatically clicks menu item. - * - * @param msm MenuSelectionManager for the menu hierarchy + * + * @param msm + * MenuSelectionManager for the menu hierarchy */ protected void doClick(MenuSelectionManager msm) { @@ -239,9 +305,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns maximum size for the specified menu item - * - * @param c component for which to get maximum size - * + * + * @param c + * component for which to get maximum size * @return Maximum size for the specified menu item. */ public Dimension getMaximumSize(JComponent c) @@ -251,9 +317,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns minimum size for the specified menu item - * - * @param c component for which to get minimum size - * + * + * @param c + * component for which to get minimum size * @return Minimum size for the specified menu item. */ public Dimension getMinimumSize(JComponent c) @@ -263,9 +329,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns path to this menu item. - * - * @return $MenuElement[]$ Returns array of menu elements - * that constitute a path to this menu item. + * + * @return $MenuElement[]$ Returns array of menu elements that constitute a + * path to this menu item. */ public MenuElement[] getPath() { @@ -278,12 +344,12 @@ public class BasicMenuItemUI extends MenuItemUI Component c = menuItem; while (c instanceof MenuElement) { - path.add(0, (MenuElement) c); + path.add(0, (MenuElement) c); - if (c instanceof JPopupMenu) - c = ((JPopupMenu) c).getInvoker(); - else - c = c.getParent(); + if (c instanceof JPopupMenu) + c = ((JPopupMenu) c).getInvoker(); + else + c = c.getParent(); } MenuElement[] pathArray = new MenuElement[path.size()]; @@ -293,12 +359,15 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns preferred size for the given menu item. - * - * @param c menu item for which to get preferred size - * @param checkIcon check icon displayed in the given menu item - * @param arrowIcon arrow icon displayed in the given menu item - * @param defaultTextIconGap space between icon and text in the given menuItem - * + * + * @param c + * menu item for which to get preferred size + * @param checkIcon + * check icon displayed in the given menu item + * @param arrowIcon + * arrow icon displayed in the given menu item + * @param defaultTextIconGap + * space between icon and text in the given menuItem * @return $Dimension$ preferred size for the given menu item */ protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon, @@ -308,7 +377,7 @@ public class BasicMenuItemUI extends MenuItemUI JMenuItem m = (JMenuItem) c; Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m, defaultTextIconGap); - + // if menu item has accelerator then take accelerator's size into account // when calculating preferred size. KeyStroke accelerator = m.getAccelerator(); @@ -316,52 +385,57 @@ public class BasicMenuItemUI extends MenuItemUI if (accelerator != null) { - rect = getAcceleratorRect(accelerator, - m.getToolkit().getFontMetrics(acceleratorFont)); + rect = getAcceleratorRect( + accelerator, + m.getToolkit().getFontMetrics(acceleratorFont)); - // add width of accelerator's text - d.width = d.width + rect.width + defaultAcceleratorLabelGap; + // add width of accelerator's text + d.width += rect.width + defaultAcceleratorLabelGap; - // adjust the heigth of the preferred size if necessary - if (d.height < rect.height) - d.height = rect.height; + // adjust the heigth of the preferred size if necessary + if (d.height < rect.height) + d.height = rect.height; } if (checkIcon != null) { - d.width = d.width + checkIcon.getIconWidth() + defaultTextIconGap; + d.width += checkIcon.getIconWidth() + defaultTextIconGap; - if (checkIcon.getIconHeight() > d.height) - d.height = checkIcon.getIconHeight(); + if (checkIcon.getIconHeight() > d.height) + d.height = checkIcon.getIconHeight(); } if (arrowIcon != null && (c instanceof JMenu)) { - d.width = d.width + arrowIcon.getIconWidth() + defaultTextIconGap; - - if (arrowIcon.getIconHeight() > d.height) - d.height = arrowIcon.getIconHeight(); + int pWidth = m.getParent().getWidth(); + if (!((JMenu)c).isTopLevelMenu() && d.width < pWidth) + d.width = pWidth + - m.getInsets().left - m.getInsets().right; + else + d.width += arrowIcon.getIconWidth() + MenuGap; + + if (arrowIcon.getIconHeight() > d.height) + d.height = arrowIcon.getIconHeight(); } - + return d; } /** * Returns preferred size of the given component - * - * @param c component for which to return preferred size - * + * + * @param c + * component for which to return preferred size * @return $Dimension$ preferred size for the given component */ public Dimension getPreferredSize(JComponent c) { - return getPreferredMenuItemSize(c, checkIcon, arrowIcon, - defaultTextIconGap); + return getPreferredMenuItemSize(c, checkIcon, arrowIcon, defaultTextIconGap); } /** * Returns the prefix for entries in the {@link UIDefaults} table. - * + * * @return "MenuItem" */ protected String getPropertyPrefix() @@ -371,8 +445,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method installs the components for this {@link JMenuItem}. - * - * @param menuItem The {@link JMenuItem} to install components for. + * + * @param menuItem + * The {@link JMenuItem} to install components for. */ protected void installComponents(JMenuItem menuItem) { @@ -380,28 +455,27 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method installs the defaults that are defined in the Basic look and + * This method installs the defaults that are defined in the Basic look and * feel for this {@link JMenuItem}. */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuItem.setBackground(defaults.getColor("MenuItem.background")); - menuItem.setBorder(defaults.getBorder("MenuItem.border")); - menuItem.setFont(defaults.getFont("MenuItem.font")); - menuItem.setForeground(defaults.getColor("MenuItem.foreground")); - menuItem.setMargin(defaults.getInsets("MenuItem.margin")); - menuItem.setOpaque(true); - acceleratorFont = defaults.getFont("MenuItem.acceleratorFont"); - acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground"); - acceleratorSelectionForeground = defaults.getColor("MenuItem.acceleratorSelectionForeground"); - selectionBackground = defaults.getColor("MenuItem.selectionBackground"); - selectionForeground = defaults.getColor("MenuItem.selectionForeground"); - acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter"); - + String prefix = getPropertyPrefix(); + LookAndFeel.installBorder(menuItem, prefix + ".border"); + LookAndFeel.installColorsAndFont(menuItem, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + menuItem.setMargin(UIManager.getInsets(prefix + ".margin")); + acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont"); + acceleratorForeground = UIManager.getColor(prefix + ".acceleratorForeground"); + acceleratorSelectionForeground = UIManager.getColor(prefix + ".acceleratorSelectionForeground"); + selectionBackground = UIManager.getColor(prefix + ".selectionBackground"); + selectionForeground = UIManager.getColor(prefix + ".selectionForeground"); + acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter"); + checkIcon = UIManager.getIcon(prefix + ".checkIcon"); + menuItem.setHorizontalTextPosition(SwingConstants.TRAILING); menuItem.setHorizontalAlignment(SwingConstants.LEADING); + menuItem.setOpaque(true); } /** @@ -409,7 +483,17 @@ public class BasicMenuItemUI extends MenuItemUI */ protected void installKeyboardActions() { - // FIXME: Need to implement + InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); + if (focusedWindowMap == null) + focusedWindowMap = new ComponentInputMapUIResource(menuItem); + focusedWindowMap.put(menuItem.getAccelerator(), "doClick"); + SwingUtilities.replaceUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap); + + ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem); + if (UIActionMap == null) + UIActionMap = new ActionMapUIResource(); + UIActionMap.put("doClick", new ClickAction()); + SwingUtilities.replaceUIActionMap(menuItem, UIActionMap); } /** @@ -421,15 +505,17 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.addMouseMotionListener(mouseInputListener); menuItem.addMenuDragMouseListener(menuDragMouseListener); menuItem.addMenuKeyListener(menuKeyListener); + menuItem.addItemListener(itemListener); menuItem.addPropertyChangeListener(propertyChangeListener); } /** - * Installs and initializes all fields for this UI delegate. Any properties - * of the UI that need to be initialized and/or set to defaults will be - * done now. It will also install any listeners necessary. - * - * @param c The {@link JComponent} that is having this UI installed. + * Installs and initializes all fields for this UI delegate. Any properties of + * the UI that need to be initialized and/or set to defaults will be done now. + * It will also install any listeners necessary. + * + * @param c + * The {@link JComponent} that is having this UI installed. */ public void installUI(JComponent c) { @@ -438,13 +524,16 @@ public class BasicMenuItemUI extends MenuItemUI installDefaults(); installComponents(menuItem); installListeners(); + installKeyboardActions(); } /** * Paints given menu item using specified graphics context - * - * @param g The graphics context used to paint this menu item - * @param c Menu Item to paint + * + * @param g + * The graphics context used to paint this menu item + * @param c + * Menu Item to paint */ public void paint(Graphics g, JComponent c) { @@ -454,10 +543,13 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints background of the menu item - * - * @param g The graphics context used to paint this menu item - * @param menuItem menu item to paint - * @param bgColor Background color to use when painting menu item + * + * @param g + * The graphics context used to paint this menu item + * @param menuItem + * menu item to paint + * @param bgColor + * Background color to use when painting menu item */ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { @@ -470,15 +562,21 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints specified menu item - * - * @param g The graphics context used to paint this menu item - * @param c menu item to paint - * @param checkIcon check icon to use when painting menu item - * @param arrowIcon arrow icon to use when painting menu item - * @param background Background color of the menu item - * @param foreground Foreground color of the menu item - * @param defaultTextIconGap space to use between icon and - * text when painting menu item + * + * @param g + * The graphics context used to paint this menu item + * @param c + * menu item to paint + * @param checkIcon + * check icon to use when painting menu item + * @param arrowIcon + * arrow icon to use when painting menu item + * @param background + * Background color of the menu item + * @param foreground + * Foreground color of the menu item + * @param defaultTextIconGap + * space to use between icon and text when painting menu item */ protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, @@ -496,7 +594,7 @@ public class BasicMenuItemUI extends MenuItemUI int horAlign = m.getHorizontalAlignment(); int vertTextPos = m.getVerticalTextPosition(); int horTextPos = m.getHorizontalTextPosition(); - + Font f = m.getFont(); g.setFont(f); FontMetrics fm = g.getFontMetrics(f); @@ -504,8 +602,10 @@ public class BasicMenuItemUI extends MenuItemUI SwingUtilities.calculateInsetArea(br, m.getInsets(), vr); paintBackground(g, m, m.getBackground()); - /* MenuItems insets are equal to menuItems margin, space between text and - menuItems border. We need to paint insets region as well. */ + /* + * MenuItems insets are equal to menuItems margin, space between text and + * menuItems border. We need to paint insets region as well. + */ Insets insets = m.getInsets(); br.x -= insets.left; br.y -= insets.top; @@ -514,89 +614,93 @@ public class BasicMenuItemUI extends MenuItemUI // Menu item is considered to be highlighted when it is selected. // But we don't want to paint the background of JCheckBoxMenuItems - if ((m.isSelected() && checkIcon == null) || m.getModel().isArmed() && - (m.getParent() instanceof MenuElement)) + ButtonModel mod = m.getModel(); + if ((m.isSelected() && checkIcon == null) || (mod != null && + mod.isArmed()) + && (m.getParent() instanceof MenuElement)) { - if (m.isContentAreaFilled()) - { - g.setColor(selectionBackground); - g.fillRect(br.x, br.y, br.width, br.height); - } + if (m.isContentAreaFilled()) + { + g.setColor(selectionBackground); + g.fillRect(br.x, br.y, br.width, br.height); + } } else { - if (m.isContentAreaFilled()) - { - g.setColor(m.getBackground()); - g.fillRect(br.x, br.y, br.width, br.height); - } + if (m.isContentAreaFilled()) + { + g.setColor(m.getBackground()); + g.fillRect(br.x, br.y, br.width, br.height); + } } // If this menu item is a JCheckBoxMenuItem then paint check icon if (checkIcon != null) { - SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, - horAlign, vertTextPos, horTextPos, - vr, cr, tr, defaultTextIconGap); + SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, + horAlign, vertTextPos, horTextPos, + vr, cr, tr, defaultTextIconGap); checkIcon.paintIcon(m, g, cr.x, cr.y); - // We need to calculate position of the menu text and position of - // user menu icon if there exists one relative to the check icon. - // So we need to adjust view rectangle s.t. its starting point is at - // checkIcon.width + defaultTextIconGap. - vr.x = cr.x + cr.width + defaultTextIconGap; + // We need to calculate position of the menu text and position of + // user menu icon if there exists one relative to the check icon. + // So we need to adjust view rectangle s.t. its starting point is at + // checkIcon.width + defaultTextIconGap. + vr.x = cr.x + cr.width + defaultTextIconGap; } // if this is a submenu, then paint arrow icon to indicate it. if (arrowIcon != null && (c instanceof JMenu)) { - if (! ((JMenu) c).isTopLevelMenu()) - { - int width = arrowIcon.getIconWidth(); - int height = arrowIcon.getIconHeight(); - - arrowIcon.paintIcon(m, g, vr.width - width + defaultTextIconGap, - vr.y + 2); - } + if (!((JMenu) c).isTopLevelMenu()) + { + int width = arrowIcon.getIconWidth(); + int height = arrowIcon.getIconHeight(); + int offset = (vr.height - height) / 2; + arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset); + } } - // paint text and user menu icon if it exists + // paint text and user menu icon if it exists Icon i = m.getIcon(); - SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, tr, - defaultTextIconGap); + SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, vertAlign, + horAlign, vertTextPos, horTextPos, vr, + ir, tr, defaultTextIconGap); if (i != null) i.paintIcon(c, g, ir.x, ir.y); paintText(g, m, tr, m.getText()); - // paint accelerator + // paint accelerator String acceleratorText = ""; if (m.getAccelerator() != null) { - acceleratorText = getAcceleratorText(m.getAccelerator()); - fm = g.getFontMetrics(acceleratorFont); - ar.width = fm.stringWidth(acceleratorText); - ar.x = br.width - ar.width; - vr.x = br.width - ar.width; - - SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, ar, - defaultTextIconGap); - - paintAccelerator(g, m, ar, acceleratorText); + acceleratorText = getAcceleratorText(m.getAccelerator()); + fm = g.getFontMetrics(acceleratorFont); + ar.width = fm.stringWidth(acceleratorText); + ar.x = br.width - ar.width; + vr.x = br.width - ar.width - defaultTextIconGap; + + SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, + vertAlign, horAlign, vertTextPos, + horTextPos, vr, ir, ar, + defaultTextIconGap); + + paintAccelerator(g, m, ar, acceleratorText); } } /** * Paints label for the given menu item - * - * @param g The graphics context used to paint this menu item - * @param menuItem menu item for which to draw its label - * @param textRect rectangle specifiying position of the text relative to - * the given menu item - * @param text label of the menu item + * + * @param g + * The graphics context used to paint this menu item + * @param menuItem + * menu item for which to draw its label + * @param textRect + * rectangle specifiying position of the text relative to the given + * menu item + * @param text + * label of the menu item */ protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) @@ -605,43 +709,46 @@ public class BasicMenuItemUI extends MenuItemUI g.setFont(f); FontMetrics fm = g.getFontMetrics(f); - if (text != null && ! text.equals("")) + if (text != null && !text.equals("")) { - if (menuItem.isEnabled()) + if (menuItem.isEnabled()) { // Menu item is considered to be highlighted when it is selected. // But not if it's a JCheckBoxMenuItem - if ((menuItem.isSelected() && checkIcon == null) || menuItem.getModel().isArmed() && - (menuItem.getParent() instanceof MenuElement)) + ButtonModel mod = menuItem.getModel(); + if ((menuItem.isSelected() && checkIcon == null) + || (mod != null && mod.isArmed()) + && (menuItem.getParent() instanceof MenuElement)) g.setColor(selectionForeground); else g.setColor(menuItem.getForeground()); } - else - // FIXME: should fix this to use 'disabledForeground', but its - // default value in BasicLookAndFeel is null. - + else + // FIXME: should fix this to use 'disabledForeground', but its + // default value in BasicLookAndFeel is null. + // FIXME: should there be different foreground colours for selected // or deselected, when disabled? g.setColor(Color.gray); - int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); + int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); - if (mnemonicIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, - textRect.x, - textRect.y - + fm.getAscent()); - else - BasicGraphicsUtils.drawString(g, text, 0, textRect.x, - textRect.y + fm.getAscent()); + if (mnemonicIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, + textRect.x, + textRect.y + + fm.getAscent()); + else + BasicGraphicsUtils.drawString(g, text, 0, textRect.x, + textRect.y + fm.getAscent()); } } /** * This method uninstalls the components for this {@link JMenuItem}. - * - * @param menuItem The {@link JMenuItem} to uninstall components for. + * + * @param menuItem + * The {@link JMenuItem} to uninstall components for. */ protected void uninstallComponents(JMenuItem menuItem) { @@ -676,8 +783,9 @@ public class BasicMenuItemUI extends MenuItemUI * Uninstalls any keyboard actions. */ protected void uninstallKeyboardActions() - { - // FIXME: need to implement + { + SwingUtilities.replaceUIInputMap(menuItem, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); } /** @@ -688,15 +796,17 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.removeMouseListener(mouseInputListener); menuItem.removeMenuDragMouseListener(menuDragMouseListener); menuItem.removeMenuKeyListener(menuKeyListener); + menuItem.removeItemListener(itemListener); menuItem.removePropertyChangeListener(propertyChangeListener); } /** * Performs the opposite of installUI. Any properties or resources that need - * to be cleaned up will be done now. It will also uninstall any listeners - * it has. In addition, any properties of this UI will be nulled. - * - * @param c The {@link JComponent} that is having this UI uninstalled. + * to be cleaned up will be done now. It will also uninstall any listeners it + * has. In addition, any properties of this UI will be nulled. + * + * @param c + * The {@link JComponent} that is having this UI uninstalled. */ public void uninstallUI(JComponent c) { @@ -708,9 +818,11 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method calls paint. - * - * @param g The graphics context used to paint this menu item - * @param c The menu item to paint + * + * @param g + * The graphics context used to paint this menu item + * @param c + * The menu item to paint */ public void update(Graphics g, JComponent c) { @@ -719,9 +831,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Return text representation of the specified accelerator - * - * @param accelerator Accelerator for which to return string representation - * + * + * @param accelerator + * Accelerator for which to return string representation * @return $String$ Text representation of the given accelerator */ private String getAcceleratorText(KeyStroke accelerator) @@ -744,10 +856,11 @@ public class BasicMenuItemUI extends MenuItemUI /** * Calculates and return rectange in which accelerator should be displayed - * - * @param accelerator accelerator for which to return the display rectangle - * @param fm The font metrics used to measure the text - * + * + * @param accelerator + * accelerator for which to return the display rectangle + * @param fm + * The font metrics used to measure the text * @return $Rectangle$ reactangle which will be used to display accelerator */ private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm) @@ -759,12 +872,16 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints accelerator inside menu item - * - * @param g The graphics context used to paint the border - * @param menuItem Menu item for which to draw accelerator - * @param acceleratorRect rectangle representing position - * of the accelerator relative to the menu item - * @param acceleratorText accelerator's text + * + * @param g + * The graphics context used to paint the border + * @param menuItem + * Menu item for which to draw accelerator + * @param acceleratorRect + * rectangle representing position of the accelerator relative to the + * menu item + * @param acceleratorText + * accelerator's text */ private void paintAccelerator(Graphics g, JMenuItem menuItem, Rectangle acceleratorRect, @@ -785,10 +902,9 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This class handles mouse events occuring inside the menu item. - * Most of the events are forwarded for processing to MenuSelectionManager - * of the current menu hierarchy. - * + * This class handles mouse events occuring inside the menu item. Most of the + * events are forwarded for processing to MenuSelectionManager of the current + * menu hierarchy. */ protected class MouseInputHandler implements MouseInputListener { @@ -797,13 +913,15 @@ public class BasicMenuItemUI extends MenuItemUI */ protected MouseInputHandler() { + // Nothing to do here. } /** - * This method is called when mouse is clicked on the menu item. - * It forwards this event to MenuSelectionManager. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is clicked on the menu item. It forwards + * this event to MenuSelectionManager. + * + * @param e + * A {@link MouseEvent}. */ public void mouseClicked(MouseEvent e) { @@ -812,10 +930,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse is dragged inside the menu item. - * It forwards this event to MenuSelectionManager. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is dragged inside the menu item. It + * forwards this event to MenuSelectionManager. + * + * @param e + * A {@link MouseEvent}. */ public void mouseDragged(MouseEvent e) { @@ -824,29 +943,31 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse enters menu item. - * When this happens menu item is considered to be selected and selection path - * in MenuSelectionManager is set. This event is also forwarded to MenuSelection - * Manager for further processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse enters menu item. When this happens menu + * item is considered to be selected and selection path in + * MenuSelectionManager is set. This event is also forwarded to + * MenuSelection Manager for further processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseEntered(MouseEvent e) { Component source = (Component) e.getSource(); if (source.getParent() instanceof MenuElement) { - MenuSelectionManager manager = MenuSelectionManager.defaultManager(); - manager.setSelectedPath(getPath()); - manager.processMouseEvent(e); + MenuSelectionManager manager = MenuSelectionManager.defaultManager(); + manager.setSelectedPath(getPath()); + manager.processMouseEvent(e); } } /** - * This method is called when mouse exits menu item. The event is - * forwarded to MenuSelectionManager for processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse exits menu item. The event is forwarded + * to MenuSelectionManager for processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseExited(MouseEvent e) { @@ -855,10 +976,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse is inside the menu item. - * This event is forwarder to MenuSelectionManager for further processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is inside the menu item. This event is + * forwarder to MenuSelectionManager for further processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseMoved(MouseEvent e) { @@ -869,8 +991,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method is called when mouse is pressed. This event is forwarded to * MenuSelectionManager for further processing. - * - * @param e A {@link MouseEvent}. + * + * @param e + * A {@link MouseEvent}. */ public void mousePressed(MouseEvent e) { @@ -882,8 +1005,9 @@ public class BasicMenuItemUI extends MenuItemUI * This method is called when mouse is released. If the mouse is released * inside this menuItem, then this menu item is considered to be chosen and * the menu hierarchy should be closed. - * - * @param e A {@link MouseEvent}. + * + * @param e + * A {@link MouseEvent}. */ public void mouseReleased(MouseEvent e) { @@ -892,24 +1016,25 @@ public class BasicMenuItemUI extends MenuItemUI if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0 && e.getY() < size.height) { - manager.clearSelectedPath(); - menuItem.doClick(); + manager.clearSelectedPath(); + menuItem.doClick(); } else - manager.processMouseEvent(e); + manager.processMouseEvent(e); } } /** * This class handles mouse dragged events. */ - protected class MenuDragMouseHandler implements MenuDragMouseListener + private class MenuDragMouseHandler implements MenuDragMouseListener { /** * Tbis method is invoked when mouse is dragged over the menu item. - * - * @param e The MenuDragMouseEvent + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseDragged(MenuDragMouseEvent e) { @@ -918,10 +1043,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Tbis method is invoked when mouse enters the menu item while it is - * being dragged. - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse enters the menu item while it is being + * dragged. + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseEntered(MenuDragMouseEvent e) { @@ -930,27 +1056,29 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Tbis method is invoked when mouse exits the menu item while - * it is being dragged - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse exits the menu item while it is being + * dragged + * + * @param e the MenuDragMouseEvent */ public void menuDragMouseExited(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } /** - * Tbis method is invoked when mouse was dragged and released - * inside the menu item. - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse was dragged and released inside the + * menu item. + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseReleased(MenuDragMouseEvent e) { MenuElement[] path = e.getPath(); if (path[path.length - 1] instanceof JMenuItem) - ((JMenuItem) path[path.length - 1]).doClick(); + ((JMenuItem) path[path.length - 1]).doClick(); MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.clearSelectedPath(); @@ -961,50 +1089,63 @@ public class BasicMenuItemUI extends MenuItemUI * This class handles key events occuring when menu item is visible on the * screen. */ - protected class MenuKeyHandler implements MenuKeyListener + private class MenuKeyHandler implements MenuKeyListener { /** * This method is invoked when key has been pressed - * - * @param e A {@link MenuKeyEvent}. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyPressed(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** * This method is invoked when key has been pressed - * - * @param e A {@link MenuKeyEvent}. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyReleased(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** - * This method is invoked when key has been typed - * It handles the mnemonic key for the menu item. - * - * @param e A {@link MenuKeyEvent}. + * This method is invoked when key has been typed It handles the mnemonic + * key for the menu item. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyTyped(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } } - + /** - * Helper class that listens for changes to the properties of the {@link + * Helper class that listens for item changes to the properties of the {@link * JMenuItem}. */ - protected class PropertyChangeHandler implements PropertyChangeListener + private class ItemHandler implements ItemListener { /** - * This method is called when one of the menu item's properties change. + * This method is called when one of the menu item changes. * - * @param evt A {@link PropertyChangeEvent}. + * @param evt A {@link ItemEvent}. */ - public void propertyChange(PropertyChangeEvent evt) + public void itemStateChanged(ItemEvent evt) { + boolean state = false; + if (menuItem instanceof JCheckBoxMenuItem) + { + if (evt.getStateChange() == ItemEvent.SELECTED) + state = true; + ((JCheckBoxMenuItem) menuItem).setState(state); + } menuItem.revalidate(); menuItem.repaint(); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java index 30be592ee79..827cbb0f50d 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Component; import java.awt.Dimension; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; @@ -46,8 +47,8 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; -import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.LookAndFeel; import javax.swing.MenuSelectionManager; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -92,7 +93,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected ChangeListener createChangeListener(JComponent c) { - return new ChangeHandler(); + return new ChangeHandler((JMenu) c, this); } /** @@ -180,12 +181,6 @@ public class BasicMenuUI extends BasicMenuItemUI */ public Dimension getMaximumSize(JComponent c) { - // If this menu is in a popup menu, treat it like a regular JMenuItem - if (!((JMenu)c).isTopLevelMenu()) - { - JMenuItem menuItem = new JMenuItem(((JMenu)c).getText(), ((JMenu)c).getIcon()); - return menuItem.getMaximumSize(); - } return c.getPreferredSize(); } @@ -205,20 +200,17 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuItem.setBackground(defaults.getColor("Menu.background")); - menuItem.setBorder(defaults.getBorder("Menu.border")); - menuItem.setFont(defaults.getFont("Menu.font")); - menuItem.setForeground(defaults.getColor("Menu.foreground")); - menuItem.setMargin(defaults.getInsets("Menu.margin")); - acceleratorFont = defaults.getFont("Menu.acceleratorFont"); - acceleratorForeground = defaults.getColor("Menu.acceleratorForeground"); - acceleratorSelectionForeground = defaults.getColor("Menu.acceleratorSelectionForeground"); - selectionBackground = defaults.getColor("Menu.selectionBackground"); - selectionForeground = defaults.getColor("Menu.selectionForeground"); - arrowIcon = defaults.getIcon("Menu.arrowIcon"); - oldBorderPainted = defaults.getBoolean("Menu.borderPainted"); + LookAndFeel.installBorder(menuItem, "Menu.border"); + LookAndFeel.installColorsAndFont(menuItem, "Menu.background", + "Menu.foreground", "Menu.font"); + menuItem.setMargin(UIManager.getInsets("Menu.margin")); + acceleratorFont = UIManager.getFont("Menu.acceleratorFont"); + acceleratorForeground = UIManager.getColor("Menu.acceleratorForeground"); + acceleratorSelectionForeground = UIManager.getColor("Menu.acceleratorSelectionForeground"); + selectionBackground = UIManager.getColor("Menu.selectionBackground"); + selectionForeground = UIManager.getColor("Menu.selectionForeground"); + arrowIcon = UIManager.getIcon("Menu.arrowIcon"); + oldBorderPainted = UIManager.getBoolean("Menu.borderPainted"); menuItem.setOpaque(true); } @@ -245,6 +237,7 @@ public class BasicMenuUI extends BasicMenuItemUI protected void setupPostTimer(JMenu menu) { + // TODO: Implement this properly. } /** @@ -356,6 +349,7 @@ public class BasicMenuUI extends BasicMenuItemUI public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mousePressed(MouseEvent e) @@ -421,10 +415,13 @@ public class BasicMenuUI extends BasicMenuItemUI public void menuDeselected(MenuEvent e) { JMenu menu = (JMenu) menuItem; - if (menu.isTopLevelMenu()) - ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection(); - else - ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection(); + if (menu.getParent() != null) + { + if (menu.isTopLevelMenu()) + ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection(); + else + ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection(); + } } /** @@ -456,6 +453,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void propertyChange(PropertyChangeEvent e) { + // TODO: Implement this properly. } } @@ -464,9 +462,40 @@ public class BasicMenuUI extends BasicMenuItemUI */ public class ChangeHandler implements ChangeListener { + /** + * Not used. + */ + public boolean isSelected; + + /** + * Not used. + */ + public JMenu menu; + + /** + * Not used. + */ + public BasicMenuUI ui; + + /** + * Not used. + */ + public Component wasFocused; + + /** + * Not used. + */ + public ChangeHandler(JMenu m, BasicMenuUI ui) + { + // Not used. + } + + /** + * Not used. + */ public void stateChanged(ChangeEvent e) { - // FIXME: It seems that this class is not used anywhere + // Not used. } } @@ -506,6 +535,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseExited(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -516,6 +546,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseReleased(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } } @@ -532,6 +563,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyPressed(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -541,6 +573,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyReleased(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -551,6 +584,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyTyped(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java index c9f623259ba..6b37d315fa8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -70,8 +70,8 @@ import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; @@ -141,13 +141,14 @@ public class BasicOptionPaneUI extends OptionPaneUI optionPane); if (inf != null) { - try - { - inf.setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + inf.setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if attempt has been vetoed. + } } } } @@ -405,16 +406,30 @@ public class BasicOptionPaneUI extends OptionPaneUI || e.getPropertyName().equals(JOptionPane.WANTS_INPUT_PROPERTY) || e.getPropertyName().equals(JOptionPane.SELECTION_VALUES_PROPERTY)) { - optionPane.removeAll(); - messageAreaContainer = createMessageArea(); - optionPane.add(messageAreaContainer); - optionPane.add(buttonContainer); + optionPane.remove(messageAreaContainer); + messageAreaContainer = createMessageArea(); + optionPane.add(messageAreaContainer); + Container newButtons = createButtonArea(); + optionPane.remove(buttonContainer); + optionPane.add(newButtons); + buttonContainer = newButtons; + optionPane.add(buttonContainer); } optionPane.invalidate(); optionPane.repaint(); } } + /** + * The minimum width for JOptionPanes. + */ + public static final int MinimumWidth = 262; + + /** + * The minimum height for JOptionPanes. + */ + public static final int MinimumHeight = 90; + /** Whether the JOptionPane contains custom components. */ protected boolean hasCustomComponents = false; @@ -433,12 +448,6 @@ public class BasicOptionPaneUI extends OptionPaneUI /** The component that receives input when the JOptionPane needs it. */ protected JComponent inputComponent; - /** The minimum height of the JOptionPane. */ - public static int minimumHeight; - - /** The minimum width of the JOptionPane. */ - public static int minimumWidth; - /** The minimum dimensions of the JOptionPane. */ protected Dimension minimumSize; @@ -518,6 +527,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public void paintIcon(Component c, Graphics g, int x, int y) { + // Nothing to do here. } } @@ -637,6 +647,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public BasicOptionPaneUI() { + // Nothing to do here. } /** @@ -860,10 +871,10 @@ public class BasicOptionPaneUI extends OptionPaneUI addIcon(messageArea); JPanel rightSide = new JPanel(); - rightSide.setBorder(BorderFactory.createEmptyBorder(0, 11, 17, 0)); + rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); rightSide.setLayout(new GridBagLayout()); GridBagConstraints con = createConstraints(); - + addMessageComponents(rightSide, con, getMessage(), getMaxCharactersPerLineCount(), false); @@ -886,7 +897,7 @@ public class BasicOptionPaneUI extends OptionPaneUI } } - messageArea.add(rightSide, BorderLayout.EAST); + messageArea.add(rightSide, BorderLayout.CENTER); return messageArea; } @@ -944,8 +955,14 @@ public class BasicOptionPaneUI extends OptionPaneUI case JOptionPane.YES_NO_CANCEL_OPTION: return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING }; case JOptionPane.OK_CANCEL_OPTION: - case JOptionPane.DEFAULT_OPTION: return new Object[] { OK_STRING, CANCEL_STRING }; + case JOptionPane.DEFAULT_OPTION: + return (optionPane.getWantsInput() ) ? + new Object[] { OK_STRING, CANCEL_STRING } : + ( optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE ) ? + new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } : + // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE + new Object[] { OK_STRING }; } return null; } @@ -1142,21 +1159,17 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - optionPane.setFont(defaults.getFont("OptionPane.font")); - optionPane.setBackground(defaults.getColor("OptionPane.background")); - optionPane.setForeground(defaults.getColor("OptionPane.foreground")); - optionPane.setBorder(defaults.getBorder("OptionPane.border")); + LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background", + "OptionPane.foreground", + "OptionPane.font"); + LookAndFeel.installBorder(optionPane, "OptionPane.border"); optionPane.setOpaque(true); - messageBorder = defaults.getBorder("OptionPane.messageAreaBorder"); - messageForeground = defaults.getColor("OptionPane.messageForeground"); - buttonBorder = defaults.getBorder("OptionPane.buttonAreaBorder"); + messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); + messageForeground = UIManager.getColor("OptionPane.messageForeground"); + buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder"); - minimumSize = defaults.getDimension("OptionPane.minimumSize"); - minimumWidth = minimumSize.width; - minimumHeight = minimumSize.height; + minimumSize = UIManager.getDimension("OptionPane.minimumSize"); // FIXME: Image icons don't seem to work properly right now. // Once they do, replace the synthetic icons with these ones. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java index b715c57b360..783cec473bc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java @@ -40,8 +40,7 @@ package javax.swing.plaf.basic; import javax.swing.JComponent; import javax.swing.JPanel; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PanelUI; @@ -64,8 +63,29 @@ public class BasicPanelUI extends PanelUI public void installDefaults(JPanel p) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - p.setBackground(defaults.getColor("Panel.background")); + LookAndFeel.installColorsAndFont(p, "Panel.background", "Panel.foreground", + "Panel.font"); p.setOpaque(true); } + + /** + * Uninstalls this UI from the JPanel. + * + * @param c the JPanel from which to uninstall this UI + */ + public void uninstallUI(JComponent c) + { + uninstallDefaults((JPanel) c); + } + + /** + * Uninstalls the UI defaults that have been install through + * {@link #installDefaults}. + * + * @param p the panel from which to uninstall the UI defaults + */ + protected void uninstallDefaults(JPanel p) + { + // Nothing to do here. + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java index 044027b0b4a..76dcfc43559 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PasswordView; @@ -48,6 +49,7 @@ public class BasicPasswordFieldUI extends BasicTextFieldUI { public BasicPasswordFieldUI() { + // Nothing to do here. } public View create(Element elem) @@ -60,6 +62,11 @@ public class BasicPasswordFieldUI extends BasicTextFieldUI return new BasicPasswordFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "PasswordField" + */ protected String getPropertyPrefix() { return "PasswordField"; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java index 247117bc983..e15a17bab28 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -53,12 +53,11 @@ import javax.swing.JLayeredPane; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.event.MouseInputListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; @@ -131,12 +130,9 @@ public class BasicPopupMenuUI extends PopupMenuUI */ public void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - popupMenu.setBackground(defaults.getColor("PopupMenu.background")); - popupMenu.setBorder(defaults.getBorder("PopupMenu.border")); - popupMenu.setFont(defaults.getFont("PopupMenu.font")); - popupMenu.setForeground(defaults.getColor("PopupMenu.foreground")); + LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background", + "PopupMenu.foreground", "PopupMenu.font"); + LookAndFeel.installBorder(popupMenu, "PopupMenu.border"); popupMenu.setOpaque(true); } @@ -277,23 +273,26 @@ public class BasicPopupMenuUI extends PopupMenuUI RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities .getRoot(invoker); - ((Container) rootContainer).removeComponentListener(topWindowListener); - - // If this popup menu is the last popup menu visible on the screen, then - // stop interrupting mouse events in the glass pane before hiding this - // last popup menu. - boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu) - && ((JMenu) popupMenu.getInvoker()) - .isTopLevelMenu(); - - if (topLevelMenu || ! (popupMenu.getInvoker() instanceof MenuElement)) + if (rootContainer != null) { - // set glass pane not to interrupt mouse events and remove - // mouseInputListener - Container glassPane = (Container) rootContainer.getGlassPane(); - glassPane.setVisible(false); - glassPane.removeMouseListener(mouseInputListener); - mouseInputListener = null; + ((Container) rootContainer).removeComponentListener(topWindowListener); + + // If this popup menu is the last popup menu visible on the screen, + // then + // stop interrupting mouse events in the glass pane before hiding this + // last popup menu. + boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu) + && ((JMenu) popupMenu.getInvoker()).isTopLevelMenu(); + + if (topLevelMenu || !(popupMenu.getInvoker() instanceof MenuElement)) + { + // set glass pane not to interrupt mouse events and remove + // mouseInputListener + Container glassPane = (Container) rootContainer.getGlassPane(); + glassPane.setVisible(false); + glassPane.removeMouseListener(mouseInputListener); + mouseInputListener = null; + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java index d00628f53d4..88d949b1caf 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java @@ -46,21 +46,24 @@ import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.font.FontRenderContext; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JProgressBar; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; -import javax.swing.UIDefaults; import javax.swing.UIManager; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; @@ -110,14 +113,56 @@ public class BasicProgressBarUI extends ProgressBarUI { // Only need to listen for indeterminate changes. // All other things are done on a repaint. - if (e.getPropertyName().equals("inderterminate")) - if (((Boolean) e.getNewValue()).booleanValue()) - startAnimationTimer(); - else - stopAnimationTimer(); - else - progressBar.repaint(); + if (e.getPropertyName().equals("indeterminate")) + if (((Boolean) e.getNewValue()).booleanValue() + && progressBar.isShowing()) + startAnimationTimer(); + else + stopAnimationTimer(); + } + } + + /** + * Receives notification when the progressbar is becoming visible or + * invisible and starts/stops the animation timer accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class AncestorHandler implements AncestorListener + { + + /** + * Receives notification when the progressbar is becoming visible. This + * starts the animation timer if the progressbar is indeterminate. + * + * @param event the ancestor event + */ + public void ancestorAdded(AncestorEvent event) + { + if (progressBar.isIndeterminate()) + startAnimationTimer(); + } + + /** + * Receives notification when the progressbar is becoming invisible. This + * stops the animation timer if the progressbar is indeterminate. + * + * @param event the ancestor event + */ + public void ancestorRemoved(AncestorEvent event) + { + stopAnimationTimer(); + } + + /** + * Receives notification when an ancestor has been moved. We don't need to + * do anything here. + */ + public void ancestorMoved(AncestorEvent event) + { + // Nothing to do here. } + } /** @@ -141,6 +186,35 @@ public class BasicProgressBarUI extends ProgressBarUI } } + /** + * Receives notification when the size of the progress bar changes and + * invalidates the layout information for the box calculation in + * {@link BasicProgressBarUI#getBox(Rectangle)}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class ComponentHandler extends ComponentAdapter + { + /** + * Receives notification when the size of the progress bar changes and + * invalidates the layout information for the box calculation in + * {@link BasicProgressBarUI#getBox}. + * + * @param e the component event + */ + public void componentResized(ComponentEvent e) + { + boxDependent = -1; + boxIndependent = -1; + incr = -1; + } + } + + /** + * Holds the value of the bouncing box that is returned by {@link #getBox}. + */ + protected Rectangle boxRect; + /** The timer used to move the bouncing box. */ private transient Timer animationTimer; @@ -172,6 +246,27 @@ public class BasicProgressBarUI extends ProgressBarUI /** The progressBar for this UI. */ protected JProgressBar progressBar; + + /** + * The size of the box returned by {@link #getBox} in the orientation + * direction of the progress bar. This is package private to avoid accessor + * method. + */ + transient double boxDependent = - 1; + + /** + * The size of the box returned by {@link #getBox} against the orientation + * direction of the progress bar. This is package private to avoid accessor + * method. + */ + transient int boxIndependent = - 1; + + /** + * The increment for box animation. This is package private to avoid accessor + * method. + */ + transient double incr = -1; + /** The length of the cell. The cell is the painted part. */ private transient int cellLength; @@ -185,6 +280,18 @@ public class BasicProgressBarUI extends ProgressBarUI private transient Color selectionForeground; /** + * Listens for notification when the component becomes showing and + * starts/stops the animation timer. + */ + private AncestorListener ancestorListener; + + /** + * Listens for resize events on the progress bar and invalidates some + * layout info. + */ + private ComponentListener componentListener; + + /** * Creates a new BasicProgressBarUI object. */ public BasicProgressBarUI() @@ -248,48 +355,49 @@ public class BasicProgressBarUI extends ProgressBarUI { if (!progressBar.isIndeterminate()) return null; - //numFrames has to be an even number as defined by spec. - int iterations = numFrames / 2 + 1; + if (r == null) + r = new Rectangle(); - double boxDependent; - double boxIndependent; + Rectangle vr = new Rectangle(); + SwingUtilities.calculateInnerArea(progressBar, vr); - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) + // Recalculate the metrics only when size of the progressbar has changed. + if (incr == -1 || boxDependent == -1 || boxIndependent == -1) { - Dimension dims = getPreferredInnerHorizontal(); - boxDependent = (double) dims.width / iterations; - boxIndependent = dims.height; - } - else - { - Dimension dims = getPreferredInnerVertical(); - boxDependent = (double) dims.height / iterations; - boxIndependent = dims.width; + //numFrames has to be an even number as defined by spec. + int iterations = numFrames / 2; + if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) + { + boxDependent = vr.width / 6.; + incr = ((double) (vr.width - boxDependent)) / (double) iterations; + boxIndependent = vr.height; + } + else + { + boxDependent = vr.height / 6.; + incr = ((double) (vr.height - boxDependent)) / (double) iterations; + boxIndependent = vr.width; + } } - Rectangle vr = new Rectangle(); - SwingUtilities.calculateInnerArea(progressBar, vr); - int index = getAnimationIndex(); - if (animationIndex > (numFrames + 1) / 2) + if (animationIndex > (numFrames) / 2) index = numFrames - getAnimationIndex(); if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { - r.x = vr.x + (int) (index * boxDependent); - r.y = vr.y; - r.width = (int) boxDependent; - r.height = (int) boxIndependent; + r.x = vr.x + (int) (incr * index); + r.y = vr.y; + r.width = (int) boxDependent; + r.height = (int) boxIndependent; } else { - index++; - r.x = vr.x; - r.y = vr.height - (int) (index * boxDependent) + vr.y; - r.width = (int) boxIndependent; - r.height = (int) boxDependent; + r.x = vr.x; + r.y = vr.height - (int) (incr * index) + vr.y - (int) boxDependent; + r.width = (int) boxIndependent; + r.height = (int) boxDependent; } - return r; } @@ -324,7 +432,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + Insets insets = c.getInsets(); + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + { + ret = getPreferredInnerVertical(); + ret.height = Short.MAX_VALUE; + ret.width += insets.left + insets.right; + } + else + { + ret = getPreferredInnerHorizontal(); + ret.width = Short.MAX_VALUE; + ret.height += insets.top + insets.bottom; + } + return ret; } /** @@ -338,7 +461,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); + Insets insets = c.getInsets(); + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + { + ret = getPreferredInnerVertical(); + ret.height = 10; + ret.width += insets.left + insets.right; + } + else + { + ret = getPreferredInnerHorizontal(); + ret.width = 10; + ret.height += insets.top + insets.bottom; + } + return ret; } /** @@ -351,11 +489,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected Dimension getPreferredInnerHorizontal() { - Rectangle vr = new Rectangle(); + Font font = progressBar.getFont(); + FontMetrics fm = progressBar.getFontMetrics(font); - SwingUtilities.calculateInnerArea(progressBar, vr); + int stringWidth = 0; + String str = progressBar.getString(); + if (str != null) + stringWidth = fm.stringWidth(progressBar.getString()); + Insets i = progressBar.getInsets(); + int prefWidth = Math.max(200 - i.left - i.right, stringWidth); - return new Dimension(vr.width, vr.height); + int stringHeight = 0; + if (str != null) + stringHeight = fm.getHeight(); + int prefHeight = Math.max(16 - i.top - i.bottom, stringHeight); + + return new Dimension(prefWidth, prefHeight); } /** @@ -368,11 +517,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected Dimension getPreferredInnerVertical() { - Rectangle vr = new Rectangle(); + Font font = progressBar.getFont(); + FontMetrics fm = progressBar.getFontMetrics(font); - SwingUtilities.calculateInnerArea(progressBar, vr); + int stringWidth = 0; + String str = progressBar.getString(); + if (str != null) + stringWidth = fm.stringWidth(progressBar.getString()); + Insets i = progressBar.getInsets(); + int prefHeight = Math.max(200 - i.left - i.right, stringWidth); + + int stringHeight = 0; + if (str != null) + stringHeight = fm.getHeight(); + int prefWidth = Math.max(16 - i.top - i.bottom, stringHeight); - return new Dimension(vr.width, vr.height); + return new Dimension(prefWidth, prefHeight); } /** @@ -386,36 +546,16 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getPreferredSize(JComponent c) { - // The only thing we need to worry about is - // the text size. Insets insets = c.getInsets(); - - // make a fontrenderer context so that we can make assumptions about - // the string bounds - FontRenderContext ctx = new FontRenderContext(new AffineTransform(), - false, false); - Rectangle2D bounds = c.getFont().getStringBounds(progressBar.getString(), - ctx); - int textW = (int) bounds.getWidth(); - int textH = (int) bounds.getHeight(); - - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) - { - if (textH < 20) - textH = 20; - if (textW < 200) - textW = 200; - } + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + ret = getPreferredInnerVertical(); else - { - if (textH < 200) - textH = 200; - if (textW < 20) - textW = 20; - } - textW += insets.left + insets.right; - textH += insets.top + insets.bottom; - return new Dimension(textW, textH); + ret = getPreferredInnerHorizontal(); + ret.width += insets.left + insets.right; + ret.height += insets.top + insets.bottom; + return ret; } /** @@ -514,66 +654,22 @@ public class BasicProgressBarUI extends ProgressBarUI int min = progressBar.getMinimum(); int value = progressBar.getValue(); - Rectangle vr = new Rectangle(); - SwingUtilities.calculateInnerArea(c, vr); - - Rectangle or = c.getBounds(); - + Rectangle vr = SwingUtilities.calculateInnerArea(c, new Rectangle()); + Rectangle or = progressBar.getBounds(); Insets insets = c.getInsets(); int amountFull = getAmountFull(insets, or.width, or.height); - g.setColor(c.getBackground()); - g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false); - - if (max != min && len != 0 && value > min) - { - int iterations = value / (space + len); - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { - double spaceInUnits = space * (double) vr.width / (max - min); - double lenInUnits = len * (double) vr.width / (max - min); - double currX = vr.x; - g.setColor(c.getForeground()); - g.fill3DRect(vr.x, vr.y, amountFull, vr.height, true); - - g.setColor(c.getBackground()); - if (spaceInUnits != 0) - { - for (int i = 0; i < iterations; i++) - { - currX += lenInUnits; - g.fill3DRect((int) currX, vr.y, (int) spaceInUnits, - vr.height, true); - currX += spaceInUnits; - } - } + g.fillRect(vr.x, vr.y, amountFull, vr.height); } else { - double currY = vr.y; - double spaceInUnits = space * (double) vr.height / (max - min); - double lenInUnits = len * (double) vr.height / (max - min); - g.setColor(c.getForeground()); - g.fill3DRect(vr.x, vr.y + vr.height - amountFull, vr.width, - amountFull, true); - - g.setColor(c.getBackground()); - - if (spaceInUnits != 0) - { - for (int i = 0; i < iterations; i++) - { - currY -= lenInUnits + spaceInUnits; - g.fill3DRect(vr.x, (int) currY, vr.width, - (int) spaceInUnits, true); - } - } + g.fillRect(vr.x, vr.y + vr.height - amountFull, vr.width, amountFull); } - } if (progressBar.isStringPainted() && !progressBar.getString().equals("")) paintString(g, 0, 0, or.width, or.height, amountFull, insets); @@ -599,13 +695,12 @@ public class BasicProgressBarUI extends ProgressBarUI SwingUtilities.calculateInnerArea(c, vr); g.setColor(c.getBackground()); - g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false); + g.fillRect(vr.x, vr.y, vr.width, vr.height); - Rectangle box = new Rectangle(); - getBox(box); + boxRect = getBox(boxRect); g.setColor(c.getForeground()); - g.fill3DRect(box.x, box.y, box.width, box.height, true); + g.fillRect(boxRect.x, boxRect.y, boxRect.width, boxRect.height); if (progressBar.isStringPainted() && !progressBar.getString().equals("")) paintString(g, 0, 0, or.width, or.height, @@ -628,23 +723,34 @@ public class BasicProgressBarUI extends ProgressBarUI protected void paintString(Graphics g, int x, int y, int width, int height, int amountFull, Insets b) { + // FIXME: We do not support vertical text painting because Java2D is needed + // for this. + if (progressBar.getOrientation() == JProgressBar.VERTICAL) + return; + // We want to place in the exact center of the bar. Point placement = getStringPlacement(g, progressBar.getString(), x + b.left, y + b.top, width - b.left - b.right, height - b.top - b.bottom); - Color saved = g.getColor(); - - // FIXME: The Color of the text should use selectionForeground and selectionBackground - // but that can't be done right now, so we'll use white in the mean time. - g.setColor(Color.WHITE); + Color savedColor = g.getColor(); + Shape savedClip = g.getClip(); FontMetrics fm = g.getFontMetrics(progressBar.getFont()); + int full = getAmountFull(b, width, height); + String str = progressBar.getString(); - g.drawString(progressBar.getString(), placement.x, - placement.y + fm.getAscent()); - - g.setColor(saved); + // We draw this string two times with different clips so that the text + // over the filled area is painted with selectionForeground and over + // the clear area with selectionBackground. + g.setColor(getSelectionForeground()); + g.setClip(0, 0, full + b.left, height); + g.drawString(str, placement.x, placement.y + fm.getAscent()); + g.setColor(getSelectionBackground()); + g.setClip(full + b.left, 0, width - full, height); + g.drawString(str, placement.x, placement.y + fm.getAscent()); + g.setClip(savedClip); + g.setColor(savedColor); } /** @@ -712,21 +818,19 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - progressBar.setFont(defaults.getFont("ProgressBar.font")); - progressBar.setForeground(defaults.getColor("ProgressBar.foreground")); - progressBar.setBackground(defaults.getColor("ProgressBar.background")); - progressBar.setBorder(defaults.getBorder("ProgressBar.border")); + LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background", + "ProgressBar.foreground", + "ProgressBar.font"); + LookAndFeel.installBorder(progressBar, "ProgressBar.border"); progressBar.setOpaque(true); - selectionForeground = defaults.getColor("ProgressBar.selectionForeground"); - selectionBackground = defaults.getColor("ProgressBar.selectionBackground"); - cellLength = defaults.getInt("ProgressBar.cellLength"); - cellSpacing = defaults.getInt("ProgressBar.cellSpacing"); + selectionForeground = UIManager.getColor("ProgressBar.selectionForeground"); + selectionBackground = UIManager.getColor("ProgressBar.selectionBackground"); + cellLength = UIManager.getInt("ProgressBar.cellLength"); + cellSpacing = UIManager.getInt("ProgressBar.cellSpacing"); - int repaintInterval = defaults.getInt("ProgressBar.repaintInterval"); - int cycleTime = defaults.getInt("ProgressBar.cycleTime"); + int repaintInterval = UIManager.getInt("ProgressBar.repaintInterval"); + int cycleTime = UIManager.getInt("ProgressBar.cycleTime"); if (cycleTime % repaintInterval != 0 && (cycleTime / repaintInterval) % 2 != 0) @@ -768,6 +872,12 @@ public class BasicProgressBarUI extends ProgressBarUI progressBar.addChangeListener(changeListener); progressBar.addPropertyChangeListener(propertyListener); animationTimer.addActionListener(animation); + + ancestorListener = new AncestorHandler(); + progressBar.addAncestorListener(ancestorListener); + + componentListener = new ComponentHandler(); + progressBar.addComponentListener(componentListener); } /** @@ -783,6 +893,14 @@ public class BasicProgressBarUI extends ProgressBarUI changeListener = null; propertyListener = null; animation = null; + + if (ancestorListener != null) + progressBar.removeAncestorListener(ancestorListener); + ancestorListener = null; + + if (componentListener != null) + progressBar.removeComponentListener(componentListener); + componentListener = null; } /** @@ -806,6 +924,8 @@ public class BasicProgressBarUI extends ProgressBarUI installDefaults(); installListeners(); } + if (progressBar.isIndeterminate()) + startAnimationTimer(); } /** @@ -824,4 +944,5 @@ public class BasicProgressBarUI extends ProgressBarUI animationTimer = null; progressBar = null; } + } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java index fa74a008bae..8af5ff7f95c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java @@ -59,8 +59,6 @@ public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI public BasicRadioButtonMenuItemUI() { super(); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - checkIcon = defaults.getIcon("RadioButtonMenuItem.checkIcon"); } /** @@ -98,5 +96,7 @@ public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI MenuElement[] path, MenuSelectionManager manager) { + // TODO: May not be implemented properly. + item.processMouseEvent(e, path, manager); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java index fbd21241a0d..f3698e85908 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Rectangle; @@ -93,6 +95,10 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI b.setIcon(icon); if (b.getSelectedIcon() == null) b.setSelectedIcon(icon); + if (b.getDisabledIcon() == null) + b.setDisabledIcon(icon); + if (b.getDisabledSelectedIcon() == null) + b.setDisabledSelectedIcon(icon); } /** @@ -139,10 +145,14 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI g.setFont(f); Icon currentIcon = null; - if (b.isSelected()) + if (b.isSelected() && b.isEnabled()) currentIcon = b.getSelectedIcon(); - else + else if (!b.isSelected() && b.isEnabled()) currentIcon = b.getIcon(); + else if (b.isSelected() && !b.isEnabled()) + currentIcon = b.getDisabledSelectedIcon(); + else // (!b.isSelected() && !b.isEnabled()) + currentIcon = b.getDisabledIcon(); SwingUtilities.calculateInnerArea(b, vr); String text = SwingUtilities.layoutCompoundLabel @@ -157,6 +167,25 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI } if (text != null) paintText(g, b, tr, text); - paintFocus(g, b, vr, tr, ir); + // TODO: Figure out what is the size parameter? + if (b.hasFocus() && b.isFocusPainted() && b.isEnabled()) + paintFocus(g, tr, null); + } + + /** + * Paints the focus indicator for JRadioButtons. + * + * @param g the graphics context + * @param tr the rectangle for the text label + * @param size the size (??) + */ + // TODO: Figure out what for is the size parameter. + protected void paintFocus(Graphics g, Rectangle tr, Dimension size) + { + Color focusColor = UIManager.getColor(getPropertyPrefix() + ".focus"); + Color saved = g.getColor(); + g.setColor(focusColor); + g.drawRect(tr.x, tr.y, tr.width, tr.height); + g.setColor(saved); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java index d97f7baea67..2a698e8a162 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java @@ -42,6 +42,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; +import javax.swing.JRootPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.RootPaneUI; @@ -56,11 +57,127 @@ public class BasicRootPaneUI extends RootPaneUI public void installUI(JComponent c) { - c.setBackground(UIManager.getColor("control")); super.installUI(c); + if (c instanceof JRootPane) + { + JRootPane rp = (JRootPane) c; + installDefaults(rp); + installComponents(rp); + installListeners(rp); + installKeyboardActions(rp); + } + } + + /** + * Installs the look and feel defaults for JRootPane. + * + * @param rp the root pane to install the defaults to + */ + protected void installDefaults(JRootPane rp) + { + // Is this ok? + rp.setBackground(UIManager.getColor("control")); + } + + /** + * Installs additional look and feel components to the root pane. + * + * @param rp the root pane to install the components to + */ + protected void installComponents(JRootPane rp) + { + // All components are initialized in the JRootPane constructor, and since + // the createXXXPane methods are protected, I see no reasonable way, + // and no need to initialize them here. This method is here anyway + // for compatibility and to provide the necessary hooks to subclasses. + } + + /** + * Installs any look and feel specific listeners on the root pane. + * + * @param rp the root pane to install the listeners to + */ + protected void installListeners(JRootPane rp) + { + rp.addPropertyChangeListener(this); + } + + /** + * Installs look and feel keyboard actions on the root pane. + * + * @param rp the root pane to install the keyboard actions to + */ + protected void installKeyboardActions(JRootPane rp) + { + // We currently do not install any keyboard actions here. + // This method is here anyway for compatibility and to provide + // the necessary hooks to subclasses. } public void propertyChange(PropertyChangeEvent event) { + // TODO: Implement this properly. + } + + /** + * Uninstalls this UI from the root pane. This calls + * {@link #uninstallDefaults}, {@link #uninstallComponents}, + * {@link #uninstallListeners}, {@link #uninstallKeyboardActions} + * in this order. + * + * @param c the root pane to uninstall the UI from + */ + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + if (c instanceof JRootPane) + { + JRootPane rp = (JRootPane) c; + uninstallDefaults(rp); + uninstallComponents(rp); + uninstallListeners(rp); + uninstallKeyboardActions(rp); + } + } + + /** + * Uninstalls the look and feel defaults that have been installed in + * {@link #installDefaults}. + * + * @param rp the root pane to uninstall the defaults from + */ + protected void uninstallDefaults(JRootPane rp) + { + // We do nothing here. + } + + /** + * Uninstalls look and feel components from the root pane. + * + * @param rp the root pane to uninstall the components from + */ + protected void uninstallComponents(JRootPane rp) + { + // We do nothing here. + } + + /** + * Uninstalls any look and feel specific listeners from the root pane. + * + * @param rp the root pane to uninstall the listeners from + */ + protected void uninstallListeners(JRootPane rp) + { + rp.removePropertyChangeListener(this); + } + + /** + * Uninstalls look and feel keyboard actions from the root pane. + * + * @param rp the root pane to uninstall the keyboard actions from + */ + protected void uninstallKeyboardActions(JRootPane rp) + { + // We do nothing here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java index 22242afcd8a..2f5eaf391e1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -58,6 +58,7 @@ import javax.swing.BoundedRangeModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JScrollBar; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; @@ -80,6 +81,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected class ArrowButtonListener extends MouseAdapter { + /** * Move the thumb in the direction specified by the button's arrow. If * this button is held down, then it should keep moving the thumb. @@ -91,9 +93,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, scrollTimer.stop(); scrollListener.setScrollByBlock(false); if (e.getSource() == incrButton) - scrollListener.setDirection(POSITIVE_SCROLL); - else - scrollListener.setDirection(NEGATIVE_SCROLL); + scrollListener.setDirection(POSITIVE_SCROLL); + else if (e.getSource() == decrButton) + scrollListener.setDirection(NEGATIVE_SCROLL); + scrollTimer.setDelay(100); scrollTimer.start(); } @@ -105,6 +108,11 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, public void mouseReleased(MouseEvent e) { scrollTimer.stop(); + scrollTimer.setDelay(300); + if (e.getSource() == incrButton) + scrollByUnit(POSITIVE_SCROLL); + else if (e.getSource() == decrButton) + scrollByUnit(NEGATIVE_SCROLL); } } @@ -120,9 +128,8 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void stateChanged(ChangeEvent e) { - // System.err.println(this + ".stateChanged()"); calculatePreferredSize(); - getThumbBounds(); + updateThumbRect(); scrollbar.repaint(); } } @@ -141,31 +148,27 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { if (e.getPropertyName().equals("model")) { - ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener); - scrollbar.getModel().addChangeListener(modelListener); - getThumbBounds(); + ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener); + scrollbar.getModel().addChangeListener(modelListener); + updateThumbRect(); } else if (e.getPropertyName().equals("orientation")) { - incrButton.removeMouseListener(buttonListener); - decrButton.removeMouseListener(buttonListener); - int orientation = scrollbar.getOrientation(); - switch (orientation) - { - case (JScrollBar.HORIZONTAL): - incrButton = createIncreaseButton(EAST); - decrButton = createDecreaseButton(WEST); - break; - default: - incrButton = createIncreaseButton(SOUTH); - decrButton = createDecreaseButton(NORTH); - break; - } - incrButton.addMouseListener(buttonListener); - decrButton.addMouseListener(buttonListener); - calculatePreferredSize(); + uninstallListeners(); + uninstallComponents(); + uninstallDefaults(); + installDefaults(); + installComponents(); + installListeners(); + } + else if (e.getPropertyName().equals("enabled")) + { + Boolean b = (Boolean) e.getNewValue(); + if (incrButton != null) + incrButton.setEnabled(b.booleanValue()); + if (decrButton != null) + decrButton.setEnabled(b.booleanValue()); } - scrollbar.repaint(); } } @@ -233,19 +236,19 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { if (block) { - // Only need to check it if it's block scrolling - // We only block scroll if the click occurs - // in the track. - if (! trackListener.shouldScroll(direction)) - { - trackHighlight = NO_HIGHLIGHT; - scrollbar.repaint(); - return; - } - scrollByBlock(direction); + // Only need to check it if it's block scrolling + // We only block scroll if the click occurs + // in the track. + if (!trackListener.shouldScroll(direction)) + { + trackHighlight = NO_HIGHLIGHT; + scrollbar.repaint(); + return; + } + scrollByBlock(direction); } else - scrollByUnit(direction); + scrollByUnit(direction); } } @@ -316,9 +319,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, else value = valueForYPosition(currentMouseY); - if (value == scrollbar.getValue()) - return; - if (! thumbRect.contains(e.getPoint())) { scrollTimer.stop(); @@ -333,6 +333,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, trackHighlight = DECREASE_HIGHLIGHT; scrollListener.setDirection(NEGATIVE_SCROLL); } + scrollTimer.setDelay(100); scrollTimer.start(); } else @@ -343,8 +344,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, // "lower" edge of the thumb. The value at which // the cursor is at must be greater or equal // to that value. + + scrollListener.setScrollByBlock(false); scrollbar.setValueIsAdjusting(true); - offset = value - scrollbar.getValue(); + offset = value - scrollbar.getValue(); } scrollbar.repaint(); } @@ -357,11 +360,19 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void mouseReleased(MouseEvent e) { - trackHighlight = NO_HIGHLIGHT; scrollTimer.stop(); + scrollTimer.setDelay(300); + currentMouseX = e.getX(); + currentMouseY = e.getY(); - if (scrollbar.getValueIsAdjusting()) - scrollbar.setValueIsAdjusting(false); + if (shouldScroll(POSITIVE_SCROLL)) + scrollByBlock(POSITIVE_SCROLL); + else if (shouldScroll(NEGATIVE_SCROLL)) + scrollByBlock(NEGATIVE_SCROLL); + + trackHighlight = NO_HIGHLIGHT; + scrollListener.setScrollByBlock(false); + scrollbar.setValueIsAdjusting(true); scrollbar.repaint(); } @@ -381,6 +392,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, else value = valueForYPosition(currentMouseY); + if (thumbRect.contains(currentMouseX, currentMouseY)) + return false; + if (direction == POSITIVE_SCROLL) return (value > scrollbar.getValue()); else @@ -517,11 +531,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected JButton createIncreaseButton(int orientation) { - if (incrButton == null) - incrButton = new BasicArrowButton(orientation); - else - ((BasicArrowButton) incrButton).setDirection(orientation); - return incrButton; + return new BasicArrowButton(orientation); } /** @@ -534,11 +544,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected JButton createDecreaseButton(int orientation) { - if (decrButton == null) - decrButton = new BasicArrowButton(orientation); - else - ((BasicArrowButton) decrButton).setDirection(orientation); - return decrButton; + return new BasicArrowButton(orientation); } /** @@ -602,7 +608,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** @@ -644,7 +650,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ void calculatePreferredSize() { - // System.err.println(this + ".calculatePreferredSize()"); int height; int width; height = width = 0; @@ -707,48 +712,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected Rectangle getThumbBounds() { - int max = scrollbar.getMaximum(); - int min = scrollbar.getMinimum(); - int value = scrollbar.getValue(); - int extent = scrollbar.getVisibleAmount(); - - // System.err.println(this + ".getThumbBounds()"); - if (max == min) - { - thumbRect.x = trackRect.x; - thumbRect.y = trackRect.y; - if (scrollbar.getOrientation() == HORIZONTAL) - { - thumbRect.width = getMinimumThumbSize().width; - thumbRect.height = trackRect.height; - } - else - { - thumbRect.width = trackRect.width; - thumbRect.height = getMinimumThumbSize().height; - } - return thumbRect; - } - - if (scrollbar.getOrientation() == HORIZONTAL) - { - thumbRect.x = trackRect.x; - thumbRect.x += (value - min) * trackRect.width / (max - min); - thumbRect.y = trackRect.y; - - thumbRect.width = Math.max(extent * trackRect.width / (max - min), - getMinimumThumbSize().width); - thumbRect.height = trackRect.height; - } - else - { - thumbRect.x = trackRect.x; - thumbRect.y = trackRect.y + value * trackRect.height / (max - min); - - thumbRect.width = trackRect.width; - thumbRect.height = Math.max(extent * trackRect.height / (max - min), - getMinimumThumbSize().height); - } return thumbRect; } @@ -760,22 +723,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected Rectangle getTrackBounds() { - SwingUtilities.calculateInnerArea(scrollbar, trackRect); - - if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) - { - trackRect.width -= incrButton.getPreferredSize().getWidth(); - trackRect.width -= decrButton.getPreferredSize().getWidth(); - - trackRect.x += decrButton.getPreferredSize().getWidth(); - } - else - { - trackRect.height -= incrButton.getPreferredSize().getHeight(); - trackRect.height -= decrButton.getPreferredSize().getHeight(); - - trackRect.y += incrButton.getPreferredSize().getHeight(); - } return trackRect; } @@ -785,6 +732,18 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void installComponents() { + if (incrButton != null) + scrollbar.add(incrButton); + if (decrButton != null) + scrollbar.add(decrButton); + } + + /** + * This method installs the defaults for the scrollbar specified by the + * Basic Look and Feel. + */ + protected void installDefaults() + { int orientation = scrollbar.getOrientation(); switch (orientation) { @@ -797,31 +756,20 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, decrButton = createDecreaseButton(NORTH); break; } - scrollbar.add(incrButton); - scrollbar.add(decrButton); - } - - /** - * This method installs the defaults for the scrollbar specified by the - * Basic Look and Feel. - */ - protected void installDefaults() - { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - scrollbar.setForeground(defaults.getColor("ScrollBar.foreground")); - scrollbar.setBackground(defaults.getColor("ScrollBar.background")); - scrollbar.setBorder(defaults.getBorder("ScrollBar.border")); + LookAndFeel.installColors(scrollbar, "ScrollBar.background", + "ScrollBar.foreground"); + LookAndFeel.installBorder(scrollbar, "ScrollBar.border"); scrollbar.setOpaque(true); scrollbar.setLayout(this); - thumbColor = defaults.getColor("ScrollBar.thumb"); - thumbDarkShadowColor = defaults.getColor("ScrollBar.thumbDarkShadow"); - thumbHighlightColor = defaults.getColor("ScrollBar.thumbHighlight"); - thumbLightShadowColor = defaults.getColor("ScrollBar.thumbShadow"); + thumbColor = UIManager.getColor("ScrollBar.thumb"); + thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow"); + thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight"); + thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow"); - maximumThumbSize = defaults.getDimension("ScrollBar.maximumThumbSize"); - minimumThumbSize = defaults.getDimension("ScrollBar.minimumThumbSize"); + maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize"); + minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize"); } /** @@ -873,11 +821,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, trackRect = new Rectangle(); thumbRect = new Rectangle(); - scrollTimer = new Timer(200, null); - scrollTimer.setRepeats(true); + scrollTimer = new Timer(300, null); + installDefaults(); installComponents(); - installDefaults(); configureScrollBarColors(); installListeners(); @@ -908,17 +855,20 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void layoutHScrollbar(JScrollBar sb) { - // All we have to do is layout the 2 buttons? Rectangle vr = new Rectangle(); SwingUtilities.calculateInnerArea(scrollbar, vr); - // Update the rectangles. - getTrackBounds(); - getThumbBounds(); - Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); + + // calculate and update the track bounds + SwingUtilities.calculateInnerArea(scrollbar, trackRect); + trackRect.width -= incrDims.getWidth(); + trackRect.width -= decrDims.getWidth(); + trackRect.x += decrDims.getWidth(); + updateThumbRect(); + decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height); incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width, trackRect.height); @@ -934,12 +884,16 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, Rectangle vr = new Rectangle(); SwingUtilities.calculateInnerArea(scrollbar, vr); - // Update rectangles - getTrackBounds(); - getThumbBounds(); - Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); + + // Update rectangles + SwingUtilities.calculateInnerArea(scrollbar, trackRect); + trackRect.height -= incrDims.getHeight(); + trackRect.height -= decrDims.getHeight(); + trackRect.y += decrDims.getHeight(); + + updateThumbRect(); decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height); incrButton.setBounds(vr.x, trackRect.y + trackRect.height, @@ -947,6 +901,58 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, } /** + * Updates the thumb rect. + */ + void updateThumbRect() + { + int max = scrollbar.getMaximum(); + int min = scrollbar.getMinimum(); + int value = scrollbar.getValue(); + int extent = scrollbar.getVisibleAmount(); + if (max - extent <= min) + { + if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = getMinimumThumbSize().width; + thumbRect.height = trackRect.height; + } + else + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = trackRect.width; + thumbRect.height = getMinimumThumbSize().height; + } + } + else + { + if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) + { + thumbRect.x = trackRect.x; + thumbRect.width = Math.max(extent * trackRect.width / (max - min), + getMinimumThumbSize().width); + int availableWidth = trackRect.width - thumbRect.width; + thumbRect.x += (value - min) * availableWidth / (max - min - extent); + thumbRect.y = trackRect.y; + thumbRect.height = trackRect.height; + } + else + { + thumbRect.x = trackRect.x; + thumbRect.height = Math.max(extent * trackRect.height / (max - min), + getMinimumThumbSize().height); + int availableHeight = trackRect.height - thumbRect.height; + thumbRect.y = trackRect.y + + (value - min) * availableHeight / (max - min - extent); + thumbRect.width = trackRect.width; + } + } + + } + + /** * This method returns the minimum size required for the layout. * * @param scrollbarContainer The Container that is laid out. @@ -1124,10 +1130,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void uninstallComponents() { - scrollbar.remove(incrButton); - scrollbar.remove(decrButton); - incrButton = null; - decrButton = null; + if (incrButton != null) + scrollbar.remove(incrButton); + if (decrButton != null) + scrollbar.remove(decrButton); } /** @@ -1138,7 +1144,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { scrollbar.setForeground(null); scrollbar.setBackground(null); - scrollbar.setBorder(null); + LookAndFeel.uninstallBorder(scrollbar); + incrButton = null; + decrButton = null; } /** @@ -1155,17 +1163,22 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void uninstallListeners() { - scrollTimer.removeActionListener(scrollListener); + if (scrollTimer != null) + scrollTimer.removeActionListener(scrollListener); - scrollbar.getModel().removeChangeListener(modelListener); - scrollbar.removePropertyChangeListener(propertyChangeListener); - - decrButton.removeMouseListener(buttonListener); - incrButton.removeMouseListener(buttonListener); - - scrollbar.removeMouseListener(trackListener); - scrollbar.removeMouseMotionListener(trackListener); + if (scrollbar != null) + { + scrollbar.getModel().removeChangeListener(modelListener); + scrollbar.removePropertyChangeListener(propertyChangeListener); + scrollbar.removeMouseListener(trackListener); + scrollbar.removeMouseMotionListener(trackListener); + } + if (decrButton != null) + decrButton.removeMouseListener(buttonListener); + if (incrButton != null) + incrButton.removeMouseListener(buttonListener); + propertyChangeListener = null; modelListener = null; buttonListener = null; @@ -1182,8 +1195,8 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void uninstallUI(JComponent c) { - uninstallDefaults(); uninstallListeners(); + uninstallDefaults(); uninstallComponents(); scrollTimer = null; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java index bd1576f37a5..808ed2763e9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -40,13 +40,21 @@ package javax.swing.plaf.basic; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; +import javax.swing.JViewport; +import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneLayout; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ScrollPaneUI; @@ -54,9 +62,207 @@ public class BasicScrollPaneUI extends ScrollPaneUI implements ScrollPaneConstants { + /** + * Listens for changes in the state of the horizontal scrollbar's model and + * updates the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class HSBChangeListener implements ChangeListener + { + + /** + * Receives notification when the state of the horizontal scrollbar + * model has changed. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + JViewport vp = scrollpane.getViewport(); + Point viewPosition = vp.getViewPosition(); + int xpos = hsb.getValue(); + + if (xpos != viewPosition.x) + { + viewPosition.x = xpos; + vp.setViewPosition(viewPosition); + } + + viewPosition.y = 0; + JViewport columnHeader = scrollpane.getColumnHeader(); + if (columnHeader != null + && !columnHeader.getViewPosition().equals(viewPosition)) + columnHeader.setViewPosition(viewPosition); + } + + } + + /** + * Listens for changes in the state of the vertical scrollbar's model and + * updates the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class VSBChangeListener implements ChangeListener + { + + /** + * Receives notification when the state of the vertical scrollbar + * model has changed. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + JViewport vp = scrollpane.getViewport(); + Point viewPosition = vp.getViewPosition(); + int ypos = vsb.getValue(); + if (ypos != viewPosition.y) + { + viewPosition.y = ypos; + vp.setViewPosition(viewPosition); + } + + viewPosition.x = 0; + JViewport rowHeader = scrollpane.getRowHeader(); + if (rowHeader != null + && !rowHeader.getViewPosition().equals(viewPosition)) + rowHeader.setViewPosition(viewPosition); + } + + } + + /** + * Listens for changes of the viewport's extent size and updates the + * scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class ViewportChangeHandler implements ChangeListener + { + + /** + * Receives notification when the view's size, position or extent size + * changes. When the extents size has changed, this method calls + * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the + * scrollbars extents as well. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JViewport vp = scrollpane.getViewport(); + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + syncScrollPaneWithViewport(); + } + + } + + /** + * Listens for property changes on the scrollpane and update the view + * accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class PropertyChangeHandler implements PropertyChangeListener + { + + /** + * Receives notification when any of the scrollpane's bound property + * changes. This method calls the appropriate update method on the + * <code>ScrollBarUI</code>. + * + * @param e the property change event + * + * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("viewport")) + updateViewport(e); + else if (propName.equals("rowHeader")) + updateRowHeader(e); + else if (propName.equals("columnHeader")) + updateColumnHeader(e); + else if (propName.equals("horizontalScrollBarPolicy") + || e.getPropertyName().equals("verticalScrollBarPolicy")) + updateScrollBarDisplayPolicy(e); + else if (propName.equals("verticalScrollBar")) + { + JScrollBar oldSb = (JScrollBar) e.getOldValue(); + oldSb.getModel().removeChangeListener(vsbChangeListener); + JScrollBar newSb = (JScrollBar) e.getNewValue(); + newSb.getModel().addChangeListener(vsbChangeListener); + } + else if (propName.equals("horizontalScrollBar")) + { + JScrollBar oldSb = (JScrollBar) e.getOldValue(); + oldSb.getModel().removeChangeListener(hsbChangeListener); + JScrollBar newSb = (JScrollBar) e.getNewValue(); + newSb.getModel().addChangeListener(hsbChangeListener); + } + } + + } + + /** + * Listens for mouse wheel events and update the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.4 + */ + protected class MouseWheelHandler implements MouseWheelListener + { + + /** + * Receives notification whenever the mouse wheel is moved. + * + * @param event the mouse wheel event + */ + public void mouseWheelMoved(MouseWheelEvent event) + { + // TODO: Implement this properly. + } + + } + /** The Scrollpane for which the UI is provided by this class. */ protected JScrollPane scrollpane; + /** + * The horizontal scrollbar listener. + */ + protected ChangeListener hsbChangeListener; + + /** + * The vertical scrollbar listener. + */ + protected ChangeListener vsbChangeListener; + + /** + * The viewport listener. + */ + protected ChangeListener viewportChangeListener; + + /** + * The scrollpane property change listener. + */ + protected PropertyChangeListener spPropertyChangeListener; + + /** + * The mousewheel listener for the scrollpane. + */ + MouseWheelListener mouseWheelListener; + public static ComponentUI createUI(final JComponent c) { return new BasicScrollPaneUI(); @@ -65,11 +271,10 @@ public class BasicScrollPaneUI extends ScrollPaneUI protected void installDefaults(JScrollPane p) { scrollpane = p; - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - p.setForeground(defaults.getColor("ScrollPane.foreground")); - p.setBackground(defaults.getColor("ScrollPane.background")); - p.setFont(defaults.getFont("ScrollPane.font")); - p.setBorder(defaults.getBorder("ScrollPane.border")); + LookAndFeel.installColorsAndFont(p, "ScrollPane.background", + "ScrollPane.foreground", + "ScrollPane.font"); + LookAndFeel.installBorder(p, "ScrollPane.border"); p.setOpaque(true); } @@ -85,16 +290,141 @@ public class BasicScrollPaneUI extends ScrollPaneUI public void installUI(final JComponent c) { super.installUI(c); - this.installDefaults((JScrollPane)c); + installDefaults((JScrollPane) c); + installListeners((JScrollPane) c); + installKeyboardActions((JScrollPane) c); + } + + /** + * Installs the listeners on the scrollbars, the viewport and the scrollpane. + * + * @param sp the scrollpane on which to install the listeners + */ + protected void installListeners(JScrollPane sp) + { + if (spPropertyChangeListener == null) + spPropertyChangeListener = createPropertyChangeListener(); + sp.addPropertyChangeListener(spPropertyChangeListener); + + if (hsbChangeListener == null) + hsbChangeListener = createHSBChangeListener(); + sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener); + + if (vsbChangeListener == null) + vsbChangeListener = createVSBChangeListener(); + sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener); + + if (viewportChangeListener == null) + viewportChangeListener = createViewportChangeListener(); + sp.getViewport().addChangeListener(viewportChangeListener); + + if (mouseWheelListener == null) + mouseWheelListener = createMouseWheelListener(); + sp.addMouseWheelListener(mouseWheelListener); + } + + /** + * Installs additional keyboard actions on the scrollpane. This is a hook + * method provided to subclasses in order to install their own keyboard + * actions. + * + * @param sp the scrollpane to install keyboard actions on + */ + protected void installKeyboardActions(JScrollPane sp) + { + // TODO: Is this only a hook method or should we actually do something + // here? If the latter, than figure out what and implement this. + } + + /** + * Creates and returns the change listener for the horizontal scrollbar. + * + * @return the change listener for the horizontal scrollbar + */ + protected ChangeListener createHSBChangeListener() + { + return new HSBChangeListener(); + } + + /** + * Creates and returns the change listener for the vertical scrollbar. + * + * @return the change listener for the vertical scrollbar + */ + protected ChangeListener createVSBChangeListener() + { + return new VSBChangeListener(); + } + + /** + * Creates and returns the change listener for the viewport. + * + * @return the change listener for the viewport + */ + protected ChangeListener createViewportChangeListener() + { + return new ViewportChangeHandler(); + } + + /** + * Creates and returns the property change listener for the scrollpane. + * + * @return the property change listener for the scrollpane + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new PropertyChangeHandler(); + } + + /** + * Creates and returns the mouse wheel listener for the scrollpane. + * + * @return the mouse wheel listener for the scrollpane + */ + protected MouseWheelListener createMouseWheelListener() + { + return new MouseWheelHandler(); } public void uninstallUI(final JComponent c) { super.uninstallUI(c); this.uninstallDefaults((JScrollPane)c); + uninstallListeners((JScrollPane) c); + installKeyboardActions((JScrollPane) c); + } + + /** + * Uninstalls all the listeners that have been installed in + * {@link #installListeners(JScrollPane)}. + * + * @param c the scrollpane from which to uninstall the listeners + */ + protected void uninstallListeners(JComponent c) + { + JScrollPane sp = (JScrollPane) c; + sp.removePropertyChangeListener(spPropertyChangeListener); + sp.getHorizontalScrollBar().getModel() + .removeChangeListener(hsbChangeListener); + sp.getVerticalScrollBar().getModel() + .removeChangeListener(vsbChangeListener); + sp.getViewport().removeChangeListener(viewportChangeListener); + sp.removeMouseWheelListener(mouseWheelListener); + } + + /** + * Uninstalls all keyboard actions from the JScrollPane that have been + * installed by {@link #installKeyboardActions}. This is a hook method + * provided to subclasses to add their own keyboard actions. + * + * @param sp the scrollpane to uninstall keyboard actions from + */ + protected void uninstallKeyboardActions(JScrollPane sp) + { + // TODO: Is this only a hook method or should we actually do something + // here? If the latter, than figure out what and implement this. } - public Dimension getMinimumSize(JComponent c) { JScrollPane p = (JScrollPane ) c; @@ -107,6 +437,76 @@ public class BasicScrollPaneUI extends ScrollPaneUI // do nothing; the normal painting-of-children algorithm, along with // ScrollPaneLayout, does all the relevant work. } + + /** + * Synchronizes the scrollbars with the viewport's extents. + */ + protected void syncScrollPaneWithViewport() + { + JViewport vp = scrollpane.getViewport(); + + // Update the horizontal scrollbar. + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.setMaximum(vp.getViewSize().width); + hsb.setValue(vp.getViewPosition().x); + hsb.setVisibleAmount(vp.getExtentSize().width); + + // Update the vertical scrollbar. + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.setMaximum(vp.getViewSize().height); + vsb.setValue(vp.getViewPosition().y); + vsb.setVisibleAmount(vp.getExtentSize().height); + } + + /** + * Receives notification when the <code>columnHeader</code> property has + * changed on the scrollpane. + * + * @param ev the property change event + */ + protected void updateColumnHeader(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>rowHeader</code> property has changed + * on the scrollpane. + * + * @param ev the property change event + */ + protected void updateRowHeader(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>scrollBarDisplayPolicy</code> + * property has changed on the scrollpane. + * + * @param ev the property change event + */ + protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>viewport</code> property has changed + * on the scrollpane. + * + * This method sets removes the viewportChangeListener from the old viewport + * and adds it to the new viewport. + * + * @param ev the property change event + */ + protected void updateViewport(PropertyChangeEvent ev) + { + JViewport oldViewport = (JViewport) ev.getOldValue(); + oldViewport.removeChangeListener(viewportChangeListener); + JViewport newViewport = (JViewport) ev.getNewValue(); + oldViewport.addChangeListener(viewportChangeListener); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java index 38c9c7a2820..97caa3af7bd 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java @@ -41,13 +41,11 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Rectangle; import javax.swing.JComponent; import javax.swing.JSeparator; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SeparatorUI; @@ -121,10 +119,8 @@ public class BasicSeparatorUI extends SeparatorUI */ protected void installDefaults(JSeparator s) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - shadow = defaults.getColor("Separator.shadow"); - highlight = defaults.getColor("Separator.highlight"); + shadow = UIManager.getColor("Separator.shadow"); + highlight = UIManager.getColor("Separator.highlight"); s.setOpaque(false); } @@ -165,8 +161,8 @@ public class BasicSeparatorUI extends SeparatorUI /** * The separator is made of two lines. The top line will be - * the highlight color (or left line if it's vertical). The bottom - * or right line will be the shadow color. The two lines will + * the shadow color (or left line if it's vertical). The bottom + * or right line will be the highlight color. The two lines will * be centered inside the bounds box. If the separator is horizontal, * then it will be vertically centered, or if it's vertical, it will * be horizontally centered. @@ -180,9 +176,6 @@ public class BasicSeparatorUI extends SeparatorUI SwingUtilities.calculateInnerArea(c, r); Color saved = g.getColor(); - int midAB = r.width / 2 + r.x; - int midAD = r.height / 2 + r.y; - JSeparator s; if (c instanceof JSeparator) s = (JSeparator) c; @@ -190,21 +183,24 @@ public class BasicSeparatorUI extends SeparatorUI return; if (s.getOrientation() == JSeparator.HORIZONTAL) - { - g.setColor(highlight); - g.drawLine(r.x, midAD, r.x + r.width, midAD); - - g.setColor(shadow); - g.drawLine(r.x, midAD + 1, r.x + r.width, midAD + 1); - } - else - { - g.setColor(highlight); - g.drawLine(midAB, r.y, midAB, r.y + r.height); - - g.setColor(shadow); - g.drawLine(midAB + 1, r.y, midAB + 1, r.y + r.height); - } + { + int midAB = r.height / 2; + g.setColor(shadow); + g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1); + + g.setColor(highlight); + g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB); + } + else + { + int midAD = r.height / 2 + r.y; + g.setColor(shadow); + g.drawLine(r.x, r.y, r.x, r.y + r.height); + + g.setColor(highlight); + g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height); + } + g.setColor(saved); } /** @@ -217,28 +213,14 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getPreferredSize(JComponent c) { - Dimension dims = new Dimension(0, 0); - Insets insets = c.getInsets(); - + Dimension pref = new Dimension(2, 0); if (c instanceof JSeparator) { JSeparator s = (JSeparator) c; - if (s.getOrientation() == JSeparator.HORIZONTAL) - { - dims.height = 2; - dims.width = 40; - } - else - { - dims.width = 2; - dims.height = 40; - } + pref = new Dimension(0, 2); } - dims.width += insets.left + insets.right; - dims.height += insets.top + insets.bottom; - - return dims; + return pref; } /** @@ -251,7 +233,7 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(0, 0); } /** @@ -264,6 +246,7 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(Short.MAX_VALUE, + Short.MAX_VALUE); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java index 0b4058429c5..26f58051902 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java @@ -60,13 +60,14 @@ import java.beans.PropertyChangeListener; import java.util.Dictionary; import java.util.Enumeration; +import javax.swing.AbstractAction; import javax.swing.BoundedRangeModel; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JSlider; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.Timer; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -470,6 +471,34 @@ public class BasicSliderUI extends SliderUI } } + /** + * This class is no longer used as of JDK1.3. + */ + public class ActionScroller extends AbstractAction + { + /** + * Not used. + * + * @param slider not used + * @param dir not used + * @param block not used + */ + public ActionScroller(JSlider slider, int dir, boolean block) + { + // Not used. + } + + /** + * Not used. + * + * @param event not used + */ + public void actionPerformed(ActionEvent event) + { + // Not used. + } + } + /** Listener for changes from the model. */ protected ChangeListener changeListener; @@ -680,16 +709,14 @@ public class BasicSliderUI extends SliderUI */ protected void installDefaults(JSlider slider) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - slider.setForeground(defaults.getColor("Slider.foreground")); - slider.setBackground(defaults.getColor("Slider.background")); - shadowColor = defaults.getColor("Slider.shadow"); - highlightColor = defaults.getColor("Slider.highlight"); - focusColor = defaults.getColor("Slider.focus"); - slider.setBorder(defaults.getBorder("Slider.border")); + LookAndFeel.installColors(slider, "Slider.background", + "Slider.foreground"); + LookAndFeel.installBorder(slider, "Slider.border"); + shadowColor = UIManager.getColor("Slider.shadow"); + highlightColor = UIManager.getColor("Slider.highlight"); + focusColor = UIManager.getColor("Slider.focus"); + focusInsets = UIManager.getInsets("Slider.focusInsets"); slider.setOpaque(true); - focusInsets = defaults.getInsets("Slider.focusInsets"); } /** @@ -1465,7 +1492,7 @@ public class BasicSliderUI extends SliderUI // FIXME: Move this to propertyChangeEvent handler, when we get those. leftToRightCache = slider.getComponentOrientation() != ComponentOrientation.RIGHT_TO_LEFT; // FIXME: This next line is only here because the above line is here. - calculateThumbLocation(); + calculateGeometry(); if (slider.getPaintTrack()) paintTrack(g); @@ -1958,7 +1985,7 @@ public class BasicSliderUI extends SliderUI public void paintThumb(Graphics g) { Color saved_color = g.getColor(); - + Point a = new Point(thumbRect.x, thumbRect.y); Point b = new Point(a); Point c = new Point(a); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java index 97ab97b8972..3b7399eafaa 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -53,9 +53,8 @@ import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JSpinner; +import javax.swing.LookAndFeel; import javax.swing.Timer; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SpinnerUI; @@ -167,16 +166,9 @@ public class BasicSpinnerUI extends SpinnerUI */ protected void installDefaults() { - /* most of it copied from BasicLabelUI, I don't know what keys are - available, so someone may want to update this. Hence: TODO - */ - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - /* - spinner.setForeground(defaults.getColor("Spinner.foreground")); - spinner.setBackground(defaults.getColor("Spinner.background")); - spinner.setFont(defaults.getFont("Spinner.font")); - spinner.setBorder(defaults.getBorder("Spinner.border")); - */ + LookAndFeel.installColorsAndFont(spinner, "Spinner.background", + "Spinner.foreground", "Spinner.font"); + LookAndFeel.installBorder(spinner, "Spinner.border"); spinner.setLayout(createLayout()); spinner.setOpaque(true); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java index b8674ed2f08..69ed2be7c61 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -802,6 +802,7 @@ public class BasicSplitPaneDivider extends Container */ protected DividerLayout() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java index ef8e2282349..746f628df6f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -58,7 +58,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JSplitPane; import javax.swing.KeyStroke; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SplitPaneUI; @@ -404,7 +404,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected void setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize) - { + { int w = size; int h = containerSize.height - insets.top - insets.bottom; int x = location; @@ -637,7 +637,6 @@ public class BasicSplitPaneUI extends SplitPaneUI int x = insets.left; int h = size; int w = containerSize.width - insets.left - insets.right; - c.setBounds(x, y, w, h); } @@ -817,15 +816,12 @@ public class BasicSplitPaneUI extends SplitPaneUI int newSize = splitPane.getDividerSize(); int[] tmpSizes = layoutManager.getSizes(); dividerSize = tmpSizes[2]; - Component left = splitPane.getLeftComponent(); - Component right = splitPane.getRightComponent(); - int newSpace = newSize - tmpSizes[2]; - + int newSpace = newSize - tmpSizes[2]; tmpSizes[2] = newSize; tmpSizes[0] += newSpace / 2; tmpSizes[1] += newSpace / 2; - + layoutManager.setSizes(tmpSizes); } else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) @@ -942,6 +938,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public BasicSplitPaneUI() { + // Nothing to do here. } /** @@ -991,16 +988,16 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected void installDefaults() { + LookAndFeel.installColors(splitPane, "SplitPane.background", + "SplitPane.foreground"); + LookAndFeel.installBorder(splitPane, "SplitPane.border"); divider = createDefaultDivider(); resetLayoutManager(); nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); splitPane.add(divider, JSplitPane.DIVIDER); // There is no need to add the nonContinuousLayoutDivider - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - splitPane.setBackground(defaults.getColor("SplitPane.background")); - splitPane.setBorder(defaults.getBorder("SplitPane.border")); - splitPane.setDividerSize(defaults.getInt("SplitPane.dividerSize")); + splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); splitPane.setOpaque(true); } @@ -1301,15 +1298,41 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void setDividerLocation(JSplitPane jc, int location) { + location = validLocation(location); + Container p = jc.getParent(); + Dimension rightPrefSize = jc.getRightComponent().getPreferredSize(); + Dimension size = jc.getSize(); + // check if the size has been set for the splitpane + if (size.width == 0 && size.height == 0) + size = jc.getPreferredSize(); + + if (getOrientation() == 0 && location > size.height) + { + location = size.height; + while (p != null) + { + p.setSize(p.getWidth(), p.getHeight() + rightPrefSize.height); + p = p.getParent(); + } + } + else if (location > size.width) + { + location = size.width; + while (p != null) + { + p.setSize(p.getWidth() + rightPrefSize.width, p.getHeight()); + p = p.getParent(); + } + } + setLastDragLocation(getDividerLocation(splitPane)); splitPane.setLastDividerLocation(getDividerLocation(splitPane)); int[] tmpSizes = layoutManager.getSizes(); - tmpSizes[0] = location + tmpSizes[0] = location - layoutManager.getInitialLocation(splitPane.getInsets()); tmpSizes[1] = layoutManager.getAvailableSize(splitPane.getSize(), splitPane.getInsets()) - - tmpSizes[0] - tmpSizes[1]; - + - tmpSizes[0]; layoutManager.setSizes(tmpSizes); splitPane.revalidate(); splitPane.repaint(); @@ -1338,11 +1361,9 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public int getMinimumDividerLocation(JSplitPane jc) { - int value = layoutManager.getInitialLocation(jc.getInsets()) - - layoutManager.getAvailableSize(jc.getSize(), jc.getInsets()) - + splitPane.getDividerSize(); - if (layoutManager.components[1] != null) - value += layoutManager.minimumSizeOfComponent(1); + int value = layoutManager.getInitialLocation(jc.getInsets()); + if (layoutManager.components[0] != null) + value -= layoutManager.minimumSizeOfComponent(0); return value; } @@ -1388,6 +1409,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void paint(Graphics g, JComponent jc) { + // TODO: What should be done here? } /** @@ -1550,10 +1572,12 @@ public class BasicSplitPaneUI extends SplitPaneUI */ private int validLocation(int location) { - if (location < getMinimumDividerLocation(splitPane)) - return getMinimumDividerLocation(splitPane); - if (location > getMaximumDividerLocation(splitPane)) - return getMaximumDividerLocation(splitPane); + int min = getMinimumDividerLocation(splitPane); + int max = getMaximumDividerLocation(splitPane); + if (min > 0 && location < min) + return min; + if (max > 0 && location > max) + return max; return location; } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 7e9d9b9820c..ce9ea3ec7f1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -64,9 +64,9 @@ import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JViewport; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -136,36 +136,36 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { - if (e.getSource() == incrButton) - { - if (++currentScrollLocation >= tabCount) - currentScrollLocation = tabCount - 1; - - int width = 0; - for (int i = currentScrollLocation - 1; i < tabCount; i++) - width += rects[i].width; - if (width < viewport.getWidth()) - // FIXME: Still getting mouse events after the button is disabled. - // incrButton.setEnabled(false); - currentScrollLocation--; - else if (! decrButton.isEnabled()) - decrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } - else if (e.getSource() == decrButton) - { - if (--currentScrollLocation < 0) - currentScrollLocation = 0; - if (currentScrollLocation == 0) - decrButton.setEnabled(false); - else if (! incrButton.isEnabled()) - incrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } + if (e.getSource() == incrButton) + { + if (++currentScrollLocation >= tabCount) + currentScrollLocation = tabCount - 1; + + int width = 0; + for (int i = currentScrollLocation - 1; i < tabCount; i++) + width += rects[i].width; + if (width < viewport.getWidth()) + // FIXME: Still getting mouse events after the button is disabled. + // incrButton.setEnabled(false); + currentScrollLocation--; + else if (! decrButton.isEnabled()) + decrButton.setEnabled(true); + tabPane.revalidate(); + tabPane.repaint(); + return; + } + else if (e.getSource() == decrButton) + { + if (--currentScrollLocation < 0) + currentScrollLocation = 0; + if (currentScrollLocation == 0) + decrButton.setEnabled(false); + else if (! incrButton.isEnabled()) + incrButton.setEnabled(true); + tabPane.revalidate(); + tabPane.repaint(); + return; + } } int index = tabForCoordinate(tabPane, x, y); @@ -173,7 +173,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // We need to check since there are areas where tabs cannot be // e.g. in the inset area. if (index != -1 && tabPane.isEnabledAt(index)) - tabPane.setSelectedIndex(index); + tabPane.setSelectedIndex(index); tabPane.revalidate(); tabPane.repaint(); } @@ -198,15 +198,15 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (e.getPropertyName().equals("tabLayoutPolicy")) { - layoutManager = createLayoutManager(); - - tabPane.setLayout(layoutManager); + layoutManager = createLayoutManager(); + + tabPane.setLayout(layoutManager); } else if (e.getPropertyName().equals("tabPlacement") - && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { - incrButton = createIncreaseButton(); - decrButton = createDecreaseButton(); + incrButton = createIncreaseButton(); + decrButton = createDecreaseButton(); } tabPane.layout(); tabPane.repaint(); @@ -245,13 +245,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getSelectedIndex() != -1) { - Component visible = getVisibleComponent(); - Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); - if (visible != null) - visible.setBounds(contentRect.x + insets.left, - contentRect.y + insets.top, - contentRect.width - insets.left - insets.right, - contentRect.height - insets.top - insets.bottom); + Component visible = getVisibleComponent(); + Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); + if (visible != null) + visible.setBounds(contentRect.x + insets.left, + contentRect.y + insets.top, + contentRect.width - insets.left - insets.right, + contentRect.height - insets.top - insets.bottom); } } @@ -275,35 +275,35 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Dimension dims; for (int i = 0; i < tabPane.getTabCount(); i++) { - c = tabPane.getComponentAt(i); - if (c == null) - continue; - calcRect = c.getBounds(); - dims = c.getPreferredSize(); - if (dims != null) - { - componentHeight = Math.max(componentHeight, dims.height); - componentWidth = Math.max(componentWidth, dims.width); - } + c = tabPane.getComponentAt(i); + if (c == null) + continue; + calcRect = c.getBounds(); + dims = c.getPreferredSize(); + if (dims != null) + { + componentHeight = Math.max(componentHeight, dims.height); + componentWidth = Math.max(componentWidth, dims.width); + } } Insets insets = tabPane.getInsets(); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, componentWidth); - - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); - height = tabAreaHeight + componentHeight; + int min = calculateMaxTabWidth(tabPlacement); + width = Math.max(min, componentWidth); + + int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); + height = tabAreaHeight + componentHeight; } else { - int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, componentHeight); - - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); - width = tabAreaWidth + componentWidth; + int min = calculateMaxTabHeight(tabPlacement); + height = Math.max(min, componentHeight); + + int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); + width = tabAreaWidth + componentWidth; } return new Dimension(width, height); @@ -330,7 +330,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void calculateTabRects(int tabPlacement, int tabCount) { if (tabCount == 0) - return; + return; assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); @@ -343,113 +343,112 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start += tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - - for (int i = 0; i < tabCount; i++) - { - width = calculateTabWidth(tabPlacement, i, fm); - - if (runWidth + width > max) - { - runWidth = tabAreaInsets.left + insets.left - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; - } - else - { - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - } - } - runs++; - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; - } + int maxHeight = calculateMaxTabHeight(tabPlacement); + + calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; + max = calcRect.width + tabAreaInsets.left + insets.left; + start += tabAreaInsets.left + insets.left; + int width = 0; + int runWidth = start; + + for (int i = 0; i < tabCount; i++) + { + width = calculateTabWidth(tabPlacement, i, fm); + if (runWidth + width > max) + { + runWidth = tabAreaInsets.left + insets.left + + getTabRunIndent(tabPlacement, ++runs); + rects[i] = new Rectangle(runWidth, + insets.top + tabAreaInsets.top, + width, maxHeight); + runWidth += width; + if (runs > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runs] = i; + } + else + { + rects[i] = new Rectangle(runWidth, + insets.top + tabAreaInsets.top, + width, maxHeight); + runWidth += width; + } + } + runs++; + tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; + tabAreaRect.height = runs * maxTabHeight + - (runs - 1) * tabRunOverlay + + tabAreaInsets.top + tabAreaInsets.bottom; + contentRect.width = tabAreaRect.width; + contentRect.height = tabPane.getHeight() - insets.top + - insets.bottom - tabAreaRect.height; + contentRect.x = insets.left; + tabAreaRect.x = insets.left; + if (tabPlacement == SwingConstants.BOTTOM) + { + contentRect.y = insets.top; + tabAreaRect.y = contentRect.y + contentRect.height; + } + else + { + tabAreaRect.y = insets.top; + contentRect.y = tabAreaRect.y + tabAreaRect.height; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top + insets.top; - - int height = 0; - start += tabAreaInsets.top + insets.top; - int runHeight = start; - - int fontHeight = fm.getHeight(); - - for (int i = 0; i < tabCount; i++) - { - height = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + height > max) - { - runHeight = tabAreaInsets.top + insets.top - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; - } - else - { - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - } - } - runs++; - - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; - } + int maxWidth = calculateMaxTabWidth(tabPlacement); + calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; + max = calcRect.height + tabAreaInsets.top + insets.top; + + int height = 0; + start += tabAreaInsets.top + insets.top; + int runHeight = start; + + int fontHeight = fm.getHeight(); + + for (int i = 0; i < tabCount; i++) + { + height = calculateTabHeight(tabPlacement, i, fontHeight); + if (runHeight + height > max) + { + runHeight = tabAreaInsets.top + insets.top + + getTabRunIndent(tabPlacement, ++runs); + rects[i] = new Rectangle(insets.left + tabAreaInsets.left, + runHeight, maxWidth, height); + runHeight += height; + if (runs > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runs] = i; + } + else + { + rects[i] = new Rectangle(insets.left + tabAreaInsets.left, + runHeight, maxWidth, height); + runHeight += height; + } + } + runs++; + + tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay + + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.height = tabPane.getHeight() - insets.top + - insets.bottom; + tabAreaRect.y = insets.top; + contentRect.width = tabPane.getWidth() - insets.left - insets.right + - tabAreaRect.width; + contentRect.height = tabAreaRect.height; + contentRect.y = insets.top; + if (tabPlacement == SwingConstants.LEFT) + { + tabAreaRect.x = insets.left; + contentRect.x = tabAreaRect.x + tabAreaRect.width; + } + else + { + contentRect.x = insets.left; + tabAreaRect.x = contentRect.x + contentRect.width; + } } runCount = runs; @@ -457,62 +456,62 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants normalizeTabRuns(tabPlacement, tabCount, start, max); selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex()); if (shouldRotateTabRuns(tabPlacement)) - rotateTabRuns(tabPlacement, selectedRun); + rotateTabRuns(tabPlacement, selectedRun); // Need to pad the runs and move them to the correct location. for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (shouldPadTabRun(tabPlacement, i)) - padTabRun(tabPlacement, first, last, max); - - // Done padding, now need to move it. - if (tabPlacement == SwingConstants.TOP && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].y += (runCount - i) * maxTabHeight - - (runCount - i) * tabRunOverlay; - } - - if (tabPlacement == SwingConstants.BOTTOM) - { - int height = tabPane.getBounds().height - insets.bottom - - tabAreaInsets.bottom; - int adjustment; - if (i == 0) - adjustment = height - maxTabHeight; - else - adjustment = height - (runCount - i + 1) * maxTabHeight - - (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].y = adjustment; - } - - if (tabPlacement == SwingConstants.LEFT && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].x += (runCount - i) * maxTabWidth - - (runCount - i) * tabRunOverlay; - } - - if (tabPlacement == SwingConstants.RIGHT) - { - int width = tabPane.getBounds().width - insets.right - - tabAreaInsets.right; - int adjustment; - if (i == 0) - adjustment = width - maxTabWidth; - else - adjustment = width - (runCount - i + 1) * maxTabWidth - + (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].x = adjustment; - } + int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, i); + if (shouldPadTabRun(tabPlacement, i)) + padTabRun(tabPlacement, first, last, max); + + // Done padding, now need to move it. + if (tabPlacement == SwingConstants.TOP && i > 0) + { + for (int j = first; j <= last; j++) + rects[j].y += (runCount - i) * maxTabHeight + - (runCount - i) * tabRunOverlay; + } + + if (tabPlacement == SwingConstants.BOTTOM) + { + int height = tabPane.getBounds().height - insets.bottom + - tabAreaInsets.bottom; + int adjustment; + if (i == 0) + adjustment = height - maxTabHeight; + else + adjustment = height - (runCount - i + 1) * maxTabHeight + - (runCount - i) * tabRunOverlay; + + for (int j = first; j <= last; j++) + rects[j].y = adjustment; + } + + if (tabPlacement == SwingConstants.LEFT && i > 0) + { + for (int j = first; j <= last; j++) + rects[j].x += (runCount - i) * maxTabWidth + - (runCount - i) * tabRunOverlay; + } + + if (tabPlacement == SwingConstants.RIGHT) + { + int width = tabPane.getBounds().width - insets.right + - tabAreaInsets.right; + int adjustment; + if (i == 0) + adjustment = width - maxTabWidth; + else + adjustment = width - (runCount - i + 1) * maxTabWidth + + (runCount - i) * tabRunOverlay; + + for (int j = first; j <= last; j++) + rects[j].x = adjustment; + } } padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); } @@ -565,74 +564,74 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingUtilities.TOP || tabPlacement == SwingUtilities.BOTTOM) { - // We should only do this for runCount - 1, cause we can only shift that many times between - // runs. - for (int i = 1; i < runCount; i++) - { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; - int spaceInCurr = currRun.x + currRun.width; - int spaceInNext = nextRun.x + nextRun.width; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.width < max) - { - tabRuns[i]--; - spaceInNext += currRun.width; - spaceInCurr -= currRun.width; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - } - - // Fix the bounds. - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currX = tabAreaInsets.left; - for (int j = first; j <= last; j++) - { - rects[j].x = currX; - currX += rects[j].width; - } - } + // We should only do this for runCount - 1, cause we can only shift that many times between + // runs. + for (int i = 1; i < runCount; i++) + { + Rectangle currRun = rects[lastTabInRun(tabCount, i)]; + Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + int spaceInCurr = currRun.x + currRun.width; + int spaceInNext = nextRun.x + nextRun.width; + + int diffNow = spaceInCurr - spaceInNext; + int diffLater = (spaceInCurr - currRun.width) + - (spaceInNext + currRun.width); + while (Math.abs(diffLater) < Math.abs(diffNow) + && spaceInNext + currRun.width < max) + { + tabRuns[i]--; + spaceInNext += currRun.width; + spaceInCurr -= currRun.width; + currRun = rects[lastTabInRun(tabCount, i)]; + diffNow = spaceInCurr - spaceInNext; + diffLater = (spaceInCurr - currRun.width) + - (spaceInNext + currRun.width); + } + + // Fix the bounds. + int first = lastTabInRun(tabCount, i) + 1; + int last = lastTabInRun(tabCount, getNextTabRun(i)); + int currX = tabAreaInsets.left; + for (int j = first; j <= last; j++) + { + rects[j].x = currX; + currX += rects[j].width; + } + } } else { - for (int i = 1; i < runCount; i++) - { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; - int spaceInCurr = currRun.y + currRun.height; - int spaceInNext = nextRun.y + nextRun.height; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.height < max) - { - tabRuns[i]--; - spaceInNext += currRun.height; - spaceInCurr -= currRun.height; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - } - - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currY = tabAreaInsets.top; - for (int j = first; j <= last; j++) - { - rects[j].y = currY; - currY += rects[j].height; - } - } + for (int i = 1; i < runCount; i++) + { + Rectangle currRun = rects[lastTabInRun(tabCount, i)]; + Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + int spaceInCurr = currRun.y + currRun.height; + int spaceInNext = nextRun.y + nextRun.height; + + int diffNow = spaceInCurr - spaceInNext; + int diffLater = (spaceInCurr - currRun.height) + - (spaceInNext + currRun.height); + while (Math.abs(diffLater) < Math.abs(diffNow) + && spaceInNext + currRun.height < max) + { + tabRuns[i]--; + spaceInNext += currRun.height; + spaceInCurr -= currRun.height; + currRun = rects[lastTabInRun(tabCount, i)]; + diffNow = spaceInCurr - spaceInNext; + diffLater = (spaceInCurr - currRun.height) + - (spaceInNext + currRun.height); + } + + int first = lastTabInRun(tabCount, i) + 1; + int last = lastTabInRun(tabCount, getNextTabRun(i)); + int currY = tabAreaInsets.top; + for (int j = first; j <= last; j++) + { + rects[j].y = currY; + currY += rects[j].height; + } + } } } @@ -673,42 +672,42 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int runWidth = rects[end].x + rects[end].width; - int spaceRemaining = max - runWidth; - int numTabs = end - start + 1; - - // now divvy up the space. - int spaceAllocated = spaceRemaining / numTabs; - int currX = rects[start].x; - for (int i = start; i <= end; i++) - { - rects[i].x = currX; - rects[i].width += spaceAllocated; - currX += rects[i].width; - // This is used because since the spaceAllocated - // variable is an int, it rounds down. Sometimes, - // we don't fill an entire row, so we make it do - // so now. - if (i == end && rects[i].x + rects[i].width != max) - rects[i].width = max - rects[i].x; - } + int runWidth = rects[end].x + rects[end].width; + int spaceRemaining = max - runWidth; + int numTabs = end - start + 1; + + // now divvy up the space. + int spaceAllocated = spaceRemaining / numTabs; + int currX = rects[start].x; + for (int i = start; i <= end; i++) + { + rects[i].x = currX; + rects[i].width += spaceAllocated; + currX += rects[i].width; + // This is used because since the spaceAllocated + // variable is an int, it rounds down. Sometimes, + // we don't fill an entire row, so we make it do + // so now. + if (i == end && rects[i].x + rects[i].width != max) + rects[i].width = max - rects[i].x; + } } else { - int runHeight = rects[end].y + rects[end].height; - int spaceRemaining = max - runHeight; - int numTabs = end - start + 1; - - int spaceAllocated = spaceRemaining / numTabs; - int currY = rects[start].y; - for (int i = start; i <= end; i++) - { - rects[i].y = currY; - rects[i].height += spaceAllocated; - currY += rects[i].height; - if (i == end && rects[i].y + rects[i].height != max) - rects[i].height = max - rects[i].y; - } + int runHeight = rects[end].y + rects[end].height; + int spaceRemaining = max - runHeight; + int numTabs = end - start + 1; + + int spaceAllocated = spaceRemaining / numTabs; + int currY = rects[start].y; + for (int i = start; i <= end; i++) + { + rects[i].y = currY; + rects[i].height += spaceAllocated; + currY += rects[i].height; + if (i == end && rects[i].y + rects[i].height != max) + rects[i].height = max - rects[i].y; + } } } @@ -736,7 +735,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaHeight(int tabPlacement, int width) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 0; int runWidth = 0; @@ -758,14 +757,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // be IF we got our desired width. for (int i = 0; i < tabPane.getTabCount(); i++) { - tabWidth = calculateTabWidth(tabPlacement, i, fm); - if (runWidth + tabWidth > width) - { - runWidth = tabWidth; - runs++; - } - else - runWidth += tabWidth; + tabWidth = calculateTabWidth(tabPlacement, i, fm); + if (runWidth + tabWidth > width) + { + runWidth = tabWidth; + runs++; + } + else + runWidth += tabWidth; } runs++; @@ -787,7 +786,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaWidth(int tabPlacement, int height) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 0; int runHeight = 0; @@ -804,14 +803,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants for (int i = 0; i < tabPane.getTabCount(); i++) { - tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + tabHeight > height) - { - runHeight = tabHeight; - runs++; - } - else - runHeight += tabHeight; + tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); + if (runHeight + tabHeight > height) + { + runHeight = tabHeight; + runs++; + } + else + runHeight += tabHeight; } runs++; @@ -831,19 +830,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void rotateTabRuns(int tabPlacement, int selectedRun) { if (runCount == 1 || selectedRun == 1 || selectedRun == -1) - return; + return; int[] newTabRuns = new int[tabRuns.length]; int currentRun = selectedRun; int i = 1; do { - newTabRuns[i] = tabRuns[currentRun]; - currentRun = getNextTabRun(currentRun); - i++; + newTabRuns[i] = tabRuns[currentRun]; + currentRun = getNextTabRun(currentRun); + i++; } while (i < runCount); if (runCount > 1) - newTabRuns[0] = tabRuns[currentRun]; + newTabRuns[0] = tabRuns[currentRun]; tabRuns = newTabRuns; BasicTabbedPaneUI.this.selectedRun = 1; @@ -902,7 +901,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaHeight(int tabPlacement, int width) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 1; @@ -923,7 +922,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaWidth(int tabPlacement, int height) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 1; @@ -944,7 +943,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void calculateTabRects(int tabPlacement, int tabCount) { if (tabCount == 0) - return; + return; assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); @@ -958,76 +957,76 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start = tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - top = insets.top + tabAreaInsets.top; - for (int i = 0; i < tabCount; i++) - { - width = calculateTabWidth(tabPlacement, i, fm); - - rects[i] = new Rectangle(runWidth, top, width, maxHeight); - runWidth += width; - } - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; - } + int maxHeight = calculateMaxTabHeight(tabPlacement); + calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; + max = calcRect.width + tabAreaInsets.left + insets.left; + start = tabAreaInsets.left + insets.left; + int width = 0; + int runWidth = start; + top = insets.top + tabAreaInsets.top; + for (int i = 0; i < tabCount; i++) + { + width = calculateTabWidth(tabPlacement, i, fm); + + rects[i] = new Rectangle(runWidth, top, width, maxHeight); + runWidth += width; + } + tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; + tabAreaRect.height = runs * maxTabHeight + - (runs - 1) * tabRunOverlay + + tabAreaInsets.top + tabAreaInsets.bottom; + contentRect.width = tabAreaRect.width; + contentRect.height = tabPane.getHeight() - insets.top + - insets.bottom - tabAreaRect.height; + contentRect.x = insets.left; + tabAreaRect.x = insets.left; + if (tabPlacement == SwingConstants.BOTTOM) + { + contentRect.y = insets.top; + tabAreaRect.y = contentRect.y + contentRect.height; + } + else + { + tabAreaRect.y = insets.top; + contentRect.y = tabAreaRect.y + tabAreaRect.height; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top; - int height = 0; - start = tabAreaInsets.top + insets.top; - int runHeight = start; - int fontHeight = fm.getHeight(); - top = insets.left + tabAreaInsets.left; - for (int i = 0; i < tabCount; i++) - { - height = calculateTabHeight(tabPlacement, i, fontHeight); - rects[i] = new Rectangle(top, runHeight, maxWidth, height); - runHeight += height; - } - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; - } + int maxWidth = calculateMaxTabWidth(tabPlacement); + + calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; + max = calcRect.height + tabAreaInsets.top; + int height = 0; + start = tabAreaInsets.top + insets.top; + int runHeight = start; + int fontHeight = fm.getHeight(); + top = insets.left + tabAreaInsets.left; + for (int i = 0; i < tabCount; i++) + { + height = calculateTabHeight(tabPlacement, i, fontHeight); + rects[i] = new Rectangle(top, runHeight, maxWidth, height); + runHeight += height; + } + tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay + + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.height = tabPane.getHeight() - insets.top + - insets.bottom; + tabAreaRect.y = insets.top; + contentRect.width = tabPane.getWidth() - insets.left - insets.right + - tabAreaRect.width; + contentRect.height = tabAreaRect.height; + contentRect.y = insets.top; + if (tabPlacement == SwingConstants.LEFT) + { + tabAreaRect.x = insets.left; + contentRect.x = tabAreaRect.x + tabAreaRect.width; + } + else + { + contentRect.x = insets.left; + tabAreaRect.x = contentRect.x + contentRect.width; + } } runCount = runs; @@ -1047,68 +1046,68 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int tabCount = tabPane.getTabCount(); Point p = null; if (tabCount == 0) - return; + return; int tabPlacement = tabPane.getTabPlacement(); incrButton.hide(); decrButton.hide(); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x - + rects[tabCount - 1].width) - { - Dimension incrDims = incrButton.getPreferredSize(); - Dimension decrDims = decrButton.getPreferredSize(); - - decrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width - decrDims.width, - tabAreaRect.y, decrDims.width, - tabAreaRect.height); - incrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width, tabAreaRect.y, - decrDims.width, tabAreaRect.height); - - tabAreaRect.width -= decrDims.width + incrDims.width; - incrButton.show(); - decrButton.show(); - } + if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x + + rects[tabCount - 1].width) + { + Dimension incrDims = incrButton.getPreferredSize(); + Dimension decrDims = decrButton.getPreferredSize(); + + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y, decrDims.width, + tabAreaRect.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, tabAreaRect.y, + decrDims.width, tabAreaRect.height); + + tabAreaRect.width -= decrDims.width + incrDims.width; + incrButton.show(); + decrButton.show(); + } } if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { - if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y - + rects[tabCount - 1].height) - { - Dimension incrDims = incrButton.getPreferredSize(); - Dimension decrDims = decrButton.getPreferredSize(); - - decrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height - decrDims.height, - tabAreaRect.width, decrDims.height); - incrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height, tabAreaRect.width, - incrDims.height); - - tabAreaRect.height -= decrDims.height + incrDims.height; - incrButton.show(); - decrButton.show(); - } + if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y + + rects[tabCount - 1].height) + { + Dimension incrDims = incrButton.getPreferredSize(); + Dimension decrDims = decrButton.getPreferredSize(); + + decrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + tabAreaRect.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, tabAreaRect.width, + incrDims.height); + + tabAreaRect.height -= decrDims.height + incrDims.height; + incrButton.show(); + decrButton.show(); + } } viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, tabAreaRect.height); int tabC = tabPane.getTabCount() - 1; if (tabCount > 0) { - int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); - int h = Math.max(rects[tabC].height, tabAreaRect.height); - p = findPointForIndex(currentScrollLocation); - - // we want to cover that entire space so that borders that run under - // the tab area don't show up when we move the viewport around. - panel.setSize(w + p.x, h + p.y); + int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); + int h = Math.max(rects[tabC].height, tabAreaRect.height); + p = findPointForIndex(currentScrollLocation); + + // we want to cover that entire space so that borders that run under + // the tab area don't show up when we move the viewport around. + panel.setSize(w + p.x, h + p.y); } viewport.setViewPosition(p); viewport.repaint(); @@ -1160,7 +1159,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void paint(Graphics g, JComponent c) { - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); } } @@ -1182,6 +1181,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ private class ScrollingViewport extends JViewport implements UIResource { + // TODO: Maybe remove this inner class. } /** @@ -1407,22 +1407,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == TOP || tabPlacement == BOTTOM) { - if (index > 0) - { - w += rects[index - 1].x + rects[index - 1].width; - if (index > selectedIndex) - w -= insets.left + insets.right; - } + if (index > 0) + { + w += rects[index - 1].x + rects[index - 1].width; + if (index > selectedIndex) + w -= insets.left + insets.right; + } } else { - if (index > 0) - { - h += rects[index - 1].y + rects[index - 1].height; - if (index > selectedIndex) - h -= insets.top + insets.bottom; - } + if (index > 0) + { + h += rects[index - 1].y + rects[index - 1].height; + if (index > selectedIndex) + h -= insets.top + insets.bottom; + } } Point p = new Point(w, h); @@ -1451,16 +1451,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants super.installUI(c); if (c instanceof JTabbedPane) { - tabPane = (JTabbedPane) c; - - installComponents(); - installDefaults(); - installListeners(); - installKeyboardActions(); - - layoutManager = createLayoutManager(); - tabPane.setLayout(layoutManager); - tabPane.layout(); + tabPane = (JTabbedPane) c; + + installComponents(); + installDefaults(); + installListeners(); + installKeyboardActions(); + + layoutManager = createLayoutManager(); + tabPane.setLayout(layoutManager); + tabPane.layout(); } } @@ -1495,23 +1495,23 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return new TabbedPaneLayout(); else { - incrButton = createIncreaseButton(); - decrButton = createDecreaseButton(); - viewport = new ScrollingViewport(); - viewport.setLayout(null); - panel = new ScrollingPanel(); - viewport.setView(panel); - tabPane.add(incrButton); - tabPane.add(decrButton); - tabPane.add(viewport); - currentScrollLocation = 0; - decrButton.setEnabled(false); - panel.addMouseListener(mouseListener); - incrButton.addMouseListener(mouseListener); - decrButton.addMouseListener(mouseListener); - viewport.setBackground(Color.LIGHT_GRAY); - - return new TabbedPaneScrollLayout(); + incrButton = createIncreaseButton(); + decrButton = createDecreaseButton(); + viewport = new ScrollingViewport(); + viewport.setLayout(null); + panel = new ScrollingPanel(); + viewport.setView(panel); + tabPane.add(incrButton); + tabPane.add(decrButton); + tabPane.add(viewport); + currentScrollLocation = 0; + decrButton.setEnabled(false); + panel.addMouseListener(mouseListener); + incrButton.addMouseListener(mouseListener); + decrButton.addMouseListener(mouseListener); + viewport.setBackground(Color.LIGHT_GRAY); + + return new TabbedPaneScrollLayout(); } } @@ -1536,28 +1536,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - tabPane.setFont(defaults.getFont("TabbedPane.font")); - tabPane.setForeground(defaults.getColor("TabbedPane.foreground")); - tabPane.setBackground(defaults.getColor("TabbedPane.background")); + LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", + "TabbedPane.foreground", + "TabbedPane.font"); tabPane.setOpaque(false); - highlight = defaults.getColor("TabbedPane.highlight"); - lightHighlight = defaults.getColor("TabbedPane.lightHighlight"); + highlight = UIManager.getColor("TabbedPane.highlight"); + lightHighlight = UIManager.getColor("TabbedPane.lightHighlight"); - shadow = defaults.getColor("TabbedPane.shadow"); - darkShadow = defaults.getColor("TabbedPane.darkShadow"); + shadow = UIManager.getColor("TabbedPane.shadow"); + darkShadow = UIManager.getColor("TabbedPane.darkShadow"); - focus = defaults.getColor("TabbedPane.focus"); + focus = UIManager.getColor("TabbedPane.focus"); - textIconGap = defaults.getInt("TabbedPane.textIconGap"); - tabRunOverlay = defaults.getInt("TabbedPane.tabRunOverlay"); + textIconGap = UIManager.getInt("TabbedPane.textIconGap"); + tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); - tabInsets = defaults.getInsets("TabbedPane.tabbedPaneTabInsets"); - selectedTabPadInsets = defaults.getInsets("TabbedPane.tabbedPaneTabPadInsets"); - tabAreaInsets = defaults.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); - contentBorderInsets = defaults.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); + tabInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabInsets"); + selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); + tabAreaInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); + contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); calcRect = new Rectangle(); tabRuns = new int[10]; @@ -1737,38 +1735,42 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // we WANT to paint the outermost run first and then work our way in. int tabCount = tabPane.getTabCount(); int currRun = 1; + + if (tabCount > runCount) + runCount = tabCount; + if (tabCount < 1) return; - + if (runCount > 1) - currRun = 0; + currRun = 0; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (isScroll) - first = currentScrollLocation; - else if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - if (isScroll) - { - for (int k = first; k < tabCount; k++) - { - if (rects[k].x + rects[k].width - rects[first].x > viewport - .getWidth()) - { - last = k; - break; - } - } - } - - for (int j = first; j <= last; j++) - { - if (j != selectedIndex || isScroll) - paintTab(g, tabPlacement, rects, j, ir, tr); - } - currRun = getPreviousTabRun(currRun); + int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; + if (isScroll) + first = currentScrollLocation; + else if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, currRun); + if (isScroll) + { + for (int k = first; k < tabCount; k++) + { + if (rects[k].x + rects[k].width - rects[first].x > viewport + .getWidth()) + { + last = k; + break; + } + } + } + + for (int j = first; j <= last; j++) + { + if (j != selectedIndex || isScroll) + paintTab(g, tabPlacement, rects, j, ir, tr); + } + currRun = getPreviousTabRun(currRun); } if (! isScroll) paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); @@ -1800,24 +1802,24 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int h = calcRect.height; if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1) { - Insets insets = getTabAreaInsets(tabPlacement); - switch (tabPlacement) - { - case TOP: - h += insets.bottom; - break; - case LEFT: - w += insets.right; - break; - case BOTTOM: - y -= insets.top; - h += insets.top; - break; - case RIGHT: - x -= insets.left; - w += insets.left; - break; - } + Insets insets = getTabAreaInsets(tabPlacement); + switch (tabPlacement) + { + case TOP: + h += insets.bottom; + break; + case LEFT: + w += insets.right; + break; + case BOTTOM: + y -= insets.top; + h += insets.top; + break; + case RIGHT: + x -= insets.left; + w += insets.left; + break; + } } layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect, @@ -1856,7 +1858,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, - SwingConstants.CENTER, tabRect, + SwingConstants.RIGHT, tabRect, iconRect, textRect, textIconGap); int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); @@ -1904,8 +1906,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants View textView = getTextViewForTab(tabIndex); if (textView != null) { - textView.paint(g, textRect); - return; + textView.paint(g, textRect); + return; } Color fg = tabPane.getForegroundAt(tabIndex); @@ -1921,37 +1923,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.isEnabledAt(tabIndex)) { - g.setColor(fg); + g.setColor(fg); - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x, - textRect.y - + metrics.getAscent()); - else - g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x, + textRect.y + + metrics.getAscent()); + else + g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); } else { - g.setColor(bg.brighter()); - - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x, textRect.y); - else - g.drawString(title, textRect.x, textRect.y); - - g.setColor(bg.darker()); - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x + 1, - textRect.y + 1); - else - g.drawString(title, textRect.x + 1, textRect.y + 1); + g.setColor(bg.brighter()); + + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x, textRect.y); + else + g.drawString(title, textRect.x, textRect.y); + + g.setColor(bg.darker()); + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x + 1, + textRect.y + 1); + else + g.drawString(title, textRect.x + 1, textRect.y + 1); } g.setColor(saved_color); @@ -2037,30 +2039,30 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (! isSelected || tabPlacement != SwingConstants.TOP) { - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x, y + h, x + w, y + h); } if (! isSelected || tabPlacement != SwingConstants.LEFT) { - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); } if (! isSelected || tabPlacement != SwingConstants.RIGHT) { - g.setColor(lightHighlight); - g.drawLine(x, y, x, y + h); + g.setColor(lightHighlight); + g.drawLine(x, y, x, y + h); } if (! isSelected || tabPlacement != SwingConstants.BOTTOM) { - g.setColor(lightHighlight); - g.drawLine(x, y, x + w, y); + g.setColor(lightHighlight); + g.drawLine(x, y, x + w, y); } g.setColor(saved); @@ -2087,10 +2089,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants g.setColor(Color.LIGHT_GRAY); else { - Color bg = tabPane.getBackgroundAt(tabIndex); - if (bg == null) - bg = Color.GRAY; - g.setColor(bg); + Color bg = tabPane.getBackgroundAt(tabIndex); + if (bg == null) + bg = Color.GRAY; + g.setColor(bg); } g.fillRect(x, y, w, h); @@ -2144,14 +2146,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - - g.drawLine(x, y, startgap - diff, y); - g.drawLine(endgap - diff, y, x + w, y); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.x; + } + + g.drawLine(x, y, startgap - diff, y); + g.drawLine(endgap - diff, y, x + w, y); } else g.drawLine(x, y, x + w, y); @@ -2184,14 +2186,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.LEFT) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - - g.drawLine(x, y, x, startgap - diff); - g.drawLine(x, endgap - diff, x, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.y; + } + + g.drawLine(x, y, x, startgap - diff); + g.drawLine(x, endgap - diff, x, y + h); } else g.drawLine(x, y, x, y + h); @@ -2223,26 +2225,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.BOTTOM) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); - g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - g.drawLine(x, y + h, startgap - diff, y + h); - g.drawLine(endgap - diff, y + h, x + w, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.x; + } + + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); + g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(darkShadow); + g.drawLine(x, y + h, startgap - diff, y + h); + g.drawLine(endgap - diff, y + h, x + w, y + h); } else { - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x, y + h, x + w, y + h); } g.setColor(saved); @@ -2271,26 +2273,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.RIGHT) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); - g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, startgap - diff); - g.drawLine(x + w, endgap - diff, x + w, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.y; + } + + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); + g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); + + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, startgap - diff); + g.drawLine(x + w, endgap - diff, x + w, y + h); } else { - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, y + h); } g.setColor(saved); @@ -2337,16 +2339,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int currRun = 1; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - for (int j = first; j <= last; j++) - { - if (getTabBounds(pane, j).contains(p)) - return j; - } - currRun = getNextTabRun(currRun); + int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, currRun); + for (int j = first; j <= last; j++) + { + if (getTabBounds(pane, j).contains(p)) + return j; + } + currRun = getNextTabRun(currRun); } return -1; } @@ -2400,10 +2402,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return; else { - int numToCopy = Math.min(tabCount, rects.length); - Rectangle[] tmp = new Rectangle[tabCount]; - System.arraycopy(rects, 0, tmp, 0, numToCopy); - rects = tmp; + int numToCopy = Math.min(tabCount, rects.length); + Rectangle[] tmp = new Rectangle[tabCount]; + System.arraycopy(rects, 0, tmp, 0, numToCopy); + rects = tmp; } } @@ -2418,9 +2420,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabRuns = new int[10]; else { - int[] newRuns = new int[tabRuns.length + 10]; - System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); - tabRuns = newRuns; + int[] newRuns = new int[tabRuns.length + 10]; + System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); + tabRuns = newRuns; } } @@ -2438,12 +2440,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return 1; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (last >= tabIndex && first <= tabIndex) - return i; + int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, i); + if (last >= tabIndex && first <= tabIndex) + return i; } return -1; } @@ -2560,21 +2562,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Icon icon = getIconForTab(tabIndex); Insets insets = getTabInsets(tabPlacement, tabIndex); + int height = 0; if (icon != null) { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - calcRect = tr.union(ir); + Rectangle vr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + layoutLabel(tabPlacement, getFontMetrics(), tabIndex, + tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, + tabIndex == tabPane.getSelectedIndex()); + height = tr.union(ir).height; } else - calcRect.height = fontHeight; + height = fontHeight; - calcRect.height += insets.top + insets.bottom; - return calcRect.height; + height += insets.top + insets.bottom; + return height; } /** @@ -2614,21 +2617,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Icon icon = getIconForTab(tabIndex); Insets insets = getTabInsets(tabPlacement, tabIndex); + int width = 0; if (icon != null) { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - calcRect = tr.union(ir); + Rectangle vr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + layoutLabel(tabPlacement, getFontMetrics(), tabIndex, + tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, + tabIndex == tabPane.getSelectedIndex()); + width = tr.union(ir).width; } else - calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); + width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); - calcRect.width += insets.left + insets.right; - return calcRect.width; + width += insets.left + insets.right; + return width; } /** @@ -2775,37 +2779,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - if (direction == SwingConstants.WEST) - selectPreviousTabInRun(tabPane.getSelectedIndex()); - else if (direction == SwingConstants.EAST) - selectNextTabInRun(tabPane.getSelectedIndex()); - - else - { - int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), - tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); - selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), - offset); - } + if (direction == SwingConstants.WEST) + selectPreviousTabInRun(tabPane.getSelectedIndex()); + else if (direction == SwingConstants.EAST) + selectNextTabInRun(tabPane.getSelectedIndex()); + + else + { + int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), + tabPane.getSelectedIndex(), + (tabPlacement == SwingConstants.RIGHT) + ? true : false); + selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), + offset); + } } if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { - if (direction == SwingConstants.NORTH) - selectPreviousTabInRun(tabPane.getSelectedIndex()); - else if (direction == SwingConstants.SOUTH) - selectNextTabInRun(tabPane.getSelectedIndex()); - else - { - int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), - tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); - selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), - offset); - } + if (direction == SwingConstants.NORTH) + selectPreviousTabInRun(tabPane.getSelectedIndex()); + else if (direction == SwingConstants.SOUTH) + selectNextTabInRun(tabPane.getSelectedIndex()); + else + { + int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), + tabPane.getSelectedIndex(), + (tabPlacement == SwingConstants.RIGHT) + ? true : false); + selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), + offset); + } } } @@ -2869,16 +2873,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int y = rects[tabIndex].y + rects[tabIndex].height / 2; switch (tabPlacement) - { - case SwingConstants.TOP: - case SwingConstants.BOTTOM: - y += offset; - break; - case SwingConstants.RIGHT: - case SwingConstants.LEFT: - x += offset; - break; - } + { + case SwingConstants.TOP: + case SwingConstants.BOTTOM: + y += offset; + break; + case SwingConstants.RIGHT: + case SwingConstants.LEFT: + x += offset; + break; + } int index = tabForCoordinate(tabPane, x, y); if (index != -1) @@ -3042,31 +3046,31 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Sun's version will happily throw an NPE if params are null, // so I won't check it either. switch (targetPlacement) - { - case SwingConstants.TOP: - targetInsets.top = topInsets.top; - targetInsets.left = topInsets.left; - targetInsets.right = topInsets.right; - targetInsets.bottom = topInsets.bottom; - break; - case SwingConstants.LEFT: - targetInsets.left = topInsets.top; - targetInsets.top = topInsets.left; - targetInsets.right = topInsets.bottom; - targetInsets.bottom = topInsets.right; - break; - case SwingConstants.BOTTOM: - targetInsets.top = topInsets.bottom; - targetInsets.bottom = topInsets.top; - targetInsets.left = topInsets.left; - targetInsets.right = topInsets.right; - break; - case SwingConstants.RIGHT: - targetInsets.top = topInsets.left; - targetInsets.left = topInsets.bottom; - targetInsets.bottom = topInsets.right; - targetInsets.right = topInsets.top; - break; - } + { + case SwingConstants.TOP: + targetInsets.top = topInsets.top; + targetInsets.left = topInsets.left; + targetInsets.right = topInsets.right; + targetInsets.bottom = topInsets.bottom; + break; + case SwingConstants.LEFT: + targetInsets.left = topInsets.top; + targetInsets.top = topInsets.left; + targetInsets.right = topInsets.bottom; + targetInsets.bottom = topInsets.right; + break; + case SwingConstants.BOTTOM: + targetInsets.top = topInsets.bottom; + targetInsets.bottom = topInsets.top; + targetInsets.left = topInsets.left; + targetInsets.right = topInsets.right; + break; + case SwingConstants.RIGHT: + targetInsets.top = topInsets.left; + targetInsets.left = topInsets.bottom; + targetInsets.bottom = topInsets.right; + targetInsets.right = topInsets.top; + break; + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java index 700b406d076..ec0467a74f3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -46,7 +46,7 @@ import java.awt.event.MouseEvent; import javax.swing.CellRendererPane; import javax.swing.JComponent; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -57,8 +57,7 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -public class BasicTableHeaderUI - extends TableHeaderUI +public class BasicTableHeaderUI extends TableHeaderUI { public static ComponentUI createUI(JComponent h) @@ -71,16 +70,42 @@ public class BasicTableHeaderUI protected CellRendererPane rendererPane; protected Border cellBorder; - class MouseInputHandler - implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { - public void mouseClicked(MouseEvent e) {} - public void mouseDragged(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) {} - public void mouseMoved(MouseEvent e) {} - public void mousePressed(MouseEvent e) {} - public void mouseReleased(MouseEvent e) {} + public void mouseClicked(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseDragged(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseEntered(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseExited(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseMoved(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mousePressed(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseReleased(MouseEvent e) + { + // TODO: Implement this properly. + } } protected MouseInputListener createMouseInputListener() @@ -95,15 +120,15 @@ public class BasicTableHeaderUI protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - header.setBackground(defaults.getColor("TableHeader.background")); - header.setForeground(defaults.getColor("TableHeader.foreground")); - header.setFont(defaults.getFont("TableHeader.font")); - cellBorder = defaults.getBorder("TableHeader.cellBorder"); + LookAndFeel.installColorsAndFont(header, "TableHeader.background", + "TableHeader.foreground", + "TableHeader.font"); + cellBorder = UIManager.getBorder("TableHeader.cellBorder"); } protected void installKeyboardActions() { + // TODO: Implement this properly. } protected void installListeners() @@ -128,6 +153,7 @@ public class BasicTableHeaderUI protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } protected void uninstallListeners() @@ -157,6 +183,7 @@ public class BasicTableHeaderUI Rectangle bounds = header.getHeaderRect(i); if (bounds.intersects(clip)) { + Rectangle oldClip = gfx.getClipBounds(); TableColumn col = cmod.getColumn(i); TableCellRenderer rend = col.getHeaderRenderer(); if (rend == null) @@ -173,10 +200,12 @@ public class BasicTableHeaderUI if (comp instanceof JComponent) ((JComponent)comp).setBorder(cellBorder); gfx.translate(bounds.x, bounds.y); + gfx.setClip(0, 0, bounds.width, bounds.height); comp.setSize(bounds.width, bounds.height); comp.setLocation(0,0); comp.paint(gfx); gfx.translate(-bounds.x, -bounds.y); + gfx.setClip(oldClip); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index 4559937eb69..25a845b36b8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -49,35 +49,38 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.ActionMap; -import javax.swing.BorderFactory; import javax.swing.CellRendererPane; +import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TableUI; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; -public class BasicTableUI - extends TableUI +public class BasicTableUI extends TableUI { public static ComponentUI createUI(JComponent comp) { @@ -93,23 +96,72 @@ public class BasicTableUI /** The normal cell border. */ Border cellBorder; - /** The cell border for selected/highlighted cells. */ - Border highlightCellBorder; - /** The action bound to KeyStrokes. */ TableAction action; - class FocusHandler implements FocusListener + /** + * Listens for changes to the tables properties. + */ + private PropertyChangeListener propertyChangeListener; + + /** + * Handles key events for the JTable. Key events should be handled through + * the InputMap/ActionMap mechanism since JDK1.3. This class is only there + * for backwards compatibility. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class KeyHandler implements KeyListener + { + + /** + * Receives notification that a key has been pressed and released. + * + * @param event the key event + */ + public void keyTyped(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + + /** + * Receives notification that a key has been pressed. + * + * @param event the key event + */ + public void keyPressed(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + + /** + * Receives notification that a key has been released. + * + * @param event the key event + */ + public void keyReleased(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + } + + public class FocusHandler implements FocusListener { public void focusGained(FocusEvent e) { + // TODO: Implement this properly. } + public void focusLost(FocusEvent e) { + // TODO: Implement this properly. } } - class MouseInputHandler implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { Point begin, curr; @@ -145,54 +197,123 @@ public class BasicTableUI public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseDragged(MouseEvent e) { - curr = new Point(e.getX(), e.getY()); - updateSelection(e.isControlDown()); + if (table.isEnabled()) + { + curr = new Point(e.getX(), e.getY()); + updateSelection(e.isControlDown()); + } } + public void mouseEntered(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseExited(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mousePressed(MouseEvent e) { - ListSelectionModel rowModel = table.getSelectionModel(); - ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); - int rowLead = rowModel.getLeadSelectionIndex(); - int colLead = colModel.getLeadSelectionIndex(); + if (table.isEnabled()) + { + ListSelectionModel rowModel = table.getSelectionModel(); + ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + int rowLead = rowModel.getLeadSelectionIndex(); + int colLead = colModel.getLeadSelectionIndex(); - begin = new Point(e.getX(), e.getY()); - curr = new Point(e.getX(), e.getY()); - //if control is pressed and the cell is already selected, deselect it - if (e.isControlDown() && table. - isCellSelected(table.rowAtPoint(begin),table.columnAtPoint(begin))) - { - table.getSelectionModel(). - removeSelectionInterval(table.rowAtPoint(begin), - table.rowAtPoint(begin)); - table.getColumnModel().getSelectionModel(). - removeSelectionInterval(table.columnAtPoint(begin), - table.columnAtPoint(begin)); - } - else - updateSelection(e.isControlDown()); + begin = new Point(e.getX(), e.getY()); + curr = new Point(e.getX(), e.getY()); + //if control is pressed and the cell is already selected, deselect it + if (e.isControlDown() && table. + isCellSelected(table.rowAtPoint(begin),table.columnAtPoint(begin))) + { + table.getSelectionModel(). + removeSelectionInterval(table.rowAtPoint(begin), + table.rowAtPoint(begin)); + table.getColumnModel().getSelectionModel(). + removeSelectionInterval(table.columnAtPoint(begin), + table.columnAtPoint(begin)); + } + else + updateSelection(e.isControlDown()); - // If we were editing, but the moved to another cell, stop editing - if (rowLead != rowModel.getLeadSelectionIndex() || - colLead != colModel.getLeadSelectionIndex()) - if (table.isEditing()) - table.editingStopped(new ChangeEvent(e)); + // If we were editing, but the moved to another cell, stop editing + if (rowLead != rowModel.getLeadSelectionIndex() || + colLead != colModel.getLeadSelectionIndex()) + if (table.isEditing()) + table.editingStopped(new ChangeEvent(e)); + } } + public void mouseReleased(MouseEvent e) { - begin = null; - curr = null; + if (table.isEnabled()) + { + begin = null; + curr = null; + } + } + } + + /** + * Listens for changes to the model property of the JTable and adjusts some + * settings. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class PropertyChangeHandler implements PropertyChangeListener + { + /** + * Receives notification if one of the JTable's properties changes. + * + * @param ev the property change event + */ + public void propertyChange(PropertyChangeEvent ev) + { + String propName = ev.getPropertyName(); + if (propName.equals("model")) + { + ListSelectionModel rowSel = table.getSelectionModel(); + rowSel.clearSelection(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + colSel.clearSelection(); + TableModel model = table.getModel(); + + // Adjust lead and anchor selection indices of the row and column + // selection models. + if (model.getRowCount() > 0) + { + rowSel.setAnchorSelectionIndex(0); + rowSel.setLeadSelectionIndex(0); + } + else + { + rowSel.setAnchorSelectionIndex(-1); + rowSel.setLeadSelectionIndex(-1); + } + if (model.getColumnCount() > 0) + { + colSel.setAnchorSelectionIndex(0); + colSel.setLeadSelectionIndex(0); + } + else + { + colSel.setAnchorSelectionIndex(-1); + colSel.setLeadSelectionIndex(-1); + } + } } } @@ -206,6 +327,17 @@ public class BasicTableUI return new MouseInputHandler(); } + + /** + * Creates and returns a key listener for the JTable. + * + * @return a key listener for the JTable + */ + protected KeyListener createKeyListener() + { + return new KeyHandler(); + } + /** * Return the maximum size of the table. The maximum height is the row * height times the number of rows. The maximum width is the sum of @@ -255,47 +387,13 @@ public class BasicTableUI protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - table.setFont(defaults.getFont("Table.font")); - table.setGridColor(defaults.getColor("Table.gridColor")); - table.setForeground(defaults.getColor("Table.foreground")); - table.setBackground(defaults.getColor("Table.background")); - table.setSelectionForeground(defaults.getColor("Table.selectionForeground")); - table.setSelectionBackground(defaults.getColor("Table.selectionBackground")); + LookAndFeel.installColorsAndFont(table, "Table.background", + "Table.foreground", "Table.font"); + table.setGridColor(UIManager.getColor("Table.gridColor")); + table.setSelectionForeground(UIManager.getColor("Table.selectionForeground")); + table.setSelectionBackground(UIManager.getColor("Table.selectionBackground")); table.setOpaque(true); - - highlightCellBorder = defaults.getBorder("Table.focusCellHighlightBorder"); - cellBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); - } - - private int convertModifiers(int mod) - { - if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0) - { - mod |= KeyEvent.SHIFT_MASK; - mod &= ~KeyEvent.SHIFT_DOWN_MASK; - } - if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0) - { - mod |= KeyEvent.CTRL_MASK; - mod &= ~KeyEvent.CTRL_DOWN_MASK; - } - if ((mod & KeyEvent.META_DOWN_MASK) != 0) - { - mod |= KeyEvent.META_MASK; - mod &= ~KeyEvent.META_DOWN_MASK; - } - if ((mod & KeyEvent.ALT_DOWN_MASK) != 0) - { - mod |= KeyEvent.ALT_MASK; - mod &= ~KeyEvent.ALT_DOWN_MASK; - } - if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0) - { - mod |= KeyEvent.ALT_GRAPH_MASK; - mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK; - } - return mod; + rendererPane = new CellRendererPane(); } protected void installKeyboardActions() @@ -304,27 +402,21 @@ public class BasicTableUI InputMap ancestorMap = (InputMap)defaults.get("Table.ancestorInputMap"); InputMapUIResource parentInputMap = new InputMapUIResource(); // FIXME: The JDK uses a LazyActionMap for parentActionMap - ActionMap parentActionMap = new ActionMap(); + ActionMap parentActionMap = new ActionMapUIResource(); action = new TableAction(); Object keys[] = ancestorMap.allKeys(); // Register key bindings in the UI InputMap-ActionMap pair - // Note that we register key bindings with both the old and new modifier - // masks: InputEvent.SHIFT_MASK and InputEvent.SHIFT_DOWN_MASK and so on. for (int i = 0; i < keys.length; i++) { - parentInputMap.put(KeyStroke.getKeyStroke - (((KeyStroke)keys[i]).getKeyCode(), convertModifiers - (((KeyStroke)keys[i]).getModifiers())), - (String)ancestorMap.get((KeyStroke)keys[i])); + KeyStroke stroke = (KeyStroke)keys[i]; + String actionString = (String) ancestorMap.get(stroke); - parentInputMap.put(KeyStroke.getKeyStroke - (((KeyStroke)keys[i]).getKeyCode(), - ((KeyStroke)keys[i]).getModifiers()), - (String)ancestorMap.get((KeyStroke)keys[i])); + parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), + stroke.getModifiers()), + actionString); - parentActionMap.put - ((String)ancestorMap.get((KeyStroke)keys[i]), new ActionListenerProxy - (action, (String)ancestorMap.get((KeyStroke)keys[i]))); + parentActionMap.put (actionString, + new ActionListenerProxy (action, actionString)); } // Set the UI InputMap-ActionMap pair to be the parents of the @@ -383,8 +475,8 @@ public class BasicTableUI */ public void actionPerformed (ActionEvent e) { - ListSelectionModel rowModel = table.getSelectionModel(); - ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel(); + DefaultListSelectionModel colModel = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel(); int rowLead = rowModel.getLeadSelectionIndex(); int rowMax = table.getModel().getRowCount() - 1; @@ -392,74 +484,75 @@ public class BasicTableUI int colLead = colModel.getLeadSelectionIndex(); int colMax = table.getModel().getColumnCount() - 1; - if (e.getActionCommand().equals("selectPreviousRowExtendSelection")) + String command = e.getActionCommand(); + + if (command.equals("selectPreviousRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectLastColumn")) + else if (command.equals("selectLastColumn")) { - table.clearSelection(); rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(colMax, colMax); } - else if (e.getActionCommand().equals("startEditing")) + else if (command.equals("startEditing")) { if (table.isCellEditable(rowLead, colLead)) table.editCellAt(rowLead,colLead); } - else if (e.getActionCommand().equals("selectFirstRowExtendSelection")) + else if (command.equals("selectFirstRowExtendSelection")) { rowModel.setLeadSelectionIndex(0); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectFirstColumn")) + else if (command.equals("selectFirstColumn")) { rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(0, 0); } - else if (e.getActionCommand().equals("selectFirstColumnExtendSelection")) + else if (command.equals("selectFirstColumnExtendSelection")) { colModel.setLeadSelectionIndex(0); rowModel.setLeadSelectionIndex(rowLead); - } - else if (e.getActionCommand().equals("selectLastRow")) + } + else if (command.equals("selectLastRow")) { rowModel.setSelectionInterval(rowMax,rowMax); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("selectNextRowExtendSelection")) + else if (command.equals("selectNextRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectFirstRow")) + else if (command.equals("selectFirstRow")) { rowModel.setSelectionInterval(0,0); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("selectNextColumnExtendSelection")) + else if (command.equals("selectNextColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax)); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectLastColumnExtendSelection")) + else if (command.equals("selectLastColumnExtendSelection")) { colModel.setLeadSelectionIndex(colMax); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectPreviousColumnExtendSelection")) + else if (command.equals("selectPreviousColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0)); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectNextRow")) + else if (command.equals("selectNextRow")) { rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), Math.min(rowLead + 1, rowMax)); colModel.setSelectionInterval(colLead,colLead); } - else if (e.getActionCommand().equals("scrollUpExtendSelection")) + else if (command.equals("scrollUpExtendSelection")) { int target; if (rowLead == getFirstVisibleRowIndex()) @@ -472,13 +565,13 @@ public class BasicTableUI rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectPreviousRow")) + else if (command.equals("selectPreviousRow")) { rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), Math.max(rowLead - 1, 0)); colModel.setSelectionInterval(colLead,colLead); } - else if (e.getActionCommand().equals("scrollRightChangeSelection")) + else if (command.equals("scrollRightChangeSelection")) { int target; if (colLead == getLastVisibleColumnIndex()) @@ -491,13 +584,13 @@ public class BasicTableUI colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); } - else if (e.getActionCommand().equals("selectPreviousColumn")) + else if (command.equals("selectPreviousColumn")) { rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.max(colLead - 1, 0), Math.max(colLead - 1, 0)); } - else if (e.getActionCommand().equals("scrollLeftChangeSelection")) + else if (command.equals("scrollLeftChangeSelection")) { int target; if (colLead == getFirstVisibleColumnIndex()) @@ -510,11 +603,11 @@ public class BasicTableUI colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); } - else if (e.getActionCommand().equals("clearSelection")) + else if (command.equals("clearSelection")) { table.clearSelection(); } - else if (e.getActionCommand().equals("cancel")) + else if (command.equals("cancel")) { // FIXME: implement other parts of "cancel" like undo-ing last // selection. Right now it just calls editingCancelled if @@ -522,10 +615,10 @@ public class BasicTableUI if (table.isEditing()) table.editingCanceled(new ChangeEvent("cancel")); } - else if (e.getActionCommand().equals("selectNextRowCell") - || e.getActionCommand().equals("selectPreviousRowCell") - || e.getActionCommand().equals("selectNextColumnCell") - || e.getActionCommand().equals("selectPreviousColumnCell")) + else if (command.equals("selectNextRowCell") + || command.equals("selectPreviousRowCell") + || command.equals("selectNextColumnCell") + || command.equals("selectPreviousColumnCell")) { // If nothing is selected, select the first cell in the table if (table.getSelectedRowCount() == 0 && @@ -561,13 +654,13 @@ public class BasicTableUI // when you get to the edges of the table. if (!multColsSelected && !multRowsSelected) { - if (e.getActionCommand().indexOf("Column") != -1) + if (command.indexOf("Column") != -1) advanceSingleSelection(colModel, colMax, rowModel, rowMax, - (e.getActionCommand().equals + (command.equals ("selectPreviousColumnCell"))); else advanceSingleSelection(rowModel, rowMax, colModel, colMax, - (e.getActionCommand().equals + (command.equals ("selectPreviousRowCell"))); return; } @@ -588,25 +681,25 @@ public class BasicTableUI // If there are multiple rows and columns selected, select the next // cell and wrap at the edges of the selection. - if (e.getActionCommand().indexOf("Column") != -1) + if (command.indexOf("Column") != -1) advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, rowModel, rowMinSelected, rowMaxSelected, - (e.getActionCommand().equals + (command.equals ("selectPreviousColumnCell")), true); else advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, colModel, colMinSelected, colMaxSelected, - (e.getActionCommand().equals + (command.equals ("selectPreviousRowCell")), false); } - else if (e.getActionCommand().equals("selectNextColumn")) + else if (command.equals("selectNextColumn")) { rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.min(colLead + 1, colMax), Math.min(colLead + 1, colMax)); } - else if (e.getActionCommand().equals("scrollLeftExtendSelection")) + else if (command.equals("scrollLeftExtendSelection")) { int target; if (colLead == getFirstVisibleColumnIndex()) @@ -619,7 +712,7 @@ public class BasicTableUI colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("scrollDownChangeSelection")) + else if (command.equals("scrollDownChangeSelection")) { int target; if (rowLead == getLastVisibleRowIndex()) @@ -632,7 +725,7 @@ public class BasicTableUI rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("scrollRightExtendSelection")) + else if (command.equals("scrollRightExtendSelection")) { int target; if (colLead == getLastVisibleColumnIndex()) @@ -645,16 +738,16 @@ public class BasicTableUI colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectAll")) + else if (command.equals("selectAll")) { table.selectAll(); } - else if (e.getActionCommand().equals("selectLastRowExtendSelection")) + else if (command.equals("selectLastRowExtendSelection")) { rowModel.setLeadSelectionIndex(rowMax); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("scrollDownExtendSelection")) + else if (command.equals("scrollDownExtendSelection")) { int target; if (rowLead == getLastVisibleRowIndex()) @@ -666,8 +759,8 @@ public class BasicTableUI rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); - } - else if (e.getActionCommand().equals("scrollUpChangeSelection")) + } + else if (command.equals("scrollUpChangeSelection")) { int target; if (rowLead == getFirstVisibleRowIndex()) @@ -680,22 +773,119 @@ public class BasicTableUI rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); } + else if (command.equals("selectNextRowChangeLead")) + { + if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just "selectNextRow" + rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), + Math.min(rowLead + 1, rowMax)); + colModel.setSelectionInterval(colLead,colLead); + } + else + rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); + } + else if (command.equals("selectPreviousRowChangeLead")) + { + if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectPreviousRow + rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), + Math.min(rowLead -1, 0)); + colModel.setSelectionInterval(colLead,colLead); + } + else + rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0)); + } + else if (command.equals("selectNextColumnChangeLead")) + { + if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectNextColumn + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.min(colLead + 1, colMax), + Math.min(colLead + 1, colMax)); + } + else + colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax)); + } + else if (command.equals("selectPreviousColumnChangeLead")) + { + if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectPreviousColumn + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.max(colLead - 1, 0), + Math.max(colLead - 1, 0)); + + } + else + colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0)); + } + else if (command.equals("addToSelection")) + { + if (!table.isEditing()) + { + int oldRowAnchor = rowModel.getAnchorSelectionIndex(); + int oldColAnchor = colModel.getAnchorSelectionIndex(); + rowModel.addSelectionInterval(rowLead, rowLead); + colModel.addSelectionInterval(colLead, colLead); + rowModel.setAnchorSelectionIndex(oldRowAnchor); + colModel.setAnchorSelectionIndex(oldColAnchor); + } + } + else if (command.equals("extendTo")) + { + rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(), + rowLead); + colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(), + colLead); + } + else if (command.equals("toggleAndAnchor")) + { + if (rowModel.isSelectedIndex(rowLead)) + rowModel.removeSelectionInterval(rowLead, rowLead); + else + rowModel.addSelectionInterval(rowLead, rowLead); + + if (colModel.isSelectedIndex(colLead)) + colModel.removeSelectionInterval(colLead, colLead); + else + colModel.addSelectionInterval(colLead, colLead); + + rowModel.setAnchorSelectionIndex(rowLead); + colModel.setAnchorSelectionIndex(colLead); + } else { // If we're here that means we bound this TableAction class // to a keyboard input but we either want to ignore that input // or we just haven't implemented its action yet. + + // Uncomment the following line to print the names of unused bindings + // when their keys are pressed + + // System.out.println ("not implemented: "+e.getActionCommand()); } - if (table.isEditing() && e.getActionCommand() != "startEditing") - table.editingCanceled(new ChangeEvent("update")); - table.repaint(); - + // Any commands whose keyStrokes should be used by the Editor should not + // cause editing to be stopped: ie, the SPACE sends "addToSelection" but + // if the table is in editing mode, the space should not cause us to stop + // editing because it should be used by the Editor. + if (table.isEditing() && command != "startEditing" + && command != "addToSelection") + table.editingStopped(new ChangeEvent("update")); + table.scrollRectToVisible (table.getCellRect(rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(), false)); + table.repaint(); } + /** + * Returns the column index of the first visible column. + * @return the column index of the first visible column. + */ int getFirstVisibleColumnIndex() { ComponentOrientation or = table.getComponentOrientation(); @@ -922,10 +1112,19 @@ public class BasicTableUI protected void installListeners() { - table.addFocusListener(focusListener); + if (focusListener == null) + focusListener = createFocusListener(); + table.addFocusListener(focusListener); + if (keyListener == null) + keyListener = createKeyListener(); table.addKeyListener(keyListener); + if (mouseInputListener == null) + mouseInputListener = createMouseInputListener(); table.addMouseListener(mouseInputListener); table.addMouseMotionListener(mouseInputListener); + if (propertyChangeListener == null) + propertyChangeListener = new PropertyChangeHandler(); + table.addPropertyChangeListener(propertyChangeListener); } protected void uninstallDefaults() @@ -950,6 +1149,7 @@ public class BasicTableUI protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } protected void uninstallListeners() @@ -958,13 +1158,13 @@ public class BasicTableUI table.removeKeyListener(keyListener); table.removeMouseListener(mouseInputListener); table.removeMouseMotionListener(mouseInputListener); + table.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; } public void installUI(JComponent comp) { table = (JTable)comp; - focusListener = createFocusListener(); - mouseInputListener = createMouseInputListener(); installDefaults(); installKeyboardActions(); installListeners(); @@ -977,6 +1177,60 @@ public class BasicTableUI uninstallDefaults(); } + /** + * Paints a single cell in the table. + * + * @param g The graphics context to paint in + * @param row The row number to paint + * @param col The column number to paint + * @param bounds The bounds of the cell to paint, assuming a coordinate + * system beginning at <code>(0,0)</code> in the upper left corner of the + * table + * @param rend A cell renderer to paint with + * @param data The data to provide to the cell renderer + * @param rowLead The lead selection for the rows of the table. + * @param colLead The lead selection for the columns of the table. + */ + void paintCell(Graphics g, int row, int col, Rectangle bounds, + TableCellRenderer rend, TableModel data, + int rowLead, int colLead) + { + boolean rowSelAllowed = table.getRowSelectionAllowed(); + boolean colSelAllowed = table.getColumnSelectionAllowed(); + boolean isSel = false; + if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) + isSel = table.isCellSelected(row, col); + else + isSel = table.isRowSelected(row) && table.getRowSelectionAllowed() + || table.isColumnSelected(col) && table.getColumnSelectionAllowed(); + + // Determine the focused cell. The focused cell is the cell at the + // leadSelectionIndices of the row and column selection model. + ListSelectionModel rowSel = table.getSelectionModel(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + boolean hasFocus = table.hasFocus() && table.isEnabled() + && rowSel.getLeadSelectionIndex() == row + && colSel.getLeadSelectionIndex() == col; + + Component comp = rend.getTableCellRendererComponent(table, + data.getValueAt(row, col), + isSel, hasFocus, row, col); + + rendererPane.paintComponent(g, comp, table, bounds); + + // FIXME: this is manual painting of the Caret, why doesn't the + // JTextField take care of this itself? + if (comp instanceof JTextField) + { + Rectangle oldClip = g.getClipBounds(); + g.translate(bounds.x, bounds.y); + g.clipRect(0, 0, bounds.width, bounds.height); + ((JTextField)comp).getCaret().paint(g); + g.translate(-bounds.x, -bounds.y); + g.setClip(oldClip); + } + } + public void paint(Graphics gfx, JComponent ignored) { int ncols = table.getColumnCount(); @@ -1002,40 +1256,24 @@ public class BasicTableUI y = y0; TableColumn col = cols.getColumn(c); int width = col.getWidth(); - int modelCol = col.getModelIndex(); - + int halfGapWidth = gap.width / 2; + int halfGapHeight = gap.height / 2; for (int r = 0; r < nrows && y < ymax; ++r) { - Rectangle bounds = new Rectangle(x, y, width, height); - if (bounds.intersects(clip)) - { - TableCellRenderer rend = table.getCellRenderer(r, c); - Component comp = table.prepareRenderer(rend, r, c); - gfx.translate(x, y); - comp.setBounds(new Rectangle(0, 0, width, height)); - // Set correct border on cell renderer. - // Only the lead selection cell gets a border - if (comp instanceof JComponent) - { - if (table.getSelectionModel().getLeadSelectionIndex() == r - && table.getColumnModel().getSelectionModel(). - getLeadSelectionIndex() == c) - ((JComponent) comp).setBorder(highlightCellBorder); - else - ((JComponent) comp).setBorder(cellBorder); - } - comp.paint(gfx); - if (comp instanceof JTextField) - ((JTextField)comp).getCaret().paint(gfx); - gfx.translate(-x, -y); + Rectangle bounds = new Rectangle(x + halfGapWidth, + y + halfGapHeight + 1, + width - gap.width + 1, + height - gap.height); + if (bounds.intersects(clip)) + { + paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c), + table.getModel(), + table.getSelectionModel().getLeadSelectionIndex(), + table.getColumnModel().getSelectionModel().getLeadSelectionIndex()); } - y += height; - if (gap != null) - y += gap.height; + y += height; } x += width; - if (gap != null) - x += gap.width; } // tighten up the x and y max bounds @@ -1044,7 +1282,7 @@ public class BasicTableUI Color grid = table.getGridColor(); - // paint vertical grid lines + // paint vertical grid lines if (grid != null && table.getShowVerticalLines()) { x = x0; @@ -1053,9 +1291,7 @@ public class BasicTableUI boolean paintedLine = false; for (int c = 0; c < ncols && x < xmax; ++c) { - x += cols.getColumn(c).getWidth();; - if (gap != null) - x += gap.width; + x += cols.getColumn(c).getWidth(); gfx.drawLine(x, y0, x, ymax); paintedLine = true; } @@ -1072,8 +1308,6 @@ public class BasicTableUI for (int r = 0; r < nrows && y < ymax; ++r) { y += height; - if (gap != null) - y += gap.height; gfx.drawLine(x0, y, xmax, y); paintedLine = true; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java index 97b0ccb6ee6..36854e07fe0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java @@ -39,11 +39,16 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.beans.PropertyChangeEvent; + import javax.swing.JComponent; +import javax.swing.JTextArea; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PlainView; import javax.swing.text.View; +import javax.swing.text.WrappedPlainView; public class BasicTextAreaUI extends BasicTextUI { @@ -54,15 +59,55 @@ public class BasicTextAreaUI extends BasicTextUI public BasicTextAreaUI() { + // Nothing to do here. } + /** + * Create the view. Returns a WrappedPlainView if the text area + * has lineWrap set to true, otherwise returns a PlainView. If + * lineWrap is true has to check whether the wrap style is word + * or character and return an appropriate WrappedPlainView. + * + * @param elem the element to create a View for + * @return an appropriate View for the element + */ public View create(Element elem) { - return new PlainView(elem); + JTextArea comp = (JTextArea)getComponent(); + if (comp.getLineWrap()) + { + if (comp.getWrapStyleWord()) + return new WrappedPlainView(elem, true); + else + return new WrappedPlainView(elem, false); + } + else + return new PlainView(elem); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextArea" + */ protected String getPropertyPrefix() { return "TextArea"; } + + /** + * Receives notification whenever one of the text component's bound + * properties changes. This changes the view to WrappedPlainView + * if setLineWrap(true) is called, and back to PlainView if + * setLineWrap(false) is called. + * + * @param ev the property change event + */ + protected void propertyChange(PropertyChangeEvent ev) + { + JTextArea comp = (JTextArea)getComponent(); + if (ev.getPropertyName() == "lineWrap" + || ev.getPropertyName() == "wrapStyleWord") + modelChanged(); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java index a300446c262..4e2ca9f93df 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java @@ -41,6 +41,7 @@ package javax.swing.plaf.basic; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.FieldView; @@ -63,6 +64,11 @@ public class BasicTextFieldUI extends BasicTextUI return new BasicTextFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextField" + */ protected String getPropertyPrefix() { return "TextField"; @@ -73,8 +79,22 @@ public class BasicTextFieldUI extends BasicTextUI super.installUI(c); } + /** + * Receives notification whenever one of the text component's bound + * properties changes. Here we check for the editable and enabled + * properties and adjust the background color accordingly. + * + * @param event the property change event + */ protected void propertyChange(PropertyChangeEvent event) { - // Does nothing by default. + if (event.getPropertyName().equals("editable")) + { + boolean editable = ((Boolean) event.getNewValue()).booleanValue(); + if (editable) + textComponent.setBackground(background); + else + textComponent.setBackground(inactiveBackground); + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java index 55d908e1b88..decbed56829 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java @@ -38,10 +38,18 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; + import javax.swing.JComponent; +import javax.swing.JTextPane; +import javax.swing.plaf.ColorUIResource; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PlainView; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; import javax.swing.text.View; public class BasicTextPaneUI extends BasicEditorPaneUI @@ -61,8 +69,32 @@ public class BasicTextPaneUI extends BasicEditorPaneUI return new PlainView(elem); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextPane" + */ protected String getPropertyPrefix() { return "TextPane"; } + + /** + * Installs this UI on the specified <code>JTextPane</code>. This calls the + * super implementation and then adds a default style to the text pane. + * + * @param c the text pane to install the UI to + */ + public void installUI(JComponent c) + { + super.installUI(c); + JTextPane tp = (JTextPane) c; + Style defaultStyle = tp.getStyle(StyleContext.DEFAULT_STYLE); + defaultStyle.addAttribute(StyleConstants.Foreground, + new ColorUIResource(Color.BLACK)); + defaultStyle.addAttribute(StyleConstants.FontFamily, "Serif"); + defaultStyle.addAttribute(StyleConstants.Italic, Boolean.FALSE); + defaultStyle.addAttribute(StyleConstants.Bold, Boolean.FALSE); + defaultStyle.addAttribute(StyleConstants.FontSize, new Integer(12)); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index 91ccb0056bb..b9de92640c8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; @@ -54,6 +55,8 @@ import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -73,7 +76,6 @@ import javax.swing.text.Element; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; -import javax.swing.text.PlainView; import javax.swing.text.Position; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -92,11 +94,11 @@ public abstract class BasicTextUI extends TextUI /** * A {@link DefaultCaret} that implements {@link UIResource}. */ - public static class BasicCaret extends DefaultCaret - implements UIResource + public static class BasicCaret extends DefaultCaret implements UIResource { public BasicCaret() { + // Nothing to do here. } } @@ -108,6 +110,7 @@ public abstract class BasicTextUI extends TextUI { public BasicHighlighter() { + // Nothing to do here. } } @@ -241,7 +244,7 @@ public abstract class BasicTextUI extends TextUI public void paint(Graphics g, Shape s) { if (view != null) - view.paint(g, s); + view.paint(g, s); } @@ -252,10 +255,10 @@ public abstract class BasicTextUI extends TextUI * * This is delegated to the real root view. * - * @param pos the position of the character in the model + * @param position the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param bias either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -327,12 +330,41 @@ public abstract class BasicTextUI extends TextUI { view.changedUpdate(ev, shape, vf); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + return view.getNextVisualPositionFrom(c, pos, b, d, biasRet); + } } /** * Receives notifications when properties of the text component change. */ - class UpdateHandler implements PropertyChangeListener + class PropertyChangeHandler implements PropertyChangeListener { /** * Notifies when a property of the text component changes. @@ -342,10 +374,12 @@ public abstract class BasicTextUI extends TextUI public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals("document")) - { + { // Document changed. - modelChanged(); - } + modelChanged(); + } + + BasicTextUI.this.propertyChange(event); } } @@ -364,11 +398,10 @@ public abstract class BasicTextUI extends TextUI */ public void changedUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.changedUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.changedUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); } - + /** * Notification about a document insert event. * @@ -376,12 +409,8 @@ public abstract class BasicTextUI extends TextUI */ public void insertUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.insertUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.insertUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); - int caretPos = textComponent.getCaretPosition(); - if (caretPos >= ev.getOffset()) - textComponent.setCaretPosition(caretPos + ev.getLength()); } /** @@ -391,12 +420,8 @@ public abstract class BasicTextUI extends TextUI */ public void removeUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.removeUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.removeUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); - int caretPos = textComponent.getCaretPosition(); - if (caretPos >= ev.getOffset()) - textComponent.setCaretPosition(ev.getOffset()); } } @@ -419,16 +444,29 @@ public abstract class BasicTextUI extends TextUI /** * Receives notification when the model changes. */ - UpdateHandler updateHandler = new UpdateHandler(); + PropertyChangeHandler updateHandler = new PropertyChangeHandler(); /** The DocumentEvent handler. */ DocumentHandler documentHandler = new DocumentHandler(); /** + * The standard background color. This is the color which is used to paint + * text in enabled text components. + */ + Color background; + + /** + * The inactive background color. This is the color which is used to paint + * text in disabled text components. + */ + Color inactiveBackground; + + /** * Creates a new <code>BasicTextUI</code> instance. */ public BasicTextUI() { + // Nothing to do here. } /** @@ -506,14 +544,20 @@ public abstract class BasicTextUI extends TextUI textComponent.setHighlighter(createHighlighter()); String prefix = getPropertyPrefix(); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - textComponent.setBackground(defaults.getColor(prefix + ".background")); - textComponent.setForeground(defaults.getColor(prefix + ".foreground")); - textComponent.setMargin(defaults.getInsets(prefix + ".margin")); - textComponent.setBorder(defaults.getBorder(prefix + ".border")); - textComponent.setFont(defaults.getFont(prefix + ".font")); - - caret.setBlinkRate(defaults.getInt(prefix + ".caretBlinkRate")); + LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + LookAndFeel.installBorder(textComponent, prefix + ".border"); + textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); + + caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); + + // Fetch the colors for enabled/disabled text components. + background = UIManager.getColor(prefix + ".background"); + inactiveBackground = UIManager.getColor(prefix + ".inactiveBackground"); + textComponent.setDisabledTextColor + (UIManager.getColor(prefix + ".inactiveForeground")); + textComponent.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground")); + textComponent.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground")); } /** @@ -704,6 +748,7 @@ public abstract class BasicTextUI extends TextUI protected void uninstallListeners() { textComponent.removeFocusListener(focuslistener); + textComponent.getDocument().removeDocumentListener(documentHandler); } /** @@ -757,6 +802,18 @@ public abstract class BasicTextUI extends TextUI } /** + * Returns the minimum size for text components. This returns the size + * of the component's insets. + * + * @return the minimum size for text components + */ + public Dimension getMinimumSize(JComponent c) + { + Insets i = c.getInsets(); + return new Dimension(i.left + i.right, i.top + i.bottom); + } + + /** * Paints the text component. * * @param g the <code>Graphics</code> context to paint to @@ -776,10 +833,10 @@ public abstract class BasicTextUI extends TextUI { Caret caret = textComponent.getCaret(); Highlighter highlighter = textComponent.getHighlighter(); - + if (textComponent.isOpaque()) paintBackground(g); - + if (highlighter != null && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) highlighter.paint(g); @@ -797,8 +854,10 @@ public abstract class BasicTextUI extends TextUI */ protected void paintBackground(Graphics g) { - g.setColor(textComponent.getBackground()); - g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight()); + // This method does nothing. All the background filling is done by the + // ComponentUI update method. However, the method is called by paint + // to provide a way for subclasses to draw something different (e.g. + // background images etc) on the background. } /** @@ -885,10 +944,10 @@ public abstract class BasicTextUI extends TextUI /** * Maps a position in the document into the coordinate space of the View. * The output rectangle usually reflects the font height but has a width - * of zero. A bias of {@link Position.Bias.Forward} is used in this method. + * of zero. A bias of {@link Position.Bias#Forward} is used in this method. * + * @param t the text component * @param pos the position of the character in the model - * @param a the area that is occupied by the view * * @return a rectangle that gives the location of the document position * inside the view coordinate space @@ -908,10 +967,10 @@ public abstract class BasicTextUI extends TextUI * The output rectangle usually reflects the font height but has a width * of zero. * + * @param t the text component * @param pos the position of the character in the model - * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param bias either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -957,7 +1016,7 @@ public abstract class BasicTextUI extends TextUI */ public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn) { - return 0; // FIXME: Implement me. + return rootView.viewToModel(pt.x, pt.y, getVisibleEditorRect(), biasReturn); } /** @@ -999,16 +1058,17 @@ public abstract class BasicTextUI extends TextUI */ protected Rectangle getVisibleEditorRect() { + JTextComponent textComponent = getComponent(); int width = textComponent.getWidth(); int height = textComponent.getHeight(); if (width <= 0 || height <= 0) - return null; + return new Rectangle(0, 0, 0, 0); Insets insets = textComponent.getInsets(); return new Rectangle(insets.left, insets.top, - width - insets.left + insets.right, - height - insets.top + insets.bottom); + width - insets.left - insets.right, + height - insets.top - insets.bottom); } /** @@ -1020,6 +1080,8 @@ public abstract class BasicTextUI extends TextUI { rootView.setView(view); view.setParent(rootView); + textComponent.revalidate(); + textComponent.repaint(); } /** @@ -1043,4 +1105,17 @@ public abstract class BasicTextUI extends TextUI View view = factory.create(elem); setView(view); } + + /** + * Receives notification whenever one of the text component's bound + * properties changes. This default implementation does nothing. + * It is a hook that enables subclasses to react to property changes + * on the text component. + * + * @param ev the property change event + */ + protected void propertyChange(PropertyChangeEvent ev) + { + // The default implementation does nothing. + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java index 9106b0b6673..896ea0c89dc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java @@ -38,7 +38,13 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; public class BasicToggleButtonUI extends BasicButtonUI @@ -58,5 +64,62 @@ public class BasicToggleButtonUI extends BasicButtonUI { return "ToggleButton."; } -} + /** + * Paint the component, which is an {@link AbstractButton}, according to + * its current state. + * + * @param g The graphics context to paint with + * @param c The component to paint the state of + */ + public void paint(Graphics g, JComponent c) + { + AbstractButton b = (AbstractButton) c; + + Rectangle tr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle vr = new Rectangle(); + + Font f = c.getFont(); + + g.setFont(f); + + if (b.isBorderPainted()) + SwingUtilities.calculateInnerArea(b, vr); + else + vr = SwingUtilities.getLocalBounds(b); + String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), + b.getText(), + currentIcon(b), + b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + vr, ir, tr, + b.getIconTextGap() + + defaultTextShiftOffset); + + if ((b.getModel().isArmed() && b.getModel().isPressed()) + || b.isSelected()) + paintButtonPressed(g, b); + + paintIcon(g, b, ir); + if (text != null) + paintText(g, b, tr, text); + if (b.isFocusOwner() && b.isFocusPainted()) + paintFocus(g, b, vr, tr, ir); + } + + /** + * Paints the icon for the toggle button. This delegates to + * {@link BasicButtonUI#paintIcon(Graphics, JComponent, Rectangle)}. + * + * @param g the graphics context + * @param b the button to paint the icon for + * @param iconRect the area allocated for the icon + */ + protected void paintIcon(Graphics g, AbstractButton b, Rectangle iconRect) + { + super.paintIcon(g, b, iconRect); + } +} diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java index db29fdca583..79cf0b0c213 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java @@ -43,7 +43,6 @@ import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JSeparator; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; @@ -73,9 +72,7 @@ public class BasicToolBarSeparatorUI extends BasicSeparatorUI */ protected void installDefaults(JSeparator s) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - size = defaults.getDimension("ToolBar.separatorSize"); + size = UIManager.getDimension("ToolBar.separatorSize"); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java index 8be89efcfa6..ef4ed835f86 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java @@ -65,10 +65,11 @@ import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JToolBar; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.RootPaneContainer; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -133,6 +134,26 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants protected FocusListener toolBarFocusListener; /** + * @deprecated since JDK1.3. + */ + protected KeyStroke leftKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke rightKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke upKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke downKey; + + /** * The floating window that is responsible for holding the JToolBar when it * is dragged outside of its original parent. */ @@ -566,18 +587,16 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + LookAndFeel.installBorder(toolBar, "ToolBar.border"); + LookAndFeel.installColorsAndFont(toolBar, "ToolBar.background", + "ToolBar.foreground", "ToolBar.font"); - toolBar.setBorder(new ToolBarBorder()); - toolBar.setBackground(defaults.getColor("ToolBar.background")); - toolBar.setForeground(defaults.getColor("ToolBar.foreground")); - toolBar.setFont(defaults.getFont("ToolBar.font")); + dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground"); + dockingColor = UIManager.getColor("ToolBar.dockingBackground"); - dockingBorderColor = defaults.getColor("ToolBar.dockingForeground"); - dockingColor = defaults.getColor("ToolBar.dockingBackground"); - - floatingBorderColor = defaults.getColor("ToolBar.floatingForeground"); - floatingColor = defaults.getColor("ToolBar.floatingBackground"); + floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground"); + floatingColor = UIManager.getColor("ToolBar.floatingBackground"); + setRolloverBorders(toolBar.isRollover()); } /** @@ -591,10 +610,8 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants /** * This method installs listeners for the JToolBar. - * - * @param toolbar The JToolBar to register listeners for. */ - protected void installListeners(JToolBar toolbar) + protected void installListeners() { dockingListener = createDockingListener(); toolBar.addMouseListener(dockingListener); @@ -694,7 +711,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants toolBar.setOpaque(true); installDefaults(); installComponents(); - installListeners(toolBar); + installListeners(); installKeyboardActions(); } } @@ -1000,6 +1017,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -1030,13 +1048,15 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants } origin = new Point(0, 0); - SwingUtilities.convertPointToScreen(ssd, toolBar); + if (toolBar.isShowing()) + SwingUtilities.convertPointToScreen(ssd, toolBar); if (! (SwingUtilities.getAncestorOfClass(Window.class, toolBar) instanceof UIResource)) // Need to know who keeps the toolBar if it gets dragged back into it. origParent = toolBar.getParent(); - - SwingUtilities.convertPointToScreen(origin, toolBar); + + if (toolBar.isShowing()) + SwingUtilities.convertPointToScreen(origin, toolBar); isDragging = true; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java index b7a08aa728e..5cec2e33365 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java @@ -1,5 +1,5 @@ /* BasicToolTipUI.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,20 +39,18 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.Toolkit; import javax.swing.JComponent; import javax.swing.JToolTip; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ToolTipUI; @@ -61,58 +59,12 @@ import javax.swing.plaf.ToolTipUI; */ public class BasicToolTipUI extends ToolTipUI { - /** The default Border around the JToolTip. */ - private static Border defaultBorder = new Border() - { - // FIXME: This needs to go into Basic Look and Feel - // defaults. - /** - * This method returns the border insets. - * - * @param c The Component to find Border insets for. - * - * @return The Border insets. - */ - public Insets getBorderInsets(Component c) - { - return new Insets(4, 4, 4, 4); - } + /** The shared instance of BasicToolTipUI used for all ToolTips. */ + private static BasicToolTipUI shared; - /** - * This method returns whether the border is opaque. - * - * @return Whether the border is opaque. - */ - public boolean isBorderOpaque() - { - return false; - } - - /** - * This method paints the border. - * - * @param c The Component to paint this border around. - * @param g The Graphics object to paint with. - * @param x The x coordinate to start painting at. - * @param y The y coordinate to start painting at. - * @param w The width of the Component. - * @param h The height of the Component. - */ - public void paintBorder(Component c, Graphics g, int x, int y, int w, - int h) - { - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.drawRect(0, 0, w - 1, h - 1); - - g.setColor(saved); - } - }; - - /** The shared instance of BasicToolTipUI used for all ToolTips. */ - private static BasicToolTipUI shared; + /** The tooltip's text */ + private String text; /** * Creates a new BasicToolTipUI object. @@ -124,7 +76,7 @@ public class BasicToolTipUI extends ToolTipUI /** * This method creates a new BasicToolTip UI for the given - * JComponent. + * JComponent. * * @param c The JComponent to create a UI for. * @@ -132,9 +84,9 @@ public class BasicToolTipUI extends ToolTipUI */ public static ComponentUI createUI(JComponent c) { - if (shared == null) - shared = new BasicToolTipUI(); - return shared; + if (shared == null) + shared = new BasicToolTipUI(); + return shared; } /** @@ -171,12 +123,16 @@ public class BasicToolTipUI extends ToolTipUI public Dimension getPreferredSize(JComponent c) { JToolTip tip = (JToolTip) c; + FontMetrics fm; + Toolkit g = tip.getToolkit(); + text = tip.getTipText(); + Rectangle vr = new Rectangle(); Rectangle ir = new Rectangle(); Rectangle tr = new Rectangle(); Insets insets = tip.getInsets(); - FontMetrics fm = tip.getToolkit().getFontMetrics(tip.getFont()); - SwingUtilities.layoutCompoundLabel(tip, fm, tip.getTipText(), null, + fm = g.getFontMetrics(tip.getFont()); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, @@ -192,11 +148,9 @@ public class BasicToolTipUI extends ToolTipUI */ protected void installDefaults(JComponent c) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - c.setBackground(defaults.getColor("ToolTip.background")); - c.setForeground(defaults.getColor("ToolTip.foreground")); - c.setFont(defaults.getFont("ToolTip.font")); - c.setBorder(defaultBorder); + LookAndFeel.installColorsAndFont(c, "ToolTip.background", + "ToolTip.foreground", "ToolTip.font"); + LookAndFeel.installBorder(c, "ToolTip.border"); } /** @@ -206,6 +160,7 @@ public class BasicToolTipUI extends ToolTipUI */ protected void installListeners(JComponent c) { + // TODO: Implement this properly. } /** @@ -231,6 +186,7 @@ public class BasicToolTipUI extends ToolTipUI JToolTip tip = (JToolTip) c; String text = tip.getTipText(); + Toolkit t = tip.getToolkit(); if (text == null) return; @@ -238,19 +194,19 @@ public class BasicToolTipUI extends ToolTipUI vr = SwingUtilities.calculateInnerArea(tip, vr); Rectangle ir = new Rectangle(); Rectangle tr = new Rectangle(); - FontMetrics fm = tip.getToolkit().getFontMetrics(tip.getFont()); - SwingUtilities.layoutCompoundLabel(tip, fm, tip.getTipText(), null, + FontMetrics fm = t.getFontMetrics(tip.getFont()); + int ascent = fm.getAscent(); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0); - Color saved = g.getColor(); g.setColor(Color.BLACK); - g.drawString(text, vr.x, vr.y + fm.getAscent()); + g.drawString(text, vr.x, vr.y + ascent); - g.setColor(saved); + g.setColor(saved); } /** @@ -273,6 +229,7 @@ public class BasicToolTipUI extends ToolTipUI */ protected void uninstallListeners(JComponent c) { + // TODO: Implement this properly. } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index 6f714a39cb2..e967cd424fc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java @@ -44,6 +44,7 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -62,6 +63,7 @@ import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Enumeration; import java.util.Hashtable; import javax.swing.AbstractAction; @@ -76,6 +78,7 @@ import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIDefaults; @@ -89,6 +92,7 @@ import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TreeUI; @@ -96,7 +100,6 @@ import javax.swing.text.Caret; import javax.swing.tree.AbstractLayoutCache; import javax.swing.tree.DefaultTreeCellEditor; import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.ExpandVetoException; import javax.swing.tree.FixedHeightLayoutCache; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; @@ -110,11 +113,11 @@ import javax.swing.tree.TreeSelectionModel; * the Basic look and feel. * * @see javax.swing.JTree - * @author Sascha Brawer (brawer@dandelis.ch) + * * @author Lillian Angel (langel@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BasicTreeUI - extends TreeUI +public class BasicTreeUI extends TreeUI { /** Collapse Icon for the tree. */ protected transient Icon collapsedIcon; @@ -136,9 +139,6 @@ public class BasicTreeUI */ protected int totalChildIndent; - /** Minimum preferred size. */ - protected Dimension preferredMinsize; - /** Index of the row that was last selected. */ protected int lastSelectedRow; @@ -174,6 +174,9 @@ public class BasicTreeUI /** Size needed to completely display all the nodes. */ protected Dimension preferredSize; + + /** Minimum size needed to completely display all the nodes. */ + protected Dimension preferredMinSize; /** Is the preferredSize valid? */ protected boolean validCachedPreferredSize; @@ -223,38 +226,38 @@ public class BasicTreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - + /** The action listener for the editor's Timer. */ - private Timer editorTimer = new EditorUpdateTimer(); + Timer editorTimer = new EditorUpdateTimer(); /** The new value of the node after editing. */ - private Object newVal; + Object newVal; /** The action bound to KeyStrokes. */ - private TreeAction action; + TreeAction action; /** Boolean to keep track of editing. */ - private boolean isEditing; + boolean isEditing; + + /** The bounds of the current cell. */ + Rectangle bounds; + + /** The current path of the visible nodes in the tree. */ + TreePath currentVisiblePath; + + /** The gap between the icon and text. */ + int gap = 4; /** Listeners */ private PropertyChangeListener propertyChangeListener; - private FocusListener focusListener; - private TreeSelectionListener treeSelectionListener; - - private MouseInputListener mouseInputListener; - + private MouseListener mouseListener; private KeyListener keyListener; - private PropertyChangeListener selectionModelPropertyChangeListener; - private ComponentListener componentListener; - - private CellEditorListener cellEditorListener; - + CellEditorListener cellEditorListener; private TreeExpansionListener treeExpansionListener; - private TreeModelListener treeModelListener; /** @@ -262,6 +265,7 @@ public class BasicTreeUI */ public BasicTreeUI() { + validCachedPreferredSize = false; drawingCache = new Hashtable(); nodeDimensions = createNodeDimensions(); configureLayoutCache(); @@ -269,7 +273,7 @@ public class BasicTreeUI propertyChangeListener = createPropertyChangeListener(); focusListener = createFocusListener(); treeSelectionListener = createTreeSelectionListener(); - mouseInputListener = new MouseInputHandler(null, null, null); + mouseListener = createMouseListener(); keyListener = createKeyListener(); selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener(); componentListener = createComponentListener(); @@ -331,7 +335,7 @@ public class BasicTreeUI * * @return the indent value for the left child. */ - public int getLeftChildIndent(int newAmount) + public int getLeftChildIndent() { return leftChildIndent; } @@ -456,7 +460,6 @@ public class BasicTreeUI protected void setCellRenderer(TreeCellRenderer tcr) { currentCellRenderer = tcr; - tree.setCellRenderer(tcr); updateRenderer(); } @@ -625,14 +628,13 @@ public class BasicTreeUI { Object cell = path.getLastPathComponent(); - TreeModel mod = tree.getModel(); - if (mod != null) + if (treeModel != null) { - Object root = mod.getRoot(); + Object root = treeModel.getRoot(); + if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root))) root = getNextNode(root); - - Point loc = getCellLocation(0, 0, tree, mod, cell, root); + Point loc = getCellLocation(0, 0, tree, treeModel, cell, root); return getCellBounds(loc.x, loc.y, cell); } } @@ -650,21 +652,11 @@ public class BasicTreeUI */ public TreePath getPathForRow(JTree tree, int row) { - TreeModel mod = tree.getModel(); - if (mod != null) + if (treeModel != null && currentVisiblePath != null) { - Object node = mod.getRoot(); - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath(getPathToRoot(node, 0)))) - node = getNextNode(node); - - for (int i = 0; i < row; i++) - node = getNextVisibleNode(node); - - if (node == null) - return null; - - return new TreePath(getPathToRoot(node, 0)); + Object[] nodes = currentVisiblePath.getPath(); + if (row < nodes.length) + return new TreePath(getPathToRoot(nodes[row], 0)); } return null; } @@ -683,17 +675,20 @@ public class BasicTreeUI */ public int getRowForPath(JTree tree, TreePath path) { - int row = path.getPathCount(); - if (tree.isVisible(path)) - return row; - - path = path.getParentPath(); - while (row > 0 && !tree.isVisible(path)) + int row = 0; + Object dest = path.getLastPathComponent(); + int rowCount = getRowCount(tree); + if (currentVisiblePath != null) { - path = path.getParentPath(); - row--; + Object[] nodes = currentVisiblePath.getPath(); + while (row < rowCount) + { + if (dest.equals(nodes[row])) + return row; + row++; + } } - return row; + return -1; } /** @@ -705,22 +700,10 @@ public class BasicTreeUI */ public int getRowCount(JTree tree) { - TreeModel mod = tree.getModel(); - int count = 0; - if (mod != null) - { - Object node = mod.getRoot(); - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath((getPathToRoot(node, 0))))) - node = getNextNode(node); - - while (node != null) - { - count++; - node = getNextVisibleNode(node); - } - } - return count; + updateCurrentVisiblePath(); + if (currentVisiblePath != null) + return currentVisiblePath.getPathCount(); + return 0; } /** @@ -739,9 +722,6 @@ public class BasicTreeUI */ public TreePath getClosestPathForLocation(JTree tree, int x, int y) { - // FIXME: what if root is hidden? should not depend on (0,0) - // should start counting rows from where root is. - int row = Math.round(y / getRowHeight()); TreePath path = getPathForRow(tree, row); @@ -828,7 +808,7 @@ public class BasicTreeUI */ protected void prepareForUIInstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -837,7 +817,7 @@ public class BasicTreeUI */ protected void completeUIInstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -846,7 +826,7 @@ public class BasicTreeUI */ protected void completeUIUninstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -854,7 +834,10 @@ public class BasicTreeUI */ protected void installComponents() { - // FIXME: not implemented + currentCellRenderer = createDefaultCellRenderer(); + rendererPane = createCellRendererPane(); + createdRenderer = true; + setCellRenderer(currentCellRenderer); } /** @@ -865,8 +848,7 @@ public class BasicTreeUI */ protected AbstractLayoutCache.NodeDimensions createNodeDimensions() { - // FIXME: not implemented - return null; + return new NodeDimensionsHandler(); } /** @@ -1035,7 +1017,7 @@ public class BasicTreeUI tree.removePropertyChangeListener(propertyChangeListener); tree.removeFocusListener(focusListener); tree.removeTreeSelectionListener(treeSelectionListener); - tree.removeMouseListener(mouseInputListener); + tree.removeMouseListener(mouseListener); tree.removeKeyListener(keyListener); tree.removePropertyChangeListener(selectionModelPropertyChangeListener); tree.removeComponentListener(componentListener); @@ -1044,9 +1026,8 @@ public class BasicTreeUI TreeCellEditor tce = tree.getCellEditor(); if (tce != null) tce.removeCellEditorListener(cellEditorListener); - TreeModel tm = tree.getModel(); - if (tm != null) - tm.removeTreeModelListener(treeModelListener); + if (treeModel != null) + treeModel.removeTreeModelListener(treeModelListener); } /** @@ -1054,6 +1035,7 @@ public class BasicTreeUI */ protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } /** @@ -1061,7 +1043,10 @@ public class BasicTreeUI */ protected void uninstallComponents() { - // FIXME: not implemented + currentCellRenderer = null; + rendererPane = null; + createdRenderer = false; + setCellRenderer(currentCellRenderer); } /** @@ -1072,8 +1057,7 @@ public class BasicTreeUI */ protected int getVerticalLegBuffer() { - // FIXME: not implemented - return 0; + return getRowHeight() / 2; } /** @@ -1085,17 +1069,17 @@ public class BasicTreeUI */ protected int getHorizontalLegBuffer() { - // FIXME: not implemented - return 0; + return rightChildIndent / 2; } /** * Make all the nodes that are expanded in JTree expanded in LayoutCache. This - * invokes update ExpandedDescendants with the root path. + * invokes updateExpandedDescendants with the root path. */ protected void updateLayoutCacheExpandedNodes() { - // FIXME: not implemented + if (treeModel != null) + updateExpandedDescendants(new TreePath(treeModel.getRoot())); } /** @@ -1108,7 +1092,9 @@ public class BasicTreeUI */ protected void updateExpandedDescendants(TreePath path) { - // FIXME: not implemented + Enumeration expanded = tree.getExpandedDescendants(path); + while (expanded.hasMoreElements()) + treeState.setExpandedState(((TreePath) expanded.nextElement()), true); } /** @@ -1128,7 +1114,7 @@ public class BasicTreeUI */ protected void updateDepthOffset() { - // FIXME: not implemented + depthOffset += getVerticalLegBuffer(); } /** @@ -1148,7 +1134,15 @@ public class BasicTreeUI */ protected void updateRenderer() { - // FIXME: not implemented + if (tree != null) + { + if(tree.getCellRenderer() == null) + { + if(currentCellRenderer == null) + currentCellRenderer = createDefaultCellRenderer(); + tree.setCellRenderer(currentCellRenderer); + } + } } /** @@ -1166,19 +1160,36 @@ public class BasicTreeUI */ protected void updateSize() { - // FIXME: not implemented + preferredSize = null; + updateCachedPreferredSize(); + tree.treeDidChange(); } /** * Updates the <code>preferredSize</code> instance variable, which is - * returned from <code>getPreferredSize()</code>. For left to right - * orientations, the size is determined from the current AbstractLayoutCache. - * For RTL orientations, the preferred size becomes the width minus the - * minimum x position. + * returned from <code>getPreferredSize()</code>. */ protected void updateCachedPreferredSize() { - // FIXME: not implemented + int maxWidth = 0; + boolean isLeaf = false; + if (currentVisiblePath != null) + { + Object[] path = currentVisiblePath.getPath(); + for (int i = 0; i < path.length; i++) + { + TreePath curr = new TreePath(getPathToRoot(path[i], 0)); + Rectangle bounds = getPathBounds(tree, curr); + if (treeModel != null) + isLeaf = treeModel.isLeaf(path[i]); + if (!isLeaf && hasControlIcons()) + bounds.width += getCurrentControlIcon(curr).getIconWidth(); + maxWidth = Math.max(maxWidth, bounds.x + bounds.width); + } + preferredSize = new Dimension(maxWidth, (getRowHeight() * path.length)); + } + else preferredSize = new Dimension(0, 0); + validCachedPreferredSize = true; } /** @@ -1189,7 +1200,9 @@ public class BasicTreeUI */ protected void pathWasExpanded(TreePath path) { - // FIXME: not implemented + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** @@ -1197,28 +1210,28 @@ public class BasicTreeUI */ protected void pathWasCollapsed(TreePath path) { - // FIXME: not implemented + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** * Install all defaults for the tree. - * - * @param tree - * is the JTree to install defaults for */ - protected void installDefaults(JTree tree) + protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - tree.setFont(defaults.getFont("Tree.font")); - tree.setForeground(defaults.getColor("Tree.foreground")); - tree.setBackground(defaults.getColor("Tree.background")); + LookAndFeel.installColorsAndFont(tree, "Tree.background", + "Tree.foreground", "Tree.font"); tree.setOpaque(true); - rightChildIndent = defaults.getInt("Tree.rightChildIndent"); - leftChildIndent = defaults.getInt("Tree.leftChildIndent"); - setRowHeight(defaults.getInt("Tree.rowHeight")); + rightChildIndent = UIManager.getInt("Tree.rightChildIndent"); + leftChildIndent = UIManager.getInt("Tree.leftChildIndent"); + setRowHeight(UIManager.getInt("Tree.rowHeight")); + tree.setRowHeight(UIManager.getInt("Tree.rowHeight")); tree.requestFocusInWindow(false); + tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand")); + setExpandedIcon(UIManager.getIcon("Tree.expandedIcon")); + setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon")); } /** @@ -1229,7 +1242,7 @@ public class BasicTreeUI UIDefaults defaults = UIManager.getLookAndFeelDefaults(); InputMap focusInputMap = (InputMap) defaults.get("Tree.focusInputMap"); InputMapUIResource parentInputMap = new InputMapUIResource(); - ActionMap parentActionMap = new ActionMap(); + ActionMap parentActionMap = new ActionMapUIResource(); action = new TreeAction(); Object keys[] = focusInputMap.allKeys(); @@ -1308,7 +1321,7 @@ public class BasicTreeUI tree.addPropertyChangeListener(propertyChangeListener); tree.addFocusListener(focusListener); tree.addTreeSelectionListener(treeSelectionListener); - tree.addMouseListener(mouseInputListener); + tree.addMouseListener(mouseListener); tree.addKeyListener(keyListener); tree.addPropertyChangeListener(selectionModelPropertyChangeListener); tree.addComponentListener(componentListener); @@ -1325,37 +1338,36 @@ public class BasicTreeUI */ public void installUI(JComponent c) { - super.installUI(c); - installDefaults((JTree) c); tree = (JTree) c; + prepareForUIInstall(); + super.installUI(c); + installDefaults(); - currentCellRenderer = createDefaultCellRenderer(); - rendererPane = createCellRendererPane(); - createdRenderer = true; - + installComponents(); + installKeyboardActions(); + installListeners(); + setCellEditor(createDefaultCellEditor()); createdCellEditor = true; isEditing = false; - + TreeModel mod = tree.getModel(); setModel(mod); - tree.setRootVisible(true); if (mod != null) - tree.expandPath(new TreePath(mod.getRoot())); + { + TreePath path = new TreePath(mod.getRoot()); + if (!tree.isExpanded(path)) + toggleExpandState(path); + } treeSelectionModel = tree.getSelectionModel(); - installKeyboardActions(); - installListeners(); completeUIInstall(); } /** * Uninstall the defaults for the tree - * - * @param tree - * to uninstall defaults for */ - protected void uninstallDefaults(JTree tree) + protected void uninstallDefaults() { tree.setFont(null); tree.setForeground(null); @@ -1370,10 +1382,12 @@ public class BasicTreeUI */ public void uninstallUI(JComponent c) { - uninstallDefaults((JTree) c); + prepareForUIUninstall(); + uninstallDefaults(); uninstallKeyboardActions(); uninstallListeners(); tree = null; + uninstallComponents(); completeUIUninstall(); } @@ -1393,20 +1407,16 @@ public class BasicTreeUI public void paint(Graphics g, JComponent c) { JTree tree = (JTree) c; - - TreeModel mod = tree.getModel(); - - if (mod != null) + if (currentVisiblePath == null) + updateCurrentVisiblePath(); + + if (currentVisiblePath != null && treeModel != null) { - Object root = mod.getRoot(); - - if (!tree.isRootVisible()) - tree.expandPath(new TreePath(root)); - - paintRecursive(g, 0, 0, 0, 0, tree, mod, root); - + Object root = treeModel.getRoot(); + paintRecursive(g, 0, 0, 0, tree, treeModel, root); + if (hasControlIcons()) - paintControlIcons(g, 0, 0, 0, 0, tree, mod, root); + paintControlIcons(g, 0, 0, 0, tree, treeModel, root); } } @@ -1420,7 +1430,19 @@ public class BasicTreeUI */ protected void ensureRowsAreVisible(int beginRow, int endRow) { - // FIXME: not implemented + if (beginRow < endRow) + { + int temp = endRow; + endRow = beginRow; + beginRow = temp; + } + + for (int i = beginRow; i < endRow; i++) + { + TreePath path = getPathForRow(tree, i); + if (!tree.isVisible(path)) + tree.makeVisible(path); + } } /** @@ -1431,7 +1453,7 @@ public class BasicTreeUI */ public void setPreferredMinSize(Dimension newSize) { - // FIXME: not implemented + preferredMinSize = newSize; } /** @@ -1441,8 +1463,7 @@ public class BasicTreeUI */ public Dimension getPreferredMinSize() { - // FIXME: not implemented - return null; + return preferredMinSize; } /** @@ -1472,28 +1493,10 @@ public class BasicTreeUI */ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy) { - // FIXME: checkConsistancy not implemented, c not used - TreeModel model = tree.getModel(); - int maxWidth = 0; - int count = 0; - if (model != null) - { - Object node = model.getRoot(); - if (node != null) - { - maxWidth = (int) (getCellBounds(0, 0, node).getWidth()); - while (node != null) - { - count++; - Object nextNode = getNextVisibleNode(node); - if (nextNode != null) - maxWidth = Math.max(maxWidth, - (int) (getCellBounds(0, 0, nextNode).getWidth())); - node = nextNode; - } - } - } - return new Dimension(maxWidth, (getRowHeight() * count)); + // FIXME: checkConsistancy not implemented, c not used + if(!validCachedPreferredSize) + updateCachedPreferredSize(); + return preferredSize; } /** @@ -1506,8 +1509,10 @@ public class BasicTreeUI */ public Dimension getMinimumSize(JComponent c) { - // FIXME: not implemented - return getPreferredSize(c); + Dimension min = getPreferredMinSize(); + if (min == null) + return new Dimension(); + return min; } /** @@ -1520,8 +1525,9 @@ public class BasicTreeUI */ public Dimension getMaximumSize(JComponent c) { - // FIXME: not implemented - return getPreferredSize(c); + if (c instanceof JTree) + return ((JTree) c).getPreferredSize(); + return new Dimension(); } /** @@ -1565,7 +1571,7 @@ public class BasicTreeUI } if (messageTree) - tree.getModel().valueForPathChanged(tree.getLeadSelectionPath(), newVal); + treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal); } /** @@ -1584,7 +1590,7 @@ public class BasicTreeUI int y; if (event == null) { - Rectangle bounds = getPathBounds(tree, path); + bounds = getPathBounds(tree, path); x = bounds.x; y = bounds.y; } @@ -1600,6 +1606,7 @@ public class BasicTreeUI { editingPath = path; editingRow = tree.getRowForPath(editingPath); + Object val = editingPath.getLastPathComponent(); cellEditor.addCellEditorListener(cellEditorListener); stopEditingInCompleteEditing = false; @@ -1613,6 +1620,8 @@ public class BasicTreeUI editingComponent.getParent().validate(); tree.add(editingComponent.getParent()); editingComponent.getParent().validate(); + validCachedPreferredSize = false; + tree.revalidate(); ((JTextField) editingComponent).requestFocusInWindow(false); editorTimer.start(); return true; @@ -1634,7 +1643,8 @@ public class BasicTreeUI protected void checkForClickInExpandControl(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented + if (isLocationInExpandControl(path, mouseX, mouseY)) + toggleExpandState(path); } /** @@ -1655,8 +1665,18 @@ public class BasicTreeUI protected boolean isLocationInExpandControl(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented - return false; + boolean cntlClick = false; + int row = getRowForPath(tree, path); + + if (!isLeaf(row)) + { + bounds = getPathBounds(tree, path); + + if (hasControlIcons() && (mouseX < bounds.x) + && (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap))) + cntlClick = true; + } + return cntlClick; } /** @@ -1672,7 +1692,7 @@ public class BasicTreeUI */ protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented + toggleExpandState(path); } /** @@ -1686,7 +1706,11 @@ public class BasicTreeUI */ protected void toggleExpandState(TreePath path) { - // FIXME: not implemented + if (tree.isExpanded(path)) + tree.collapsePath(path); + else + tree.expandPath(path); + updateCurrentVisiblePath(); } /** @@ -1700,8 +1724,8 @@ public class BasicTreeUI */ protected boolean isToggleSelectionEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.SINGLE_TREE_SELECTION); } /** @@ -1715,8 +1739,8 @@ public class BasicTreeUI */ protected boolean isMultiSelectEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); } /** @@ -1731,8 +1755,7 @@ public class BasicTreeUI */ protected boolean isToggleEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return true; } /** @@ -1749,7 +1772,29 @@ public class BasicTreeUI */ protected void selectPathForEvent(TreePath path, MouseEvent event) { - // FIXME: not implemented + if (isToggleSelectionEvent(event)) + { + if (tree.isPathSelected(path)) + tree.removeSelectionPath(path); + else + { + tree.addSelectionPath(path); + tree.setAnchorSelectionPath(path); + } + } + else if (isMultiSelectEvent(event)) + { + TreePath anchor = tree.getAnchorSelectionPath(); + if (anchor != null) + { + int aRow = getRowForPath(tree, anchor); + tree.addSelectionInterval(aRow, getRowForPath(tree, path)); + } + else + tree.addSelectionPath(path); + } + else + tree.addSelectionPath(path); } /** @@ -1766,7 +1811,7 @@ public class BasicTreeUI return true; Object node = pathForRow.getLastPathComponent(); - return tree.getModel().isLeaf(node); + return treeModel.isLeaf(node); } /** @@ -1800,9 +1845,9 @@ public class BasicTreeUI (new TreeTraverseAction(0, "")).actionPerformed(e); else if (e.getActionCommand().equals("selectAll")) { - TreePath[] paths = new TreePath[tree.getRowCount()]; + TreePath[] paths = new TreePath[tree.getVisibleRowCount()]; - Object curr = getNextVisibleNode(tree.getModel().getRoot()); + Object curr = getNextVisibleNode(treeModel.getRoot()); int i = 0; while (curr != null && i < paths.length) { @@ -1822,13 +1867,8 @@ public class BasicTreeUI { Object last = lead.getLastPathComponent(); TreePath path = new TreePath(getPathToRoot(last, 0)); - if (!tree.getModel().isLeaf(last)) - { - if (tree.isExpanded(path)) - tree.collapsePath(path); - else - tree.expandPath(path); - } + if (!treeModel.isLeaf(last)) + toggleExpandState(path); } } else if (e.getActionCommand().equals("clearSelection")) @@ -1920,8 +1960,7 @@ public class BasicTreeUI /** * Updates the preferred size when scrolling, if necessary. */ - public class ComponentHandler - extends ComponentAdapter + public class ComponentHandler extends ComponentAdapter implements ActionListener { /** @@ -1937,6 +1976,7 @@ public class BasicTreeUI */ public ComponentHandler() { + // Nothing to do here. } /** @@ -1947,6 +1987,7 @@ public class BasicTreeUI */ public void componentMoved(ComponentEvent e) { + // TODO: What should be done here, if anything? } /** @@ -1955,6 +1996,7 @@ public class BasicTreeUI */ protected void startTimer() { + // TODO: Implement this properly. } /** @@ -1976,21 +2018,22 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent ae) { + // TODO: Implement this properly. } - }// ComponentHandler + } /** * Listener responsible for getting cell editing events and updating the tree * accordingly. */ - public class CellEditorHandler - implements CellEditorListener + public class CellEditorHandler implements CellEditorListener { /** * Constructor */ public CellEditorHandler() { + // Nothing to do here. } /** @@ -2023,6 +2066,9 @@ public class BasicTreeUI isEditing = false; tree.requestFocusInWindow(false); editorTimer.stop(); + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** @@ -2051,6 +2097,8 @@ public class BasicTreeUI tree.requestFocusInWindow(false); editorTimer.stop(); isEditing = false; + validCachedPreferredSize = false; + tree.revalidate(); tree.repaint(); } }// CellEditorHandler @@ -2066,6 +2114,7 @@ public class BasicTreeUI */ public FocusHandler() { + // Nothing to do here. } /** @@ -2077,6 +2126,7 @@ public class BasicTreeUI */ public void focusGained(FocusEvent e) { + // TODO: Implement this properly. } /** @@ -2088,8 +2138,9 @@ public class BasicTreeUI */ public void focusLost(FocusEvent e) { + // TODO: Implement this properly. } - }// FocusHandler + } /** * This is used to get multiple key down events to appropriately genereate @@ -2109,6 +2160,7 @@ public class BasicTreeUI */ public KeyHandler() { + // Nothing to do here. } /** @@ -2122,6 +2174,7 @@ public class BasicTreeUI */ public void keyTyped(KeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2132,6 +2185,7 @@ public class BasicTreeUI */ public void keyPressed(KeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2142,22 +2196,22 @@ public class BasicTreeUI */ public void keyReleased(KeyEvent e) { + // TODO: What should be done here, if anything? } - }// KeyHandler + } /** * MouseListener is responsible for updating the selection based on mouse * events. */ - public class MouseHandler - extends MouseAdapter - implements MouseMotionListener + public class MouseHandler extends MouseAdapter implements MouseMotionListener { /** * Constructor */ public MouseHandler() { + // Nothing to do here. } /** @@ -2168,6 +2222,57 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { + Point click = e.getPoint(); + TreePath path = getClosestPathForLocation(tree, click.x, click.y); + + if (path != null) + { + bounds = getPathBounds(tree, path); + int row = getRowForPath(tree, path); + boolean cntlClick = isLocationInExpandControl(path, click.x, click.y); + + boolean isLeaf = isLeaf(row); + + TreeCellRenderer tcr = getCellRenderer(); + Icon icon; + if (isLeaf) + icon = UIManager.getIcon("Tree.leafIcon"); + else if (tree.isExpanded(path)) + icon = UIManager.getIcon("Tree.openIcon"); + else + icon = UIManager.getIcon("Tree.closedIcon"); + + if (tcr instanceof DefaultTreeCellRenderer) + { + Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon(); + if (tmp != null) + icon = tmp; + } + + // add gap*2 for the space before and after the text + if (icon != null) + bounds.width += icon.getIconWidth() + gap*2; + + boolean inBounds = bounds.contains(click.x, click.y); + if ((inBounds || cntlClick) && tree.isVisible(path)) + { + if (inBounds) + { + selectPath(tree, path); + if (e.getClickCount() == 2 && !isLeaf(row)) + toggleExpandState(path); + } + + if (cntlClick) + { + handleExpandControlClick(path, click.x, click.y); + if (cellEditor != null) + cellEditor.cancelCellEditing(); + } + else if (tree.isEditable()) + startEditing(path, e); + } + } } /** @@ -2181,6 +2286,7 @@ public class BasicTreeUI */ public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2192,6 +2298,7 @@ public class BasicTreeUI */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2202,16 +2309,16 @@ public class BasicTreeUI */ public void mouseReleased(MouseEvent e) { + // TODO: What should be done here, if anything? } - }// MouseHandler + } /** * MouseInputHandler handles passing all mouse events, including mouse motion * events, until the mouse is released to the destination it is constructed * with. */ - public class MouseInputHandler - implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { /** Source that events are coming from */ protected Component source; @@ -2232,6 +2339,8 @@ public class BasicTreeUI public MouseInputHandler(Component source, Component destination, MouseEvent e) { + this.source = source; + this.destination = destination; } /** @@ -2243,6 +2352,7 @@ public class BasicTreeUI */ public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2253,42 +2363,7 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { - Point click = e.getPoint(); - int row = Math.round(click.y / getRowHeight()); - TreePath path = getClosestPathForLocation(tree, click.x, click.y); - - if (path != null) - { - boolean inBounds = false; - boolean cntlClick = false; - Rectangle bounds = getPathBounds(tree, path); - - bounds.x -= rightChildIndent - 4; - bounds.width += rightChildIndent + 4; - - if (bounds.contains(click.x, click.y)) - inBounds = true; - else if (hasControlIcons() - && (click.x < (bounds.x - rightChildIndent + 5) && - click.x > (bounds.x - rightChildIndent - 5))) - cntlClick = true; - - if ((inBounds || cntlClick) && tree.isVisible(path)) - { - selectPath(tree, path); - - if ((e.getClickCount() == 2 || cntlClick) && !isLeaf(row)) - { - if (tree.isExpanded(path)) - tree.collapsePath(path); - else - tree.expandPath(path); - } - - if (!cntlClick && tree.isEditable()) - startEditing(path, e); - } - } + // TODO: What should be done here, if anything? } /** @@ -2299,6 +2374,7 @@ public class BasicTreeUI */ public void mouseReleased(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2309,6 +2385,7 @@ public class BasicTreeUI */ public void mouseEntered(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2319,6 +2396,7 @@ public class BasicTreeUI */ public void mouseExited(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2332,6 +2410,7 @@ public class BasicTreeUI */ public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2343,6 +2422,7 @@ public class BasicTreeUI */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2350,8 +2430,9 @@ public class BasicTreeUI */ protected void removeFromSource() { + // TODO: Implement this properly. } - }// MouseInputHandler + } /** * Class responsible for getting size of node, method is forwarded to @@ -2366,6 +2447,7 @@ public class BasicTreeUI */ public NodeDimensionsHandler() { + // Nothing to do here. } /** @@ -2413,6 +2495,7 @@ public class BasicTreeUI */ public PropertyChangeHandler() { + // Nothing to do here. } /** @@ -2424,8 +2507,15 @@ public class BasicTreeUI */ public void propertyChange(PropertyChangeEvent event) { + if ((event.getPropertyName()).equals("rootVisible")) + { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); + tree.repaint(); + } } - }// PropertyChangeHandler + } /** * Listener on the TreeSelectionModel, resets the row selection if any of the @@ -2440,6 +2530,7 @@ public class BasicTreeUI */ public SelectionModelPropertyChangeHandler() { + // Nothing to do here. } /** @@ -2451,8 +2542,9 @@ public class BasicTreeUI */ public void propertyChange(PropertyChangeEvent event) { + // TODO: What should be done here, if anything? } - }// SelectionModelPropertyChangeHandler + } /** * ActionListener that invokes cancelEditing when action performed. @@ -2464,8 +2556,9 @@ public class BasicTreeUI /** * Constructor */ - public TreeCancelEditingAction() + public TreeCancelEditingAction(String name) { + // TODO: Implement this properly. } /** @@ -2476,6 +2569,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2485,9 +2579,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly. return false; } - }// TreeCancelEditingAction + } /** * Updates the TreeState in response to nodes expanding/collapsing. @@ -2501,6 +2596,7 @@ public class BasicTreeUI */ public TreeExpansionHandler() { + // Nothing to do here. } /** @@ -2511,6 +2607,9 @@ public class BasicTreeUI */ public void treeExpanded(TreeExpansionEvent event) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2522,6 +2621,9 @@ public class BasicTreeUI */ public void treeCollapsed(TreeExpansionEvent event) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } }// TreeExpansionHandler @@ -2547,6 +2649,7 @@ public class BasicTreeUI */ public TreeHomeAction(int direction, String name) { + // TODO: Implement this properly } /** @@ -2557,6 +2660,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly } /** @@ -2566,9 +2670,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - }// TreeHomeAction + } /** * TreeIncrementAction is used to handle up/down actions. Selection is moved @@ -2591,6 +2696,7 @@ public class BasicTreeUI */ public TreeIncrementAction(int direction, String name) { + // TODO: Implement this properly } /** @@ -2606,11 +2712,11 @@ public class BasicTreeUI if (e.getActionCommand().equals("selectPreviousChangeLead")) { Object prev = getPreviousVisibleNode(last); - + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, new TreePath(getPathToRoot(prev, 0))); + selectPath(tree, newPath); tree.setLeadSelectionPath(newPath); } } @@ -2627,15 +2733,17 @@ public class BasicTreeUI else if (e.getActionCommand().equals("selectPrevious")) { Object prev = getPreviousVisibleNode(last); + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, new TreePath(getPathToRoot(prev, 0))); + selectPath(tree, newPath); } } else if (e.getActionCommand().equals("selectNext")) { Object next = getNextVisibleNode(last); + if (next != null) { TreePath newPath = new TreePath(getPathToRoot(next, 0)); @@ -2671,21 +2779,22 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - }// TreeIncrementAction + } /** * Forwards all TreeModel events to the TreeState. */ - public class TreeModelHandler - implements TreeModelListener + public class TreeModelHandler implements TreeModelListener { /** * Constructor */ public TreeModelHandler() { + // Nothing to do here. } /** @@ -2703,6 +2812,9 @@ public class BasicTreeUI */ public void treeNodesChanged(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2716,6 +2828,9 @@ public class BasicTreeUI */ public void treeNodesInserted(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2732,6 +2847,9 @@ public class BasicTreeUI */ public void treeNodesRemoved(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2747,6 +2865,12 @@ public class BasicTreeUI */ public void treeStructureChanged(TreeModelEvent e) { + if (e.getPath().length == 1 + && !e.getPath()[0].equals(treeModel.getRoot())) + tree.expandPath(new TreePath(treeModel.getRoot())); + updateCurrentVisiblePath(); + validCachedPreferredSize = false; + tree.revalidate(); tree.repaint(); } }// TreeModelHandler @@ -2754,8 +2878,7 @@ public class BasicTreeUI /** * TreePageAction handles page up and page down events. */ - public class TreePageAction - extends AbstractAction + public class TreePageAction extends AbstractAction { /** Specifies the direction to adjust the selection by. */ protected int direction; @@ -2770,6 +2893,7 @@ public class BasicTreeUI */ public TreePageAction(int direction, String name) { + this.direction = direction; } /** @@ -2780,6 +2904,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2797,14 +2922,14 @@ public class BasicTreeUI * Listens for changes in the selection model and updates the display * accordingly. */ - public class TreeSelectionHandler - implements TreeSelectionListener + public class TreeSelectionHandler implements TreeSelectionListener { /** * Constructor */ public TreeSelectionHandler() { + // Nothing to do here. } /** @@ -2824,8 +2949,7 @@ public class BasicTreeUI /** * For the first selected row expandedness will be toggled. */ - public class TreeToggleAction - extends AbstractAction + public class TreeToggleAction extends AbstractAction { /** * Constructor @@ -2835,6 +2959,7 @@ public class BasicTreeUI */ public TreeToggleAction(String name) { + // Nothing to do here. } /** @@ -2845,6 +2970,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2862,8 +2988,7 @@ public class BasicTreeUI * TreeTraverseAction is the action used for left/right keys. Will toggle the * expandedness of a node, as well as potentially incrementing the selection. */ - public class TreeTraverseAction - extends AbstractAction + public class TreeTraverseAction extends AbstractAction { /** * Determines direction to traverse, 1 means expand, -1 means collapse. @@ -2880,6 +3005,7 @@ public class BasicTreeUI */ public TreeTraverseAction(int direction, String name) { + this.direction = direction; } /** @@ -2890,16 +3016,15 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { - TreeModel mod = tree.getModel(); Object last = tree.getLeadSelectionPath().getLastPathComponent(); if (e.getActionCommand().equals("selectParent")) { TreePath path = new TreePath(getPathToRoot(last, 0)); - Object p = getParent(mod.getRoot(), last); + Object p = getParent(treeModel.getRoot(), last); - if (!mod.isLeaf(last) && tree.isExpanded(path)) - tree.collapsePath(path); + if (!treeModel.isLeaf(last)) + toggleExpandState(path); else if (p != null) selectPath(tree, new TreePath(getPathToRoot(p, 0))); } @@ -2907,8 +3032,8 @@ public class BasicTreeUI { TreePath path = new TreePath(getPathToRoot(last, 0)); - if (!mod.isLeaf(last) && tree.isCollapsed(path)) - tree.expandPath(path); + if (!treeModel.isLeaf(last)) + toggleExpandState(path); else { Object next = getNextVisibleNode(last); @@ -2926,9 +3051,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - } // TreeTraverseAction + } /** * Returns the cell bounds for painting selected cells Package private for use @@ -2951,8 +3077,7 @@ public class BasicTreeUI FontMetrics fm = tree.getToolkit().getFontMetrics(f); if (s != null) - return new Rectangle(x, y, - SwingUtilities.computeStringWidth(fm, s) + 4, + return new Rectangle(x, y, SwingUtilities.computeStringWidth(fm, s), fm.getHeight()); } return new Rectangle(x, y, 0, 0); @@ -2982,80 +3107,19 @@ public class BasicTreeUI int rowHeight = getRowHeight(); if (startNode == null || startNode.equals(node)) { - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath(mod.getRoot()))) - return new Point(x + ((getLevel(node)) * rightChildIndent), y); - - return new Point(x + ((getLevel(node) + 1) * rightChildIndent), y); - } - - if (!mod.isLeaf(startNode) - && tree.isExpanded(new TreePath(getPathToRoot(startNode, 0))) - && !mod.isLeaf(startNode) && mod.getChildCount(startNode) > 0) - { - Object child = mod.getChild(startNode, 0); - if (child != null) - return getCellLocation(x, y + rowHeight, tree, mod, node, child); + int level = getLevel(node); + if (level == 0) + return new Point(x, y); + if (!tree.isRootVisible() && + tree.isExpanded(new TreePath(mod.getRoot()))) + return new Point(x + ((level - 1) * rightChildIndent), y); + return new Point(x + (level * rightChildIndent), y); } - return getCellLocation(x, y + rowHeight, tree, mod, node, getNextVisibleNode(startNode)); } /** - * Paints a node in the tree Package private for use in inner classes. - * - * @param g - * the Graphics context in which to paint - * @param x - * the x location of the node - * @param y - * the y location of the node - * @param tree - * the tree to draw on - * @param node - * the object to draw - */ - void paintNode(Graphics g, int x, int y, JTree tree, Object node, - boolean isLeaf) - { - TreePath curr = new TreePath(getPathToRoot(node, 0)); - boolean selected = tree.isPathSelected(curr); - boolean expanded = false; - boolean hasIcons = false; - - if (tree.isVisible(curr)) - { - if (!isLeaf) - expanded = tree.isExpanded(curr); - - if (editingComponent != null && editingPath != null && isEditing(tree) - && node.equals(editingPath.getLastPathComponent())) - { - Rectangle bounds = getPathBounds(tree, editingPath); - rendererPane.paintComponent(g, editingComponent.getParent(), null, - new Rectangle(0, 0, bounds.width, - bounds.height)); - } - else - { - TreeCellRenderer dtcr = tree.getCellRenderer(); - if (dtcr == null) - dtcr = createDefaultCellRenderer(); - - int row = getRowForPath(tree, curr); - - Component c = dtcr.getTreeCellRendererComponent(tree, node, - selected, expanded, - isLeaf, row, false); - - rendererPane.paintComponent(g, c, c.getParent(), - getCellBounds(x, y, node)); - } - } - } - - /** * Recursively paints all elements of the tree Package private for use in * inner classes. * @@ -3065,8 +3129,6 @@ public class BasicTreeUI * of the current object * @param descent * is the number of elements drawn - * @param childNumber - * is the index of the current child in the tree * @param depth * is the depth of the current object in the tree * @param tree @@ -3077,71 +3139,79 @@ public class BasicTreeUI * is the current object to draw * @return int - current descent of the tree */ - int paintRecursive(Graphics g, int indentation, int descent, int childNumber, + int paintRecursive(Graphics g, int indentation, int descent, int depth, JTree tree, TreeModel mod, Object curr) { - Rectangle clip = g.getClipBounds(); + Rectangle clip = tree.getVisibleRect(); if (indentation > clip.x + clip.width + rightChildIndent || descent > clip.y + clip.height + getRowHeight()) return descent; + TreePath path = new TreePath(getPathToRoot(curr, 0)); int halfHeight = getRowHeight() / 2; int halfWidth = rightChildIndent / 2; int y0 = descent + halfHeight; int heightOfLine = descent + halfHeight; + int row = getRowForPath(tree, path); boolean isRootVisible = tree.isRootVisible(); - - if (mod.isLeaf(curr)) + boolean isExpanded = tree.isExpanded(path); + boolean isLeaf = mod.isLeaf(curr); + Rectangle bounds = getPathBounds(tree, path); + Object root = mod.getRoot(); + + if (isLeaf) { - paintNode(g, indentation + 4, descent, tree, curr, true); + paintRow(g, clip, null, bounds, path, row, true, false, true); descent += getRowHeight(); } else { if (depth > 0 || isRootVisible) { - paintNode(g, indentation + 4, descent, tree, curr, false); + paintRow(g, clip, null, bounds, path, row, isExpanded, false, false); descent += getRowHeight(); y0 += halfHeight; } - - int max = 0; - if (!mod.isLeaf(curr)) - max = mod.getChildCount(curr); - if (tree.isExpanded(new TreePath(getPathToRoot(curr, 0)))) + + if (isExpanded) { + int max = mod.getChildCount(curr); for (int i = 0; i < max; i++) { + Object child = mod.getChild(curr, i); + boolean childVis = tree.isVisible(new TreePath + (getPathToRoot(child, 0))); int indent = indentation + rightChildIndent; if (!isRootVisible && depth == 0) indent = 0; - else if ((!isRootVisible && !curr.equals(mod.getRoot())) - || isRootVisible) + else if (isRootVisible || + (!isRootVisible && !curr.equals(root)) && childVis) { g.setColor(getHashColor()); heightOfLine = descent + halfHeight; - g.drawLine(indentation + halfWidth, heightOfLine, - indentation + rightChildIndent, heightOfLine); + paintHorizontalLine(g, (JComponent) tree, heightOfLine, + indentation + halfWidth, indentation + rightChildIndent); } - descent = paintRecursive(g, indent, descent, i, depth + 1, - tree, mod, mod.getChild(curr, i)); + descent = paintRecursive(g, indent, descent, depth + 1, + tree, mod, child); } } } - if (tree.isExpanded(new TreePath(getPathToRoot(curr, 0)))) - if (y0 != heightOfLine && !mod.isLeaf(curr) - && mod.getChildCount(curr) > 0) + if (isExpanded) + if (y0 != heightOfLine + && (mod.getChildCount(curr) > 0 && + tree.isVisible(new TreePath(getPathToRoot(mod.getChild + (curr, 0), 0))))) { g.setColor(getHashColor()); - g.drawLine(indentation + halfWidth, y0, indentation + halfWidth, - heightOfLine); + paintVerticalLine(g, (JComponent) tree, indentation + halfWidth, y0, + heightOfLine); } - return descent; } - + /** * Recursively paints all the control icons on the tree. Package private for * use in inner classes. @@ -3152,76 +3222,99 @@ public class BasicTreeUI * of the current object * @param descent * is the number of elements drawn - * @param childNumber - * is the index of the current child in the tree * @param depth * is the depth of the current object in the tree * @param tree * is the tree to draw to * @param mod * is the TreeModel we are using to draw - * @param curr + * @param node * is the current object to draw - * @return int - current descent of the tree + * @return int current descent of the tree */ int paintControlIcons(Graphics g, int indentation, int descent, - int childNumber, int depth, JTree tree, TreeModel mod, + int depth, JTree tree, TreeModel mod, Object node) { - int h = descent; int rowHeight = getRowHeight(); - Icon ei = UIManager.getLookAndFeelDefaults().getIcon("Tree.expandedIcon"); - Icon ci = UIManager.getLookAndFeelDefaults().getIcon("Tree.collapsedIcon"); - Rectangle clip = g.getClipBounds(); + TreePath path = new TreePath(getPathToRoot(node, 0)); + Icon icon = getCurrentControlIcon(path); + + Rectangle clip = tree.getVisibleRect(); if (indentation > clip.x + clip.width + rightChildIndent || descent > clip.y + clip.height + getRowHeight()) return descent; - + if (mod.isLeaf(node)) descent += rowHeight; else - { + { + if (!node.equals(mod.getRoot()) && + (tree.isRootVisible() || getLevel(node) != 1)) + { + int width = icon.getIconWidth(); + int height = icon.getIconHeight() + 2; + int posX = indentation - rightChildIndent; + int posY = descent; + if (width > rightChildIndent) + posX -= gap; + else posX += width/2; + + if (height < rowHeight) + posY += height/2; + + icon.paintIcon(tree, g, posX, posY); + } + if (depth > 0 || tree.isRootVisible()) descent += rowHeight; - - int max = 0; - if (!mod.isLeaf(node)) - max = mod.getChildCount(node); - if (tree.isExpanded(new TreePath(getPathToRoot(node, 0)))) + + if (tree.isExpanded(path)) { - if (!node.equals(mod.getRoot())) - ei.paintIcon(tree, g, indentation - rightChildIndent - 3, h); - + int max = 0; + if (!mod.isLeaf(node)) + max = mod.getChildCount(node); + for (int i = 0; i < max; i++) { int indent = indentation + rightChildIndent; + Object child = mod.getChild(node, i); if (depth == 0 && !tree.isRootVisible()) - indent = -1; - - descent = paintControlIcons(g, indent, descent, i, depth + 1, - tree, mod, mod.getChild(node, i)); + indent = 1; + if (tree.isVisible(new TreePath(getPathToRoot(child, 0)))) + descent = paintControlIcons(g, indent, descent, depth + 1, + tree, mod, child); } } - else if (!node.equals(mod.getRoot())) - ci.paintIcon(tree, g, indentation - rightChildIndent - 3, - descent - getRowHeight()); } - + return descent; } /** - * Returns true if the LookAndFeel implements the control icons Package + * Returns true if the LookAndFeel implements the control icons. Package * private for use in inner classes. * - * @return true if control icons are visible + * @returns true if there are control icons */ boolean hasControlIcons() { - if (UIManager.getLookAndFeelDefaults().getIcon("Tree.expandedIcon") == null - || UIManager.getLookAndFeelDefaults().getIcon("Tree.collapsedIcon") == null) - return false; - return true; + if (expandedIcon != null || collapsedIcon != null) + return true; + return false; + } + + /** + * Returns control icon. It is null if the LookAndFeel does not implements the + * control icons. Package private for use in inner classes. + * + * @return control icon if it exists. + */ + Icon getCurrentControlIcon(TreePath path) + { + if (tree.isExpanded(path)) + return expandedIcon; + return collapsedIcon; } /** @@ -3235,8 +3328,10 @@ public class BasicTreeUI */ Object getParent(Object root, Object node) { - if (root == null || node == null) + if (root == null || node == null || + root.equals(node)) return null; + if (node instanceof TreeNode) return ((TreeNode) node).getParent(); return findNode(root, node); @@ -3253,113 +3348,69 @@ public class BasicTreeUI */ private Object findNode(Object root, Object node) { - TreeModel mod = tree.getModel(); - int size = 0; - if (!mod.isLeaf(root)) - size = mod.getChildCount(root); - for (int i = 0; i < size; i++) - { - if (mod.getIndexOfChild(root, node) != -1) - return root; - - Object n = findNode(mod.getChild(root, i), node); - if (n != null) - return n; - } - return null; - } - - /** - * Get next visible node in the tree. Package private for use in inner - * classes. - * - * @param the - * current node - * @return the next visible node in the JTree. Return null if there are no - * more. - */ - Object getNextVisibleNode(Object node) - { - Object next = null; - TreePath current = null; - - if (node != null) - next = getNextNode(node); - - if (next != null) + if (!treeModel.isLeaf(root) && !root.equals(node)) { - current = new TreePath(getPathToRoot(next, 0)); - if (tree.isVisible(current)) - return next; - - while (next != null && !tree.isVisible(current)) + int size = treeModel.getChildCount(root); + for (int j = 0; j < size; j++) { - next = getNextNode(next); + Object child = treeModel.getChild(root, j); + if (node.equals(child)) + return root; - if (next != null) - current = new TreePath(getPathToRoot(next, 0)); + Object n = findNode(child, node); + if (n != null) + return n; } } - return next; + return null; } - + /** * Get previous visible node in the tree. Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the next visible node in the JTree. Return null if there are no * more. */ Object getPreviousVisibleNode(Object node) { - Object prev = null; - TreePath current = null; - - if (node != null) - prev = getPreviousNode(node); - - if (prev != null) + updateCurrentVisiblePath(); + if (currentVisiblePath != null) { - current = new TreePath(getPathToRoot(prev, 0)); - if (tree.isVisible(current)) - return prev; - - while (prev != null && !tree.isVisible(current)) - { - prev = getPreviousNode(prev); - - if (prev != null) - current = new TreePath(getPathToRoot(prev, 0)); - } + Object[] nodes = currentVisiblePath.getPath(); + int i = 0; + while (i < nodes.length && !node.equals(nodes[i])) + i++; + // return the next node + if (i-1 >= 0) + return nodes[i-1]; } - return prev; + return null; } /** * Returns the next node in the tree Package private for use in inner classes. * - * @param the + * @param curr - * current node * @return the next node in the tree */ Object getNextNode(Object curr) { - TreeModel mod = tree.getModel(); - if (!mod.isLeaf(curr) && mod.getChildCount(curr) > 0) - return mod.getChild(curr, 0); + if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0) + return treeModel.getChild(curr, 0); Object node = curr; Object sibling = null; - do { sibling = getNextSibling(node); - node = getParent(mod.getRoot(), node); + node = getParent(treeModel.getRoot(), node); } while (sibling == null && node != null); - + return sibling; } @@ -3367,14 +3418,13 @@ public class BasicTreeUI * Returns the previous node in the tree Package private for use in inner * classes. * - * @param the + * @param node * current node * @return the previous node in the tree */ Object getPreviousNode(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; @@ -3384,13 +3434,13 @@ public class BasicTreeUI return parent; int size = 0; - if (!mod.isLeaf(sibling)) - size = mod.getChildCount(sibling); + if (!treeModel.isLeaf(sibling)) + size = treeModel.getChildCount(sibling); while (size > 0) { - sibling = mod.getChild(sibling, size - 1); - if (!mod.isLeaf(sibling)) - size = mod.getChildCount(sibling); + sibling = treeModel.getChild(sibling, size - 1); + if (!treeModel.isLeaf(sibling)) + size = treeModel.getChildCount(sibling); else size = 0; } @@ -3402,52 +3452,50 @@ public class BasicTreeUI * Returns the next sibling in the tree Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the next sibling in the tree */ Object getNextSibling(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; - int index = mod.getIndexOfChild(parent, node) + 1; + int index = treeModel.getIndexOfChild(parent, node) + 1; int size = 0; - if (!mod.isLeaf(parent)) - size = mod.getChildCount(parent); + if (!treeModel.isLeaf(parent)) + size = treeModel.getChildCount(parent); if (index == 0 || index >= size) return null; - return mod.getChild(parent, index); + return treeModel.getChild(parent, index); } - + /** * Returns the previous sibling in the tree Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the previous sibling in the tree */ Object getPreviousSibling(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; - int index = mod.getIndexOfChild(parent, node) - 1; + int index = treeModel.getIndexOfChild(parent, node) - 1; int size = 0; - if (!mod.isLeaf(parent)) - size = mod.getChildCount(parent); + if (!treeModel.isLeaf(parent)) + size = treeModel.getChildCount(parent); if (index < 0 || index >= size) return null; - return mod.getChild(parent, index); + return treeModel.getChild(parent, index); } /** @@ -3463,22 +3511,24 @@ public class BasicTreeUI { if (path != null) { - if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) + if (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.SINGLE_TREE_SELECTION) { + tree.getSelectionModel().clearSelection(); tree.addSelectionPath(path); tree.setLeadSelectionPath(path); } - else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) + else if (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) { // TODO } else { - tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - - tree.getSelectionModel().clearSelection(); tree.addSelectionPath(path); tree.setLeadSelectionPath(path); + tree.getSelectionModel().setSelectionMode + (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } } } @@ -3495,7 +3545,6 @@ public class BasicTreeUI */ Object[] getPathToRoot(Object node, int depth) { - TreeModel mod = tree.getModel(); if (node == null) { if (depth == 0) @@ -3504,7 +3553,7 @@ public class BasicTreeUI return new Object[depth]; } - Object[] path = getPathToRoot(getParent(mod.getRoot(), node), depth + 1); + Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node), depth + 1); path[path.length - depth - 1] = node; return path; } @@ -3512,22 +3561,23 @@ public class BasicTreeUI /** * Returns the level of the node in the tree. * - * @param the + * @param node - * current node * @return the number of the level */ int getLevel(Object node) { int count = -1; + Object current = node; do { - current = getParent(tree.getModel().getRoot(), current); + current = getParent(treeModel.getRoot(), current); count++; } while (current != null); - + return count; } @@ -3586,10 +3636,271 @@ public class BasicTreeUI * is the center position in y-direction FIXME what to do if x < * (icon.width / 2). Same with y */ - protected void drawCentered(JComponent c, Graphics g, Icon icon, int x, int y) + protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y) { int beginPositionX = x - icon.getIconWidth() / 2; int beginPositionY = y - icon.getIconHeight() / 2; icon.paintIcon(c, g, beginPositionX, beginPositionY); } + + /** + * Draws a dashed horizontal line. + * + * @param g - the graphics configuration. + * @param y - the y location to start drawing at + * @param x1 - the x location to start drawing at + * @param x2 - the x location to finish drawing at + */ + protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2) + { + for (int i = x1; i < x2; i += 2) + g.drawLine(i, y, i + 1, y); + } + + /** + * Draws a dashed vertical line. + * + * @param g - the graphics configuration. + * @param x - the x location to start drawing at + * @param y1 - the y location to start drawing at + * @param y2 - the y location to finish drawing at + */ + protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) + { + for (int i = y1; i < y2; i += 2) + g.drawLine(x, i, x, i + 1); + } + + /** + * Paints the expand (toggle) part of a row. The receiver should NOT modify + * clipBounds, or insets. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintExpandControl(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + if (treeModel != null && hasControlIcons()) + paintControlIcons(g, 0, 0, 0, tree, treeModel, path.getLastPathComponent()); + } + + /** + * Paints the horizontal part of the leg. The receiver should NOT modify + * clipBounds, or insets. + * NOTE: parentRow can be -1 if the root is not visible. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + // FIXME: not implemented + } + + /** + * Paints the vertical part of the leg. The receiver should NOT modify + * clipBounds, insets. + * + * @param g - the graphics configuration. + * @param clipBounds - + * @param insets - + * @param path - the path to draw the vertical part for. + */ + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, TreePath path) + { + // FIXME: not implemented + } + + /** + * Paints the renderer part of a row. The receiver should NOT modify clipBounds, + * or insets. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintRow(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + boolean selected = tree.isPathSelected(path); + boolean hasIcons = false; + Object node = path.getLastPathComponent(); + + if (tree.isVisible(path)) + { + if (!validCachedPreferredSize) + updateCachedPreferredSize(); + bounds.x += gap; + bounds.width = preferredSize.width + bounds.x; + + if (editingComponent != null && editingPath != null && isEditing(tree) + && node.equals(editingPath.getLastPathComponent())) + { + rendererPane.paintComponent(g, editingComponent.getParent(), null, + bounds); + } + else + { + TreeCellRenderer dtcr = tree.getCellRenderer(); + if (dtcr == null) + dtcr = createDefaultCellRenderer(); + + Component c = dtcr.getTreeCellRendererComponent(tree, node, + selected, isExpanded, isLeaf, row, tree.hasFocus()); + rendererPane.paintComponent(g, c, c.getParent(), bounds); + } + } + } + + /** + * Prepares for the UI to uninstall. + */ + protected void prepareForUIUninstall() + { + // TODO: Implement this properly. + } + + /** + * Returns true if the expand (toggle) control should be drawn for the + * specified row. + * + * @param path - current path to check for. + * @param row - current row to check for. + * @param isExpanded - true if the path is expanded + * @param hasBeenExpanded - true if the path has been expanded already + * @param isLeaf - true if the row is a lead + */ + protected boolean shouldPaintExpandControl(TreePath path, int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) + { + Object node = path.getLastPathComponent(); + if (treeModel != null && (!isLeaf && !node.equals(treeModel.getRoot())) && + (tree.isRootVisible() || getLevel(node) != 1)) + return true; + return false; + } + + /** + * Updates the cached current TreePath of all visible + * nodes in the tree. + */ + void updateCurrentVisiblePath() + { + if (treeModel == null) + return; + + Object next = treeModel.getRoot(); + Rectangle bounds = getCellBounds(0, 0, next); + boolean rootVisible = isRootVisible(); + + // If root is not a valid size to be visible, or is + // not visible and the tree is expanded, then the next node acts + // as the root + if ((bounds.width == 0 && bounds.height == 0) || (!rootVisible + && tree.isExpanded(new TreePath(next)))) + next = getNextNode(next); + + Object root = next; + TreePath current = null; + while (next != null) + { + if (current == null) + current = new TreePath(next); + else + current = current.pathByAddingChild(next); + do + { + TreePath path = new TreePath(getPathToRoot(next, 0)); + if ((tree.isVisible(path) && tree.isExpanded(path)) + || treeModel.isLeaf(next)) + next = getNextNode(next); + else + { + Object pNext = next; + next = getNextSibling(pNext); + // if no next sibling, check parent's next sibling. + if (next == null) + { + Object parent = getParent(root, pNext); + while (next == null && parent != null) + { + next = getNextSibling(parent); + if (next == null) + parent = getParent(treeModel.getRoot(), next); + } + } + } + } + while (next != null && + !tree.isVisible(new TreePath(getPathToRoot(next, 0)))); + } + + currentVisiblePath = current; + if (currentVisiblePath != null) + tree.setVisibleRowCount(currentVisiblePath.getPathCount()); + else tree.setVisibleRowCount(0); + + if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 && + currentVisiblePath != null) + selectPath(tree, new TreePath(getPathToRoot(currentVisiblePath. + getPathComponent(0), 0))); + } + + /** + * Get next visible node in the currentVisiblePath. Package private for use in + * inner classes. + * + * @param node + * current node + * @return the next visible node in the JTree. Return null if there are no + * more. + */ + Object getNextVisibleNode(Object node) + { + if (currentVisiblePath != null) + { + Object[] nodes = currentVisiblePath.getPath(); + int i = 0; + while (i < nodes.length && !node.equals(nodes[i])) + i++; + // return the next node + if (i+1 < nodes.length) + return nodes[i+1]; + } + return null; + } } // BasicTreeUI diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java index 0d461332a70..51b902d6443 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java @@ -38,61 +38,22 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.image.ImageObserver; - import javax.swing.JComponent; -import javax.swing.JViewport; -import javax.swing.ViewportLayout; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; +import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ViewportUI; public class BasicViewportUI extends ViewportUI { - - ChangeListener changeListener; - Image backingStoreImage; - int backingStoreWidth = -1; - int backingStoreHeight = -1; - - class ChangeHandler implements ChangeListener + protected void installDefaults(JComponent c) { - public void stateChanged(ChangeEvent event) - { - JViewport v = (JViewport) event.getSource(); - v.repaint(); - } - } - - void installDefaults(JComponent c) - { c.setOpaque(true); + LookAndFeel.installColorsAndFont(c, "Viewport.background", + "Viewport.foreground", "Viewport.font"); } - - void uninstallDefaults(JComponent c) - { - } - - void installListeners(JComponent c) + protected void uninstallDefaults(JComponent c) { - ((JViewport)c).addChangeListener(changeListener); - } - - void uninstallListeners(JComponent c) - { - ((JViewport)c).removeChangeListener(changeListener); - } - - public BasicViewportUI() - { - changeListener = new ChangeHandler(); + // TODO: Implement this properly. } public static ComponentUI createUI(JComponent c) @@ -103,132 +64,12 @@ public class BasicViewportUI extends ViewportUI public void installUI(JComponent c) { super.installUI(c); - installListeners(c); + installDefaults(c); } public void uninstallUI(JComponent c) { - uninstallListeners(c); - } - - - public Dimension getPreferredSize(JComponent c) - { - // let the ViewportLayout decide - return null; - } - - public void paint(Graphics g, JComponent c) - { - JViewport port = (JViewport)c; - Component view = port.getView(); - - if (view == null) - return; - - Point pos = port.getViewPosition(); - Rectangle viewBounds = view.getBounds(); - Rectangle portBounds = port.getBounds(); - - if (viewBounds.width == 0 - || viewBounds.height == 0 - || portBounds.width == 0 - || portBounds.height == 0) - return; - - switch (port.getScrollMode()) - { - - case JViewport.BACKINGSTORE_SCROLL_MODE: - paintBackingStore(g, port, view, pos, viewBounds, portBounds); - break; - - case JViewport.BLIT_SCROLL_MODE: - // FIXME: implement separate blit mode - - case JViewport.SIMPLE_SCROLL_MODE: - default: - paintSimple(g, port, view, pos, viewBounds, portBounds); - break; - } - } - - private void paintSimple(Graphics g, - JViewport v, - Component view, - Point pos, - Rectangle viewBounds, - Rectangle portBounds) - { - Rectangle oldClip = g.getClipBounds(); - g.setClip(new Rectangle(0, 0, portBounds.width, portBounds.height)); - g.translate (-pos.x, -pos.y); - try - { - view.paint(g); - } - finally - { - g.translate (pos.x, pos.y); - g.setClip (oldClip); - } - } - - private void paintBackingStore(Graphics g, - JViewport v, - Component view, - Point pos, - Rectangle viewBounds, - Rectangle portBounds) - { - if (backingStoreImage == null - || backingStoreWidth != viewBounds.width - || backingStoreHeight != viewBounds.height) - { - backingStoreImage = v.createImage(viewBounds.width, viewBounds.height); - backingStoreWidth = viewBounds.width; - backingStoreHeight = viewBounds.height; - } - - Graphics g2 = backingStoreImage.getGraphics(); - - if (v.getBackground() != null) - { - // fill the backing store background - java.awt.Color save = g2.getColor(); - g2.setColor(v.getBackground()); - g2.fillRect (0, 0, backingStoreWidth, backingStoreHeight); - g2.setColor(save); - - // fill the viewport background - save = g.getColor(); - g.setColor(v.getBackground()); - g.fillRect (0, 0, portBounds.width, portBounds.height); - g.setColor(save); - - } - else - { - // clear the backing store background - g2.clearRect(0, 0, backingStoreWidth, backingStoreHeight); - - // clear the viewport background - g.clearRect(0, 0, portBounds.width, portBounds.height); - } - - g2.setClip(g.getClipBounds()); - g2.translate(-pos.x, -pos.y); - try - { - view.paint(g2); - } - finally - { - g2.translate(pos.x, pos.y); - } - g2 = null; - g.drawImage(backingStoreImage, - 0, 0, - (ImageObserver)null); + super.uninstallUI(c); + uninstallDefaults(c); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java index f55510684c6..4fa3b364056 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java @@ -45,21 +45,29 @@ import java.awt.Insets; import javax.swing.AbstractButton; import javax.swing.ButtonModel; +import javax.swing.JButton; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.border.AbstractBorder; import javax.swing.border.Border; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicBorders; +import javax.swing.text.JTextComponent; /** - * This factory class creates borders for the different Swing components - * UI. + * A factory class that creates borders for the different Swing components. * * @author Roman Kennke (roman@kennke.org) */ @@ -69,12 +77,24 @@ public class MetalBorders /** The shared instance for getButtonBorder(). */ private static Border buttonBorder; + /** The shared instance for getToggleButtonBorder(). */ + private static Border toggleButtonBorder; + + /** The shared instance for getDesktopIconBorder(). */ + private static Border desktopIconBorder; + /** The shared instance for getRolloverButtonBorder(). */ private static Border toolbarButtonBorder; /** The shared instance for getTextFieldBorder(). */ private static Border textFieldBorder; + /** The shared instance for getTextBorder(). */ + private static Border textBorder; + + /** The shared instance for getRolloverBorder(). */ + private static Border rolloverBorder; + /** * A MarginBorder that gets shared by multiple components. * Created on demand by the private helper function {@link @@ -83,20 +103,19 @@ public class MetalBorders private static BasicBorders.MarginBorder marginBorder; /** - * The border that is drawn around Swing buttons. + * A border used for {@link JButton} components. */ - public static class ButtonBorder - extends AbstractBorder - implements UIResource + public static class ButtonBorder extends AbstractBorder implements UIResource { /** The borders insets. */ protected static Insets borderInsets = new Insets(3, 3, 3, 3); /** - * Creates a new instance of ButtonBorder. + * Creates a new instance of <code>ButtonBorder</code>. */ public ButtonBorder() { + // Nothing to do here. } /** @@ -122,46 +141,55 @@ public class MetalBorders Color light = MetalLookAndFeel.getWhite(); Color middle = MetalLookAndFeel.getControl(); - // draw dark border - g.setColor(darkShadow); - g.drawRect(x, y, w - 2, h - 2); - - if (!bmodel.isPressed()) - { - // draw light border - g.setColor(light); - g.drawRect(x + 1, y + 1, w - 2, h - 2); + if (c.isEnabled()) + { + // draw dark border + g.setColor(darkShadow); + g.drawRect(x, y, w - 2, h - 2); - // draw crossing pixels of both borders - g.setColor(middle); - g.drawRect(x + 1, y + h - 2, 0, 0); - g.drawRect(x + w - 2, y + 1, 0, 0); - } - else - { - // draw light border - g.setColor(light); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + if (!bmodel.isPressed()) + { + // draw light border + g.setColor(light); + g.drawRect(x + 1, y + 1, w - 2, h - 2); - // draw shadow border - g.setColor(middle); - g.drawLine(x + 1, y + 1, x + w - 2, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + // draw crossing pixels of both borders + g.setColor(middle); + g.drawRect(x + 1, y + h - 2, 0, 0); + g.drawRect(x + w - 2, y + 1, 0, 0); + } + else + { + // draw light border + g.setColor(light); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - // draw crossing pixels of both borders - g.setColor(shadow); - g.drawRect(x + 1, y + h - 2, 0, 0); - g.drawRect(x + w - 2, y + 1, 0, 0); + // draw shadow border + g.setColor(middle); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawRect(x + 1, y + h - 2, 0, 0); + g.drawRect(x + w - 2, y + 1, 0, 0); + } + } + else + { + // draw disabled border + g.setColor(MetalLookAndFeel.getInactiveControlTextColor()); + g.drawRect(x, y, w - 2, h - 2); } } /** - * Returns the insets of the ButtonBorder. + * Returns the insets of the <code>ButtonBorder</code>. * * @param c the component for which the border is used * - * @return the insets of the ButtonBorder + * @return The insets of the ButtonBorder */ public Insets getBorderInsets(Component c) { @@ -169,19 +197,20 @@ public class MetalBorders } /** - * Returns the insets of the ButtonBorder in the specified Insets object. + * Returns the insets of the <code>ButtonBorder</code> in the specified + * <code>newInsets</code> object. * * @param c the component for which the border is used - * @param newInsets the insets object where to put the values + * @param newInsets the insets object where to put the values (if + * <code>null</code>, a new instance is created). * - * @return the insets of the ButtonBorder + * @return The insets. */ public Insets getBorderInsets(Component c, Insets newInsets) { if (newInsets == null) newInsets = new Insets(0, 0, 0, 0); - AbstractButton b = (AbstractButton) c; newInsets.bottom = borderInsets.bottom; newInsets.left = borderInsets.left; newInsets.right = borderInsets.right; @@ -191,6 +220,71 @@ public class MetalBorders } /** + * A border used when painting {@link JInternalFrame} instances. + */ + static class DesktopIconBorder extends AbstractBorder + implements UIResource + { + /** + * Creates a new border instance. + */ + public DesktopIconBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(3, 3, 2, 3); + else + { + newInsets.top = 3; + newInsets.left = 3; + newInsets.bottom = 2; + newInsets.right = 3; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawRect(x, y, w - 1, h - 1); + } + + } + + /** * A simple 3D border. */ public static class Flush3DBorder extends AbstractBorder @@ -201,6 +295,7 @@ public class MetalBorders */ public Flush3DBorder() { + // Nothing to do here. } /** @@ -262,6 +357,88 @@ public class MetalBorders } /** + * A border used for a {@link JInternalFrame} when it is being used as a + * palette. + * + * @since 1.3 + */ + public static class PaletteBorder extends AbstractBorder + implements UIResource + { + /** + * Creates a new <code>PaletteBorder</code>. + */ + public PaletteBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @param newInsets the insets object that, if non-<code>null</code>, will + * be populated with the result from this method. + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(1, 1, 1, 1); + else + { + newInsets.top = 1; + newInsets.left = 1; + newInsets.bottom = 1; + newInsets.right = 1; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + Color savedColor = g.getColor(); + + // draw the outline + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawRect(x, y, w - 1, h - 1); + + // put a dot in each corner + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x, y, 1, 1); + g.fillRect(x + w - 1, y, 1, 1); + g.fillRect(x + w - 1, y + h - 1, 1, 1); + g.fillRect(x, y + h - 1, 1, 1); + g.setColor(savedColor); + } + + } + + /** * A border used for the {@link JTextField} component. */ public static class TextFieldBorder extends Flush3DBorder @@ -272,6 +449,7 @@ public class MetalBorders */ public TextFieldBorder() { + // Nothing to do here. } /** @@ -286,8 +464,17 @@ public class MetalBorders */ public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) - { - if (c.isEnabled()) + { + boolean enabledTextBorder; + if (c instanceof JTextComponent) + { + JTextComponent tc = (JTextComponent) c; + enabledTextBorder = tc.isEnabled() && tc.isEditable(); + } + else + enabledTextBorder = false; + + if (enabledTextBorder) super.paintBorder(c, g, x, y, w, h); else { @@ -301,7 +488,7 @@ public class MetalBorders } /** - * A border used when painting {@link JInternalFrame} instances. + * A border used for the {@link JInternalFrame} component. */ public static class InternalFrameBorder extends AbstractBorder implements UIResource @@ -311,6 +498,7 @@ public class MetalBorders */ public InternalFrameBorder() { + // Nothing to do here. } /** @@ -386,7 +574,10 @@ public class MetalBorders g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15); // draw the line highlights - g.setColor(MetalLookAndFeel.getControl()); + if (f.isSelected()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlShadow()); g.drawLine(x + 15, y + 3, x + w - 14, y + 3); g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2); g.drawLine(x + 3, y + 15, x + 3, y + h - 14); @@ -396,24 +587,129 @@ public class MetalBorders } /** + * A border used for {@link JInternalFrame} components that are + * presented as dialogs (by the {@link JOptionPane} class). + */ + public static class OptionDialogBorder extends AbstractBorder + implements UIResource + { + + /** + * Creates a new border instance. + */ + public OptionDialogBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(3, 3, 3, 3); + else + { + newInsets.top = 3; + newInsets.left = 3; + newInsets.bottom = 3; + newInsets.right = 3; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + + JInternalFrame f = (JInternalFrame) c; + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + if (f.getContentPane() instanceof JOptionPane) + { + JOptionPane pane = (JOptionPane) f.getContentPane(); + int type = pane.getMessageType(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + if (type == JOptionPane.QUESTION_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.questionDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + if (type == JOptionPane.WARNING_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.warningDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + else if (type == JOptionPane.ERROR_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.errorDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + } + + // fill the border background + g.fillRect(x, y, w, 3); + g.fillRect(x, y, 3, h); + g.fillRect(x + w - 3, y, 3, h); + g.fillRect(x, y + h - 3, w, 3); + + // draw a dot in each corner + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x, y, 1, 1); + g.fillRect(x + w - 1, y, 1, 1); + g.fillRect(x + w - 1, y + h - 1, 1, 1); + g.fillRect(x, y + h - 1, 1, 1); + + } + + } + + /** * A border used for {@link JMenu} and {@link JMenuItem} components. */ - public static class MenuItemBorder - extends AbstractBorder - implements UIResource + public static class MenuItemBorder extends AbstractBorder + implements UIResource { /** The border insets. */ - protected static Insets borderInsets = new Insets(2, 2, 2, 2); - - // TODO: find where the real colors come from - private static Color borderColorDark = new Color(102, 102, 153); - private static Color borderColorLight = new Color(255, 255, 255); + protected static Insets borderInsets = new Insets(1, 1, 1, 1); /** * Creates a new border instance. */ public MenuItemBorder() { + // Nothing to do here. } /** @@ -430,15 +726,17 @@ public class MetalBorders public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { + Color dark = MetalLookAndFeel.getPrimaryControlDarkShadow(); + Color light = MetalLookAndFeel.getPrimaryControlHighlight(); if (c instanceof JMenu) { JMenu menu = (JMenu) c; if (menu.isSelected()) { - g.setColor(borderColorDark); + g.setColor(dark); g.drawLine(x, y, x, y + h); g.drawLine(x, y, x + w, y); g.drawLine(x + w - 2, y + 1, x + w - 2, y + h); - g.setColor(borderColorLight); + g.setColor(light); g.drawLine(x + w - 1, y + 1, x + w - 1, y + h); } } @@ -446,12 +744,18 @@ public class MetalBorders { JMenuItem item = (JMenuItem) c; if (item.isArmed()) - { - g.setColor(borderColorDark); - g.drawLine(x, y, x + w, y); - g.setColor(borderColorLight); - g.drawLine(x, y + h - 1, x + w, y + h - 1); - } + { + g.setColor(dark); + g.drawLine(x, y, x + w, y); + g.setColor(light); + g.drawLine(x, y + h - 1, x + w, y + h - 1); + } + else + { + // Normally we draw a light line on the left. + g.setColor(light); + g.drawLine(x, y, x, y + h); + } } } @@ -505,6 +809,7 @@ public class MetalBorders */ public MenuBarBorder() { + // Nothing to do here. } /** @@ -558,7 +863,7 @@ public class MetalBorders } /** - * A border for JScrollPanes. + * A border for {@link JScrollPane} components. */ public static class ScrollPaneBorder extends AbstractBorder @@ -572,6 +877,7 @@ public class MetalBorders */ public ScrollPaneBorder() { + // Nothing to do here. } /** @@ -634,6 +940,45 @@ public class MetalBorders } /** + * A button border that is only visible when the mouse pointer is within + * the button's bounds. + */ + public static class RolloverButtonBorder + extends MetalBorders.ButtonBorder + { + /** + * Creates a new border instance. + */ + public RolloverButtonBorder() + { + // Nothing to do here. + } + + /** + * Paints the border. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + boolean mouseIsOver = false; + if (c instanceof AbstractButton) + { + ButtonModel bmodel = ((AbstractButton) c).getModel(); + mouseIsOver = bmodel.isRollover(); + } + if (mouseIsOver) + super.paintBorder(c, g, x, y, w, h); + } + } + + /** * This border is used in Toolbar buttons as inner border. */ static class RolloverMarginBorder extends AbstractBorder @@ -646,6 +991,7 @@ public class MetalBorders */ public RolloverMarginBorder() { + // Nothing to do here. } /** @@ -693,13 +1039,14 @@ public class MetalBorders { /** The border's insets. */ - protected static Insets borderInsets = new Insets(2, 2, 1, 1); + protected static Insets borderInsets = new Insets(3, 1, 2, 1); /** * Constructs a new PopupMenuBorder. */ public PopupMenuBorder() { + // Nothing to do here. } /** @@ -763,13 +1110,258 @@ public class MetalBorders // draw highlighted inner border (only top and left) g.setColor(light); - g.drawLine(x + 1, y + 1, x + 1, y + h - 2); g.drawLine(x + 1, y + 1, x + w - 2, y + 1); } } /** + * A border used for the {@link JToggleButton} component. + * + * @since 1.3 + */ + public static class ToggleButtonBorder + extends ButtonBorder + { + /** + * Creates a new border instance. + */ + public ToggleButtonBorder() + { + // Nothing to do here. + } + + /** + * Paints the toggle button border. + * + * @param c the component for which we paint the border + * @param g the Graphics context to use + * @param x the X coordinate of the upper left corner of c + * @param y the Y coordinate of the upper left corner of c + * @param w the width of c + * @param h the height of c + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + ButtonModel bmodel = null; + + if (c instanceof AbstractButton) + bmodel = ((AbstractButton) c).getModel(); + + Color darkShadow = MetalLookAndFeel.getControlDarkShadow(); + Color shadow = MetalLookAndFeel.getControlShadow(); + Color light = MetalLookAndFeel.getWhite(); + Color middle = MetalLookAndFeel.getControl(); + + if (c.isEnabled()) + { + // draw dark border + g.setColor(darkShadow); + g.drawRect(x, y, w - 2, h - 2); + + if (!bmodel.isArmed()) + { + // draw light border + g.setColor(light); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + if (bmodel.isSelected()) + g.setColor(middle); + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); + } + else + { + // draw light border + g.setColor(light); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + + // draw shadow border + g.setColor(shadow); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); + + } + // draw corners + g.setColor(middle); + g.drawLine(x, y + h - 1, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y); + } + else + { + // draw disabled border + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawRect(x, y, w - 2, h - 2); + } + } + } + + /** + * A border used for the {@link JToolBar} component. + */ + public static class ToolBarBorder extends AbstractBorder + implements UIResource, SwingConstants + { + /** + * Creates a new border instance. + */ + public ToolBarBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + JToolBar tb = (JToolBar) c; + if (tb.getOrientation() == JToolBar.HORIZONTAL) + { + if (newInsets == null) + newInsets = new Insets(2, 16, 2, 2); + else + { + newInsets.top = 2; + newInsets.left = 16; + newInsets.bottom = 2; + newInsets.right = 2; + } + return newInsets; + } + else // assume JToolBar.VERTICAL + { + if (newInsets == null) + newInsets = new Insets(16, 2, 2, 2); + else + { + newInsets.top = 16; + newInsets.left = 2; + newInsets.bottom = 2; + newInsets.right = 2; + } + return newInsets; + } + + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + + JToolBar tb = (JToolBar) c; + if (tb.getOrientation() == JToolBar.HORIZONTAL) + { + MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + 11, y + h - 5, + MetalLookAndFeel.getControlHighlight(), + MetalLookAndFeel.getControlDarkShadow()); + } + else + { + MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + w - 5, y + 11, + MetalLookAndFeel.getControlHighlight(), + MetalLookAndFeel.getControlDarkShadow()); + } + } + + } + + /** + * A border for table header cells. + * + * @since 1.3 + */ + public static class TableHeaderBorder extends AbstractBorder + { + /** + * The insets of this border. + */ + // TODO: According to tests that I have done, this is really the border + // that should be returned by getBorderInsets(). However, the name + // is very distracting. Is there any deeper meaning in it? + protected Insets editorBorderInsets; + + /** + * Creates a new instance of <code>TableHeaderBorder</code>. + */ + public TableHeaderBorder() + { + editorBorderInsets = new Insets(1, 1, 1, 1); + } + + /** + * Return the insets of this border. + * + * @return the insets of this border + */ + public Insets getBorderInsets(Component c) + { + return editorBorderInsets; + } + + /** + * Paints the border. + * + * @param c the component for which to paint the border + * @param g the graphics context to use + * @param x the x cooridinate of the border rectangle + * @param y the y cooridinate of the border rectangle + * @param w the width of the border rectangle + * @param h the height of the border rectangle + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) + { + Color dark = MetalLookAndFeel.getControlDarkShadow(); + Color light = MetalLookAndFeel.getWhite(); + Color old = g.getColor(); + g.setColor(light); + g.drawLine(x, y, x + w - 2, y); + g.drawLine(x, y, x, y + h - 2); + g.setColor(dark); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(old); + } + } + + /** * Returns a border for Swing buttons in the Metal Look & Feel. * * @return a border for Swing buttons in the Metal Look & Feel @@ -785,6 +1377,40 @@ public class MetalBorders } return buttonBorder; } + + /** + * Returns a border for use with {@link JToggleButton} components. + * + * @return A border. + * + * @since 1.3 + */ + public static Border getToggleButtonBorder() + { + if (toggleButtonBorder == null) + { + Border outer = new ToggleButtonBorder(); + Border inner = getMarginBorder(); + toggleButtonBorder = new BorderUIResource.CompoundBorderUIResource + (outer, inner); + } + return toggleButtonBorder; + } + + /** + * Returns a border instance that is used with a {@link JInternalFrame} when + * it is in the iconified state. + * + * @return A border. + * + * @since 1.3 + */ + public static Border getDesktopIconBorder() + { + if (desktopIconBorder == null) + desktopIconBorder = new DesktopIconBorder(); + return desktopIconBorder; + } /** * Returns a border for use by the {@link JTextField} component. @@ -796,11 +1422,36 @@ public class MetalBorders public static Border getTextFieldBorder() { if (textFieldBorder == null) - textFieldBorder = new TextFieldBorder(); + { + Border inner = getMarginBorder(); + Border outer = new TextFieldBorder(); + textFieldBorder = + new BorderUIResource.CompoundBorderUIResource(outer, inner); + } return textFieldBorder; } /** + * Returns the border that is used for text components (except text fields, + * which use {@link #getTextFieldBorder}. + * + * @return the border that is used for text components + * + * @since 1.3 + */ + public static Border getTextBorder() + { + if (textBorder == null) + { + Border inner = getMarginBorder(); + Border outer = new Flush3DBorder(); + textBorder = + new BorderUIResource.CompoundBorderUIResource(outer, inner); + } + return textBorder; + } + + /** * Returns a border for Toolbar buttons in the Metal Look & Feel. * * @return a border for Toolbar buttons in the Metal Look & Feel @@ -828,4 +1479,22 @@ public class MetalBorders marginBorder = new BasicBorders.MarginBorder(); return marginBorder; } + + /** + * Returns a shared instance of a compound border for rollover buttons. + * + * @return A shared border instance. + */ + static Border getRolloverBorder() + { + if (rolloverBorder == null) + { + Border outer = new MetalBorders.RolloverButtonBorder(); + Border inner = MetalBorders.getMarginBorder(); + rolloverBorder = new BorderUIResource.CompoundBorderUIResource(outer, + inner); + } + return rolloverBorder; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java new file mode 100644 index 00000000000..e6fb22e929f --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java @@ -0,0 +1,86 @@ +/* MetalButtonListener.java + Copyright (C) 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 javax.swing.plaf.metal; + +import java.beans.PropertyChangeEvent; + +import javax.swing.AbstractButton; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicButtonListener; + +/** + * A listener for buttons under the {@link MetalLookAndFeel}. + * + * @see MetalButtonUI#createButtonListener + */ +class MetalButtonListener extends BasicButtonListener +{ + + /** + * Creates a new instance. + * + * @param button the button. + */ + public MetalButtonListener(AbstractButton button) + { + super(button); + } + + /** + * Handles property change events. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + super.propertyChange(e); + if (e.getPropertyName().equals( + AbstractButton.ROLLOVER_ENABLED_CHANGED_PROPERTY)) + { + AbstractButton b = (AbstractButton) e.getSource(); + if (b.getBorder() instanceof UIResource) + { + if (Boolean.TRUE.equals(e.getNewValue())) + b.setBorder(MetalBorders.getRolloverBorder()); + else if (Boolean.FALSE.equals(e.getNewValue())) + b.setBorder(MetalBorders.getButtonBorder()); + } + } + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java index 0dac5ec3953..02c39c1499e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java @@ -39,18 +39,23 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; import javax.swing.AbstractButton; +import javax.swing.JButton; import javax.swing.JComponent; -import javax.swing.JToolBar; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; /** - * The Metal Look & Feel implementation for - * {@link javax.swing.AbstractButton}s. + * A UI delegate for the {@link JButton} component. * * @author Roman Kennke (roman@kennke.org) */ @@ -58,27 +63,25 @@ public class MetalButtonUI extends BasicButtonUI { - /** The cached MetalButtonUI instance. */ - private static MetalButtonUI instance = null; - - /** The color for the focus border. */ + /** The color used to draw the focus rectangle around the text and/or icon. */ protected Color focusColor; - - /** The color that indicates a selected button. */ + + /** The background color for the button when it is pressed. */ protected Color selectColor; /** The color for disabled button labels. */ protected Color disabledTextColor; /** - * Creates a new instance of MetalButtonUI. + * Creates a new instance. */ public MetalButtonUI() { super(); - focusColor = getFocusColor(); - selectColor = getSelectColor(); - disabledTextColor = getDisabledTextColor(); + UIDefaults def = UIManager.getLookAndFeelDefaults(); + focusColor = def.getColor(getPropertyPrefix() + "focus"); + selectColor = def.getColor(getPropertyPrefix() + "select"); + disabledTextColor = def.getColor(getPropertyPrefix() + "disabledText"); } /** @@ -88,8 +91,7 @@ public class MetalButtonUI */ protected Color getFocusColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".focus"); + return focusColor; } /** @@ -99,8 +101,7 @@ public class MetalButtonUI */ protected Color getSelectColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".select"); + return selectColor; } /** @@ -110,36 +111,123 @@ public class MetalButtonUI */ protected Color getDisabledTextColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".disabledText"); + return disabledTextColor; } /** - * Returns an instance of MetalButtonUI. - * - * @param component a button for which a UI instance should be returned + * Returns a UI delegate for the specified component. + * + * @param c the component (should be a subclass of {@link AbstractButton}). + * + * @return A new instance of <code>MetalButtonUI</code>. */ - public static ComponentUI createUI(JComponent component) - { - if (instance == null) - instance = new MetalButtonUI(); - return instance; + public static ComponentUI createUI(JComponent c) { + return new MetalButtonUI(); } /** - * Install the Look & Feel defaults for Buttons. - * - * @param button the button for which to install the Look & Feel + * Installs the default settings for the specified button. + * + * @param button the button. + * + * @see #uninstallDefaults(AbstractButton) */ public void installDefaults(AbstractButton button) { super.installDefaults(button); + if (button.isRolloverEnabled()) + { + if (button.getBorder() instanceof UIResource) + button.setBorder(MetalBorders.getRolloverBorder()); + } + } + + /** + * Removes the defaults added by {@link #installDefaults(AbstractButton)}. + */ + public void uninstallDefaults(AbstractButton button) + { + super.uninstallDefaults(button); + if (button.getBorder() instanceof UIResource) + button.setBorder(null); + } - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - button.setFont(defaults.getFont("Button.font")); + /** + * Returns a button listener for the specified button. + * + * @param button the button. + * + * @return A button listener. + */ + protected BasicButtonListener createButtonListener(AbstractButton button) + { + return new MetalButtonListener(button); + } - if (button.getParent() instanceof JToolBar) - button.setBorder(MetalBorders.getToolbarButtonBorder()); + /** + * Paints the background of the button to indicate that it is in the "pressed" + * state. + * + * @param g the graphics context. + * @param b the button. + */ + protected void paintButtonPressed(Graphics g, AbstractButton b) + { + if (b.isContentAreaFilled()) + { + Rectangle area = b.getVisibleRect(); + g.setColor(selectColor); + g.fillRect(area.x, area.y, area.width, area.height); + } + } + + /** + * Paints the focus rectangle around the button text and/or icon. + * + * @param g the graphics context. + * @param b the button. + * @param viewRect the button bounds. + * @param textRect the text bounds. + * @param iconRect the icon bounds. + */ + protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, + Rectangle textRect, Rectangle iconRect) { + if (b.isEnabled() && b.hasFocus() && b.isFocusPainted()) + { + Color savedColor = g.getColor(); + g.setColor(getFocusColor()); + Rectangle focusRect = iconRect.union(textRect); + g.drawRect(focusRect.x - 1, focusRect.y, + focusRect.width + 1, focusRect.height); + g.setColor(savedColor); + } + } + + /** + * Paints the button text. + * + * @param g the graphics context. + * @param c the button. + * @param textRect the text bounds. + * @param text the text to display. + */ + protected void paintText(Graphics g, JComponent c, Rectangle textRect, + String text) + { + AbstractButton b = (AbstractButton) c; + Font f = b.getFont(); + g.setFont(f); + FontMetrics fm = g.getFontMetrics(f); + + if (b.isEnabled()) + { + g.setColor(b.getForeground()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + } + else + { + g.setColor(getDisabledTextColor()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + } } - } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java index 3f3c9ce58b3..6b9f31b85b6 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java @@ -38,10 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.awt.Color; import java.awt.Component; import java.awt.Graphics; - import java.io.Serializable; import javax.swing.Icon; @@ -49,8 +47,7 @@ import javax.swing.JCheckBox; import javax.swing.plaf.UIResource; /** - * An {@link Icon} implementation for {@link JCheckBox}es in the - * Metal Look & Feel. + * An {@link Icon} used by the {@link MetalCheckBoxUI} class. * * @author Roman Kennke (roman@kennke.org) */ @@ -79,7 +76,10 @@ public class MetalCheckBoxIcon */ protected void drawCheck(Component c, Graphics g, int x, int y) { - g.setColor(Color.BLACK); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(3 + x, 5 + y, 3 + x, 9 + y); g.drawLine(4 + x, 5 + y, 4 + x, 9 + y); g.drawLine(5 + x, 7 + y, 9 + x, 3 + y); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java index c46cb5f2fb1..b4f6f0a56cd 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java @@ -44,8 +44,7 @@ import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; /** - * A UI delegate for the {@link JCheckBox} component under the - * {@link MetalLookAndFeel}. + * A UI delegate for the {@link JCheckBox} component. */ public class MetalCheckBoxUI extends MetalRadioButtonUI @@ -64,11 +63,11 @@ public class MetalCheckBoxUI } /** - * Returns an instance of MetalCheckBoxUI. + * Returns a shared instance of <code>MetalCheckBoxUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalCheckBoxUI + * @return A shared instance of <code>MetalCheckBoxUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java new file mode 100644 index 00000000000..6993e18e9b9 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java @@ -0,0 +1,241 @@ +/* MetalComboBoxButton.java + Copyright (C) 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 javax.swing.plaf.metal; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.CellRendererPane; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.SwingUtilities; + +/** + * A button used by the {@link MetalComboBoxUI} class. + */ +public class MetalComboBoxButton extends JButton { + + /** A reference to the JComboBox that the button belongs to. */ + protected JComboBox comboBox; + + /** A reference to the JList. */ + protected JList listBox; + + /** ??? */ + protected CellRendererPane rendererPane; + + /** The button icon. */ + protected Icon comboIcon; + + /** Display just the icon, or the icon plus the label. */ + protected boolean iconOnly; + + /** + * Creates a new button. + * + * @param cb the combo that the button is used for (<code>null</code> not + * permitted). + * @param i the icon displayed on the button. + * @param pane the rendering pane. + * @param list the list. + */ + public MetalComboBoxButton(JComboBox cb, Icon i, CellRendererPane pane, + JList list) + { + this(cb, i, cb.isEditable(), pane, list); + } + + /** + * Creates a new button. + * + * @param cb the combo that the button is used for (<code>null</code> not + * permitted). + * @param i the icon displayed on the button. + * @parma onlyIcon a flag that specifies whether the button displays only an + * icon, or text as well. + * @param pane the rendering pane. + * @param list the list. + */ + public MetalComboBoxButton(JComboBox cb, Icon i, boolean onlyIcon, + CellRendererPane pane, JList list) + { + super(); + if (cb == null) + throw new NullPointerException("Null 'cb' argument"); + comboBox = cb; + comboIcon = i; + iconOnly = onlyIcon; + listBox = list; + rendererPane = pane; + } + + /** + * Returns the combo box that the button is used with. + * + * @return The combo box. + */ + public final JComboBox getComboBox() + { + return comboBox; + } + + /** + * Sets the combo box that the button is used with. + * + * @param cb the combo box. + */ + public final void setComboBox(JComboBox cb) + { + comboBox = cb; + } + + /** + * Returns the icon displayed by the button. By default, this will be an + * instance of {@link MetalComboBoxIcon}. + * + * @return The icon displayed by the button. + */ + public final Icon getComboIcon() + { + return comboIcon; + } + + /** + * Sets the icon displayed by the button. + * + * @param i the icon. + */ + public final void setComboIcon(Icon i) + { + comboIcon = i; + } + + /** + * Returns a flag that controls whether the button displays an icon only, + * or text as well. + * + * @return A boolean. + */ + public final boolean isIconOnly() + { + return iconOnly; + } + + /** + * Sets the flag that controls whether the button displays an icon only, + * or text as well. + * + * @param isIconOnly the flag. + */ + public final void setIconOnly(boolean isIconOnly) + { + iconOnly = isIconOnly; + } + + /** + * Returns <code>false</code>, to indicate that this component is not part + * of the focus traversal group. + * + * @return <code>false</code> + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Enables or disables the button. + * + * @param enabled the new status. + */ + public void setEnabled(boolean enabled) + { + super.setEnabled(enabled); + // TODO: figure out what this might need to be used for + // perhaps it has something to do with the button's icon and/or border? + } + + /** + * Paints the component. + * + * @param g the graphics device. + */ + public void paintComponent(Graphics g) + { + super.paintComponent(g); + if (iconOnly) + { + Rectangle bounds = getBounds(); + int x = (bounds.width - comboIcon.getIconWidth()) / 2; + int y = (bounds.height - comboIcon.getIconHeight()) / 2; + comboIcon.paintIcon(comboBox, g, x, y); + } + else + { + Object selected = comboBox.getModel().getSelectedItem(); + if (selected == null) + selected = ""; + Rectangle bounds = comboBox.getBounds(); + Rectangle innerArea = SwingUtilities.calculateInnerArea(this, null); + Insets insets = comboBox.getInsets(); + Rectangle renderArea = new Rectangle(innerArea.x, innerArea.y, + innerArea.width - comboIcon.getIconWidth() - 4, innerArea.height); + Component cellRenderer + = comboBox.getRenderer().getListCellRendererComponent(this.listBox, + selected, comboBox.getSelectedIndex(), false, false); + cellRenderer.setBackground(comboBox.getBackground()); + cellRenderer.setEnabled(comboBox.isEnabled()); + rendererPane.paintComponent(g, cellRenderer, this, renderArea); + if (comboBox.hasFocus()) + { + g.setColor(MetalLookAndFeel.getFocusColor()); + g.drawRect(innerArea.x, innerArea.y - 1, innerArea.width - 1, + innerArea.height); + } + int iconX = bounds.width - insets.right - comboIcon.getIconWidth() - 7; + int iconY = insets.top + + (bounds.height - comboIcon.getIconHeight()) / 2; + comboIcon.paintIcon(comboBox, g, iconX, iconY); + } + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java new file mode 100644 index 00000000000..a531079cb1d --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java @@ -0,0 +1,143 @@ +/* MetalComboBoxEditor.java + Copyright (C) 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 javax.swing.plaf.metal; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.JTextField; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalBorders.Flush3DBorder; + +/** + * An editor used by the {@link MetalComboBoxUI} class. + */ +public class MetalComboBoxEditor extends BasicComboBoxEditor +{ + /** + * A border used for the {@link JTextField} component. + */ + static class MetalComboBoxEditorBorder extends Flush3DBorder + { + /** + * Creates a new border instance. + */ + public MetalComboBoxEditorBorder() + { + // Nothing to do here. + } + + /** + * Paints the border for the specified component. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + Color savedColor = g.getColor(); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 2); + g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + g.setColor(savedColor); + } + + /** + * Measures the width of this border. + * + * @param c the component whose border is to be measured. + * + * @return an Insets object whose <code>left</code>, <code>right</code>, + * <code>top</code> and <code>bottom</code> fields indicate the + * width of the border at the respective edge, which is zero + * for the default implementation provided by AbstractButton. + * + * @see #getBorderInsets(java.awt.Component, java.awt.Insets) + */ + public Insets getBorderInsets(Component c) + { + return editorBorderInsets; + } + } + + /** + * A subclass of {@link MetalComboBoxEditor} that implements the + * {@link javax.swing.plaf.UIResource} interface. + */ + public static class UIResource extends MetalComboBoxEditor + implements javax.swing.plaf.UIResource + { + /** + * Creates a new instance. + */ + public UIResource() + { + // Nothing to do here. + } + } + + /** The editor's border insets. */ + protected static Insets editorBorderInsets = new Insets(4, 2, 4, 0); + + /** + * Creates a new editor. + */ + public MetalComboBoxEditor() + { + super(); + editor.setBorder(new MetalComboBoxEditorBorder()); + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java index ce4fa7d32ce..f21c5af6136 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java @@ -1,4 +1,4 @@ -/* MetalComboBoxButton.java +/* MetalComboBoxIcon.java Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -88,10 +88,11 @@ public class MetalComboBoxIcon implements Icon, Serializable { */ public void paintIcon(Component c, Graphics g, int x, int y) { - // TODO: work out whether/how the icon changes with different component - // states (and also different metal themes) Color savedColor = g.getColor(); - g.setColor(Color.black); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); for (int i = 0; i < 5; i++) g.drawLine(x + i, y + i, x + 9 - i, y + i); g.setColor(savedColor); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java index 28c279d8ed6..a43ee3cb04f 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java @@ -38,20 +38,134 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.CellRendererPane; +import javax.swing.ComboBoxEditor; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; -public class MetalComboBoxUI - extends BasicComboBoxUI -{ - /** The UI instances for JComboBoxes. */ - private static HashMap instances = null; +/** + * A UI delegate for the {@link JComboBox} component. + */ +public class MetalComboBoxUI extends BasicComboBoxUI +{ + /** + * A layout manager that arranges the editor component (if active) and the + * button that make up the combo box. + */ + public class MetalComboBoxLayoutManager + extends BasicComboBoxUI.ComboBoxLayoutManager + { + /** + * Creates a new instance of the layout manager. + */ + public MetalComboBoxLayoutManager() + { + // Nothing to do here. + } + + /** + * Arranges the editor (if visible) and button that comprise the combo + * box. + * + * @param parent the parent. + */ + public void layoutContainer(Container parent) + { + JComboBox cb = (JComboBox) parent; + if (!cb.isEditable()) + { + Rectangle bounds = parent.getBounds(); + arrowButton.setBounds(0, 0, bounds.width, bounds.height); + } + else + superLayout(parent); + } + + /** + * Calls the <code>layoutContainer(Container)</code> method in the super + * class. + * + * @param parent the container. + */ + public void superLayout(Container parent) + { + super.layoutContainer(parent); + } + } + + /** + * A listener used to handle property changes in the {@link JComboBox} + * component, to ensure that the UI delegate accurately reflects the current + * state in the rendering onscreen. + */ + public class MetalPropertyChangeListener + extends BasicComboBoxUI.PropertyChangeHandler + { + /** + * Creates a new listener. + */ + public MetalPropertyChangeListener() + { + // Nothing to do here. + } + + /** + * Handles a property change event, updating the UI components as + * appropriate. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals("editable")) + editablePropertyChanged(e); + super.propertyChange(e); + } + } /** + * A popup menu for the combo-box. + * + * @see #createPopup() + * + * @deprecated 1.4 + */ + public class MetalComboPopup extends BasicComboPopup + { + /** + * Creates a new popup. + * + * @param cBox the combo box. + */ + public MetalComboPopup(JComboBox cBox) + { + super(cBox); + } + + public void delegateFocus(MouseEvent e) + { + super.delegateFocus(e); + } + } + + /** * Constructs a new instance of MetalComboBoxUI. */ public MetalComboBoxUI() @@ -68,19 +182,135 @@ public class MetalComboBoxUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalComboBoxUI instance; - if (o == null) + return new MetalComboBoxUI(); + } + + /** + * Creates an editor for the combo box. + * + * @return An editor. + */ + protected ComboBoxEditor createEditor() + { + return new MetalComboBoxEditor.UIResource(); + } + + /** + * Creates a popup for the combo box. + * + * @return A popup. + */ + protected ComboPopup createPopup() + { + return new MetalComboPopup(comboBox); + } + + /** + * Creates a new button for use in rendering the JComboBox. + * + * @return A button. + */ + protected JButton createArrowButton() + { + JButton button = new MetalComboBoxButton(comboBox, new MetalComboBoxIcon(), + new CellRendererPane(), listBox); + button.setMargin(new Insets(0, 1, 1, 3)); + return button; + } + + /** + * Creates a new property change listener. + * + * @return A new property change listener. + */ + public PropertyChangeListener createPropertyChangeListener() + { + return new MetalPropertyChangeListener(); + } + + public void paint(Graphics g, JComponent c) + { + // do nothing, the button and text field are painted elsewhere + } + + /** + * Updates the button and text field to reflect a change in the 'editable' + * property. + * + * @param e the event. + * + * @deprecated 1.4 + */ + protected void editablePropertyChanged(PropertyChangeEvent e) + { + if (arrowButton instanceof MetalComboBoxButton) + { + MetalComboBoxButton b = (MetalComboBoxButton) arrowButton; + b.setIconOnly(comboBox.isEditable()); + } + if (comboBox.isEditable()) { - instance = new MetalComboBoxUI(); - instances.put(component, instance); + arrowButton.setText(null); + if (editor != null) + editor.setVisible(true); } else - instance = (MetalComboBoxUI) o; - - return instance; + { + String text = ""; + Object selected = comboBox.getSelectedItem(); + if (selected != null) + text = selected.toString(); + arrowButton.setText(text); + if (editor != null) + editor.setVisible(true); + } + } + + /** + * Creates a new layout manager for the UI delegate. + * + * @return A new layout manager. + */ + protected LayoutManager createLayoutManager() + { + return new MetalComboBoxLayoutManager(); + } + + /** + * Not used in Classpath. + * + * @deprecated 1.4 + */ + protected void removeListeners() + { + // no longer used in JDK 1.4 + } + + /** + * Returns the minimum size for the combo. + * + * @param c the component + * + * @return The minimum size for the combo box. + */ + public Dimension getMinimumSize(JComponent c) + { + Dimension d = getDisplaySize(); + MetalComboBoxButton b = (MetalComboBoxButton) arrowButton; + Insets insets = b.getInsets(); + int insetsH = insets.top + insets.bottom; + int insetsW = insets.left + insets.right; + if (!comboBox.isEditable()) + { + Icon icon = b.getComboIcon(); + int iconWidth = icon.getIconWidth() + 6; + return new Dimension(d.width + insetsW + iconWidth, d.height + insetsH); + } + else + // FIXME: the following dimensions pass most of the Mauve tests, but + // I don't yet understand the logic behind this...it is probably wrong + return new Dimension(d.width + insetsW + (d.height + insetsH) - 4, + d.height + insetsH + 1); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java index 00870545bc6..ecbb76e6e7a 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java @@ -39,9 +39,13 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JInternalFrame; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicDesktopIconUI; +/** + * A UI delegate for the {@link JInternalFrame.JDesktopIcon} component. + */ public class MetalDesktopIconUI extends BasicDesktopIconUI { @@ -51,7 +55,7 @@ public class MetalDesktopIconUI private static MetalDesktopIconUI instance = null; /** - * Constructs a new instance of MetalDesktopIconUI. + * Constructs a new instance of <code>MetalDesktopIconUI</code>. */ public MetalDesktopIconUI() { @@ -59,11 +63,11 @@ public class MetalDesktopIconUI } /** - * Returns an instance of MetalDesktopIconUI. + * Returns a shared instance of <code>MetalDesktopIconUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalDesktopIconUI + * @return A shared instance of <code>MetalDesktopIconUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java new file mode 100644 index 00000000000..3a2e1c13508 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -0,0 +1,430 @@ +/* MetalFileChooserUI.java -- + Copyright (C) 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 javax.swing.plaf.metal; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JList; +import javax.swing.UIManager; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileSystemView; +import javax.swing.filechooser.FileView; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicFileChooserUI; + + +/** + * A UI delegate for the {@link JFileChooser} component. This class is only + * partially implemented and is not usable yet. + */ +public class MetalFileChooserUI extends BasicFileChooserUI +{ + /** + * A combo box model containing the selected directory and all its parent + * directories. + */ + protected class DirectoryComboBoxModel extends AbstractListModel + implements ComboBoxModel + { + /** Storage for the items in the model. */ + private List items; + + /** The index of the selected item. */ + private int selectedIndex; + + /** + * Creates a new model. + */ + public DirectoryComboBoxModel() + { + items = new java.util.ArrayList(); + selectedIndex = -1; + } + + /** + * Returns the number of items in the model. + * + * @return The number of items in the model. + */ + public int getSize() + { + return items.size(); + } + + /** + * Returns the item at the specified index. + * + * @param index the item index. + * + * @return The item. + */ + public Object getElementAt(int index) + { + return items.get(index); + } + + /** + * Returns the depth of the item at the given <code>index</code>. + * + * @param index the item index. + * + * @return The depth. + */ + public int getDepth(int index) + { + return Math.max(index, 0); + } + + /** + * Returns the selected item, or <code>null</code> if no item is selected. + * + * @return The selected item, or <code>null</code>. + */ + public Object getSelectedItem() + { + if (selectedIndex >= 0) + return items.get(selectedIndex); + else + return null; + } + + /** + * Sets the selected item. This clears all the directories from the + * existing list, and repopulates it with the new selected directory + * and all its parent directories. + * + * @param selectedDirectory the selected directory. + */ + public void setSelectedItem(Object selectedDirectory) + { + items.clear(); + FileSystemView fsv = getFileChooser().getFileSystemView(); + File parent = (File) selectedDirectory; + while (parent != null) + { + items.add(0, parent); + parent = fsv.getParentDirectory(parent); + } + selectedIndex = items.indexOf(selectedDirectory); + fireContentsChanged(this, 0, items.size() - 1); + } + + } + + /** + * Handles changes to the selection in the directory combo box. + */ + protected class DirectoryComboBoxAction extends AbstractAction + { + /** + * Creates a new action. + */ + protected DirectoryComboBoxAction() + { + // Nothing to do here. + } + + /** + * Handles the action event. + * + * @param e the event. + */ + public void actionPerformed(ActionEvent e) + { + JFileChooser fc = getFileChooser(); + fc.setCurrentDirectory((File) directoryModel.getSelectedItem()); + } + } + + /** + * A renderer for the files and directories in the file chooser. + */ + protected class FileRenderer extends DefaultListCellRenderer + { + + /** + * Creates a new renderer. + */ + protected FileRenderer() + { + // Nothing to do here. + } + + /** + * Returns a component that can render the specified value. + * + * @param list the list. + * @param value the value (a {@link File}). + * @param index the index. + * @param isSelected is the item selected? + * @param cellHasFocus does the item have the focus? + * + * @return The renderer. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileView v = getFileView(getFileChooser()); + File f = (File) value; + setText(v.getName(f)); + setIcon(v.getIcon(f)); + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + + if (cellHasFocus) + setBorder(UIManager.getBorder("List.focusCellHighlightBorder")); + else + setBorder(noFocusBorder); + return this; + } + } + + /** + * A combo box model for the file selection filters. + */ + protected class FilterComboBoxModel + extends AbstractListModel + implements ComboBoxModel, PropertyChangeListener + { + + /** Storage for the filters in the model. */ + protected FileFilter[] filters; + + /** The index of the selected file filter. */ + private int selectedIndex; + + /** + * Creates a new model. + */ + protected FilterComboBoxModel() + { + filters = new FileFilter[1]; + filters[0] = getAcceptAllFileFilter(getFileChooser()); + selectedIndex = 0; + } + + /** + * Handles property changes. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + { + selectedIndex = -1; + FileFilter selected = (FileFilter) e.getNewValue(); + for (int i = 0; i < filters.length; i++) + if (filters[i].equals(selected)) + selectedIndex = i; + fireContentsChanged(this, -1, -1); + } + else if (e.getPropertyName().equals( + JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) + { + // repopulate list + JFileChooser fc = getFileChooser(); + FileFilter[] choosableFilters = fc.getChoosableFileFilters(); + filters = choosableFilters; + fireContentsChanged(this, 0, filters.length); + } + } + + /** + * Sets the selected filter. + * + * @param filter the filter. + */ + public void setSelectedItem(Object filter) + { + // change the filter in the file chooser and let the property change + // event trigger the change to the selected item + getFileChooser().setFileFilter((FileFilter) filter); + } + + /** + * Returns the selected file filter. + * + * @return The selected file filter. + */ + public Object getSelectedItem() + { + if (selectedIndex >= 0) + return filters[selectedIndex]; + return null; + } + + /** + * Returns the number of items in the model. + * + * @return The number of items in the model. + */ + public int getSize() + { + return filters.length; + } + + /** + * Returns the item at the specified index. + * + * @param index the item index. + * + * @return The item at the specified index. + */ + public Object getElementAt(int index) + { + return filters[index]; + } + + } + + /** + * A renderer for the items in the file filter combo box. + */ + public class FilterComboBoxRenderer extends DefaultListCellRenderer + { + /** + * Creates a new renderer. + */ + public FilterComboBoxRenderer() + { + // Nothing to do here. + } + + /** + * Returns a component that can be used to paint the given value within + * the list. + * + * @param list the list. + * @param value the value (a {@link FileFilter}). + * @param index the item index. + * @param isSelected is the item selected? + * @param cellHasFocus does the list cell have focus? + * + * @return A component. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileFilter filter = (FileFilter) value; + return super.getListCellRendererComponent(list, filter.getDescription(), + index, isSelected, cellHasFocus); + } + } + + /** The model for the directory combo box. */ + DirectoryComboBoxModel directoryModel; + + /** + * A factory method that returns a UI delegate for the specified + * component. + * + * @param c the component (which should be a {@link JFileChooser}). + */ + public static ComponentUI createUI(JComponent c) + { + JFileChooser chooser = (JFileChooser) c; + return new MetalFileChooserUI(chooser); + } + + /** + * Creates a new instance of this UI delegate. + * + * @param filechooser the file chooser component. + */ + public MetalFileChooserUI(JFileChooser filechooser) + { + super(filechooser); + } + + /** + * Creates and returns a new instance of {@link DirectoryComboBoxModel}. + * + * @return A new instance of {@link DirectoryComboBoxModel}. + */ + protected MetalFileChooserUI.DirectoryComboBoxModel + createDirectoryComboBoxModel(JFileChooser fc) + { + return new DirectoryComboBoxModel(); + } + + /** + * Creates and returns a new instance of {@link FilterComboBoxModel}. + * + * @return A new instance of {@link FilterComboBoxModel}. + */ + protected FilterComboBoxModel createFilterComboBoxModel() + { + return new FilterComboBoxModel(); + } + + /** + * Creates and returns a new instance of {@link FilterComboBoxRenderer}. + * + * @return A new instance of {@link FilterComboBoxRenderer}. + */ + protected MetalFileChooserUI.FilterComboBoxRenderer + createFilterComboBoxRenderer() + { + return new FilterComboBoxRenderer(); + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index d8c77432653..6f4feccfc37 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -43,9 +43,11 @@ import java.awt.Component; import java.awt.Graphics; import java.io.Serializable; +import javax.swing.AbstractButton; import javax.swing.Icon; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; import javax.swing.JInternalFrame; import javax.swing.JRadioButton; import javax.swing.JRadioButtonMenuItem; @@ -75,6 +77,7 @@ public class MetalIconFactory implements Serializable */ public CheckBoxMenuItemIcon() { + // Nothing to do here. } /** @@ -137,6 +140,385 @@ public class MetalIconFactory implements Serializable } /** + * An icon used for the "detail view" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserDetailViewIcon() + */ + private static class FileChooserDetailViewIcon implements Icon, Serializable + { + + /** + * Creates a new icon. + */ + public FileChooserDetailViewIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // file 1 outline + g.drawLine(x + 2, y + 2, x + 5, y + 2); + g.drawLine(x + 6, y + 3, x + 6, y + 7); + g.drawLine(x + 2, y + 7, x + 6, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 7); + + // file 2 outline + g.drawLine(x + 2, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 11, x + 6, y + 15); + g.drawLine(x + 2, y + 15, x + 6, y + 15); + g.drawLine(x + 2, y + 10, x + 2, y + 15); + + // detail lines + g.drawLine(x + 8, y + 5, x + 15, y + 5); + g.drawLine(x + 8, y + 13, x + 15, y + 13); + + // fill files + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 3, 3, 4); + g.fillRect(x + 3, y + 11, 3, 4); + + // highlight files + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 4, y + 4, x + 4, y + 5); + g.drawLine(x + 4, y + 12, x + 4, y + 13); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "home folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserHomeFolderIcon() + */ + private static class FileChooserHomeFolderIcon implements Icon, Serializable + { + + /** + * Creates a new icon. + */ + public FileChooserHomeFolderIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // roof + g.drawLine(x + 1, y + 8, x + 8, y + 1); + g.drawLine(x + 8, y + 1, x + 15, y + 8); + + // base of house + g.drawLine(x + 3, y + 6, x + 3, y + 15); + g.drawLine(x + 3, y + 15, x + 13, y + 15); + g.drawLine(x + 13, y + 6, x + 13, y + 15); + + // door frame + g.drawLine(x + 6, y + 9, x + 6, y + 15); + g.drawLine(x + 6, y + 9, x + 10, y + 9); + g.drawLine(x + 10, y + 9, x + 10, y + 15); + + // chimney + g.drawLine(x + 11, y + 2, x + 11, y + 4); + g.drawLine(x + 12, y + 2, x + 12, y + 5); + + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // roof paint + int xx = x + 8; + for (int i = 0; i < 4; i++) + g.drawLine(xx - i, y + 2 + i, xx + i, y + 2 + i); + g.fillRect(x + 4, y + 6, 9, 2); + + // door knob + g.drawLine(x + 9, y + 12, x + 9, y + 12); + + // house paint + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.drawLine(x + 4, y + 8, x + 12, y + 8); + g.fillRect(x + 4, y + 9, 2, 6); + g.fillRect(x + 11, y + 9, 2, 6); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "list view" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserListViewIcon() + */ + private static class FileChooserListViewIcon implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserListViewIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // file 1 outline + g.drawLine(x + 2, y + 2, x + 5, y + 2); + g.drawLine(x + 6, y + 3, x + 6, y + 7); + g.drawLine(x + 2, y + 7, x + 6, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 7); + + // file 2 outline + g.drawLine(x + 2, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 11, x + 6, y + 15); + g.drawLine(x + 2, y + 15, x + 6, y + 15); + g.drawLine(x + 2, y + 10, x + 2, y + 15); + + // file 3 outline + g.drawLine(x + 10, y + 2, x + 13, y + 2); + g.drawLine(x + 14, y + 3, x + 14, y + 7); + g.drawLine(x + 10, y + 7, x + 14, y + 7); + g.drawLine(x + 10, y + 2, x + 10, y + 7); + + // file 4 outline + g.drawLine(x + 10, y + 10, x + 13, y + 10); + g.drawLine(x + 14, y + 11, x + 14, y + 15); + g.drawLine(x + 10, y + 15, x + 14, y + 15); + g.drawLine(x + 10, y + 10, x + 10, y + 15); + + g.drawLine(x + 8, y + 5, x + 8, y + 5); + g.drawLine(x + 8, y + 13, x + 8, y + 13); + g.drawLine(x + 16, y + 5, x + 16, y + 5); + g.drawLine(x + 16, y + 13, x + 16, y + 13); + + // fill files + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 3, 3, 4); + g.fillRect(x + 3, y + 11, 3, 4); + g.fillRect(x + 11, y + 3, 3, 4); + g.fillRect(x + 11, y + 11, 3, 4); + + // highlight files + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 4, y + 4, x + 4, y + 5); + g.drawLine(x + 4, y + 12, x + 4, y + 13); + g.drawLine(x + 12, y + 4, x + 12, y + 5); + g.drawLine(x + 12, y + 12, x + 12, y + 13); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "new folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserNewFolderIcon() + */ + private static class FileChooserNewFolderIcon implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserNewFolderIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + g.drawLine(x + 2, y + 5, x + 9, y + 5); + g.drawLine(x + 10, y + 6, x + 15, y + 6); + g.drawLine(x + 15, y + 5, x + 15, y + 14); + g.drawLine(x + 2, y + 14, x + 15, y + 14); + g.drawLine(x + 1, y + 6, x + 1, y + 14); + + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 11, y + 3, x + 15, y + 3); + g.drawLine(x + 10, y + 4, x + 15, y + 4); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 7, 7, 7); + g.fillRect(x + 10, y + 8, 5, 6); + g.drawLine(x + 10, y + 5, x + 14, y + 5); + + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 10, y + 7, x + 14, y + 7); + g.drawLine(x + 2, y + 6, x + 9, y + 6); + g.drawLine(x + 2, y + 6, x + 2, y + 13); + g.setColor(savedColor); + } + } + + /** + * An icon used for the "up folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserNewFolderIcon() + */ + private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon + implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserUpFolderIcon() + { + // Nothing to do here. + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + + // draw the folder + super.paintIcon(c, g, x, y); + + // now draw the up arrow + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 8, y + 9, x + 8, y + 16); + int xx = x + 8; + for (int i = 0; i < 4; i++) + g.drawLine(xx - i, y + 9 + i, xx + i, y + 9 + i); + g.setColor(savedColor); + } + } + + /** * An icon representing a file (drawn as a piece of paper with the top-right * corner turned down). */ @@ -153,13 +535,15 @@ public class MetalIconFactory implements Serializable } /** - * Returns the height of the icon, in pixels. + * Returns the height of the icon, in pixels. The height returned is + * <code>16</code> plus the value returned by + * {@link #getAdditionalHeight()}. * * @return The height of the icon. */ public int getIconHeight() { - return 16; + return 16 + getAdditionalHeight(); } /** @@ -192,9 +576,11 @@ public class MetalIconFactory implements Serializable } /** - * Returns the additional height (???). + * Returns the additional height for the icon. The + * {@link #getIconHeight()} method adds this value to the icon height it + * returns. Subclasses can override this method to adjust the icon height. * - * @return The additional height. + * @return The additional height (<code>0</code> unless overridden). */ public int getAdditionalHeight() { @@ -228,13 +614,15 @@ public class MetalIconFactory implements Serializable } /** - * Returns the height of the icon, in pixels. + * Returns the height of the icon, in pixels. The height returned is + * <code>16</code> plus the value returned by + * {@link #getAdditionalHeight()}. * * @return The height of the icon. */ public int getIconHeight() { - return 16; + return 16 + getAdditionalHeight(); } /** @@ -265,9 +653,11 @@ public class MetalIconFactory implements Serializable } /** - * Returns the additional height (???). + * Returns the additional height for the icon. The + * {@link #getIconHeight()} method adds this value to the icon height it + * returns. Subclasses can override this method to adjust the icon height. * - * @return The additional height. + * @return The additional height (<code>0</code> unless overridden). */ public int getAdditionalHeight() { @@ -285,29 +675,77 @@ public class MetalIconFactory implements Serializable } } - + /** - * An {@link Icon} implementation for {@link JCheckBox}es in the - * Metal Look & Feel. - * - * @author Roman Kennke (roman@kennke.org) + * An icon used by the {@link MetalInternalFrameUI} class when the frame + * is displayed as a palette. + * + * @since 1.3 */ - static class RadioButtonIcon - implements Icon, UIResource, Serializable + public static class PaletteCloseIcon + implements Icon, Serializable, UIResource { /** - * Draws the check in the RadioButton. - * - * @param c the component to draw on - * @param g the Graphics context to draw with + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. */ - protected void drawCheck(Component c, Graphics g) + public int getIconWidth() { - g.setColor(MetalLookAndFeel.getBlack()); - g.fillRect(4, 3, 4, 6); - g.drawLine(3, 4, 3, 7); - g.drawLine(8, 4, 8, 7); + return 7; } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 7; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + AbstractButton button = (AbstractButton) c; + if (button.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.fillRect(x + 2, y + 2, 3, 3); + g.drawLine(x + 1, y, x + 1, y + 2); + g.drawLine(x, y + 1, x + 2, y + 1); + g.drawLine(x + 5, y, x + 5, y + 2); + g.drawLine(x + 4, y + 1, x + 6, y + 1); + g.drawLine(x + 1, y + 4, x + 1, y + 6); + g.drawLine(x, y + 5, x + 2, y + 5); + g.drawLine(x + 5, y + 4, x + 5, y + 6); + g.drawLine(x + 4, y + 5, x + 6, y + 5); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(x + 2, y + 6, x + 3, y + 5); + g.drawLine(x + 5, y + 3, x + 6, y + 2); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.setColor(savedColor); + } + } + + /** + * An {@link Icon} implementation for {@link JCheckBox}es in the + * Metal Look & Feel. + * + * @author Roman Kennke (roman@kennke.org) + */ + static class RadioButtonIcon implements Icon, UIResource, Serializable + { /** * Returns the width of the icon in pixels. @@ -330,70 +768,104 @@ public class MetalIconFactory implements Serializable } /** - * Paints the icon. This first paints the border of the RadioButton and - * if the CheckBox is selected it calls {@link #drawCheck} to draw - * the check. + * Paints the icon, taking into account whether or not the component is + * enabled, selected and/or armed. * - * @param c the Component to draw on (gets casted to JCheckBox) + * @param c the Component to draw on (must be an instance of + * {@link JRadioButton}) * @param g the Graphics context to draw with * @param x the X position * @param y the Y position */ - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color dark = MetalLookAndFeel.getControlDarkShadow(); - Color light = MetalLookAndFeel.getWhite(); - g.translate(x, y); - - // The light 'circle' - g.setColor(light); - g.drawLine(4, 1, 10, 1); - g.drawLine(2, 2, 3, 2); - g.drawLine(8, 2, 11, 2); - g.drawLine(2, 3, 2, 3); - g.drawLine(11, 2, 11, 9); - g.drawLine(1, 4, 1, 7); - g.drawLine(12, 4, 12, 7); - g.drawLine(2, 8, 2, 11); - g.drawLine(11, 8, 11, 9); - g.drawLine(10, 10, 10, 10); - g.drawLine(2, 11, 9, 11); - g.drawLine(4, 12, 7, 12); - - // The dark 'circle' - g.setColor(dark); - g.drawLine(4, 0, 7, 0); - g.drawLine(2, 1, 3, 1); - g.drawLine(8, 1, 9, 1); - g.drawLine(1, 2, 1, 3); - g.drawLine(10, 2, 10, 3); - g.drawLine(0, 4, 0, 7); - g.drawLine(11, 4, 11, 7); - g.drawLine(1, 8, 1, 9); - g.drawLine(10, 8, 10, 9); - g.drawLine(2, 10, 3, 10); - g.drawLine(8, 10, 9, 10); - g.drawLine(4, 11, 7, 11); - - JRadioButton rb = (JRadioButton) c; - if (rb.isSelected()) - drawCheck(c, g); - - g.translate(-x, -y); - } + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + JRadioButton b = (JRadioButton) c; + + // draw outer circle + if (b.isEnabled()) + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(x + 2, y + 1, x + 3, y + 1); + g.drawLine(x + 4, y, x + 7, y); + g.drawLine(x + 8, y + 1, x + 9, y + 1); + g.drawLine(x + 10, y + 2, x + 10, y + 3); + g.drawLine(x + 11, y + 4, x + 11, y + 7); + g.drawLine(x + 10, y + 8, x + 10, y + 9); + g.drawLine(x + 8, y + 10, x + 9, y + 10); + g.drawLine(x + 4, y + 11, x + 7, y + 11); + g.drawLine(x + 2, y + 10, x + 3, y + 10); + g.drawLine(x + 1, y + 9, x + 1, y + 8); + g.drawLine(x, y + 7, x, y + 4); + g.drawLine(x + 1, y + 2, x + 1, y + 3); + + if (b.getModel().isArmed()) + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 4, y + 1, x + 7, y + 1); + g.drawLine(x + 4, y + 10, x + 7, y + 10); + g.drawLine(x + 1, y + 4, x + 1, y + 7); + g.drawLine(x + 10, y + 4, x + 10, y + 7); + g.fillRect(x + 2, y + 2, 8, 8); + } + else + { + // only draw inner highlight if not filled + if (b.isEnabled()) + { + g.setColor(MetalLookAndFeel.getWhite()); + + g.drawLine(x + 2, y + 8, x + 2, y + 9); + g.drawLine(x + 1, y + 4, x + 1, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 3); + g.drawLine(x + 3, y + 2, x + 3, y + 2); + g.drawLine(x + 4, y + 1, x + 7, y + 1); + g.drawLine(x + 8, y + 2, x + 9, y + 2); + } + } + + // draw outer highlight + if (b.isEnabled()) + { + g.setColor(MetalLookAndFeel.getWhite()); + + // outer + g.drawLine(x + 10, y + 1, x + 10, y + 1); + g.drawLine(x + 11, y + 2, x + 11, y + 3); + g.drawLine(x + 12, y + 4, x + 12, y + 7); + g.drawLine(x + 11, y + 8, x + 11, y + 9); + g.drawLine(x + 10, y + 10, x + 10, y + 10); + g.drawLine(x + 8, y + 11, x + 9, y + 11); + g.drawLine(x + 4, y + 12, x + 7, y + 12); + g.drawLine(x + 2, y + 11, x + 3, y + 11); + } + + if (b.isSelected()) + { + if (b.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(x + 4, y + 3, x + 7, y + 3); + g.fillRect(x + 3, y + 4, 6, 4); + g.drawLine(x + 4, y + 8, x + 7, y + 8); + } + g.setColor(savedColor); + } } /** * An icon displayed for {@link JRadioButtonMenuItem} components. */ - private static class RadioButtonMenuItemIcon - implements Icon, Serializable + private static class RadioButtonMenuItemIcon implements Icon, Serializable { /** * Creates a new icon instance. */ public RadioButtonMenuItemIcon() - { + { + // Nothing to do here. } /** @@ -463,8 +935,7 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class HorizontalSliderThumbIcon - implements Icon, Serializable + private static class HorizontalSliderThumbIcon implements Icon, Serializable { /** @@ -472,6 +943,7 @@ public class MetalIconFactory implements Serializable */ public HorizontalSliderThumbIcon() { + // Nothing to do here. } /** @@ -505,13 +977,19 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { + boolean enabled = false; boolean focus = false; - if (c != null) - focus = c.hasFocus(); - // TODO: pick up the colors from the look and feel + if (c != null) + { + enabled = c.isEnabled(); + focus = c.hasFocus(); + } // draw the outline - g.setColor(Color.black); + if (enabled) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); g.drawLine(x + 1, y, x + 13, y); g.drawLine(x + 14, y + 1, x + 14, y + 7); g.drawLine(x + 14, y + 8, x + 7, y + 15); @@ -519,8 +997,11 @@ public class MetalIconFactory implements Serializable g.drawLine(x, y + 7, x, y + 1); // fill the icon - g.setColor(focus ? new Color(153, 153, 204) : new Color(204, 204, 204)); // medium - g.fillRect(x + 2, y + 2, 12, 7); + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x + 1, y + 2, 13, 7); g.drawLine(x + 2, y + 9, x + 12, y + 9); g.drawLine(x + 3, y + 10, x + 11, y + 10); g.drawLine(x + 4, y + 11, x + 10, y + 11); @@ -528,33 +1009,42 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 6, y + 13, x + 8, y + 13); g.drawLine(x + 7, y + 14, x + 7, y + 14); - // draw highlights - g.setColor(focus ? new Color(204, 204, 255) : new Color(255, 255, 255)); // light - g.drawLine(x + 1, y + 1, x + 13, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 8); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 2); - g.drawLine(x + 10, y + 2, x + 10, y + 2); - - g.drawLine(x + 4, y + 4, x + 4, y + 4); - g.drawLine(x + 8, y + 4, x + 8, y + 4); + // if the slider is enabled, draw dots and highlights + if (c.isEnabled()) + { + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 3, x + 3, y + 3); + g.drawLine(x + 7, y + 3, x + 7, y + 3); + g.drawLine(x + 11, y + 3, x + 11, y + 3); - g.drawLine(x + 2, y + 6, x + 2, y + 6); - g.drawLine(x + 6, y + 6, x + 6, y + 6); - g.drawLine(x + 10, y + 6, x + 10, y + 6); + g.drawLine(x + 5, y + 5, x + 5, y + 5); + g.drawLine(x + 9, y + 5, x + 9, y + 5); - // draw dots - g.setColor(focus ? new Color(102, 102, 153) : Color.black); // dark - g.drawLine(x + 3, y + 3, x + 3, y + 3); - g.drawLine(x + 7, y + 3, x + 7, y + 3); - g.drawLine(x + 11, y + 3, x + 11, y + 3); + g.drawLine(x + 3, y + 7, x + 3, y + 7); + g.drawLine(x + 7, y + 7, x + 7, y + 7); + g.drawLine(x + 11, y + 7, x + 11, y + 7); - g.drawLine(x + 5, y + 5, x + 5, y + 5); - g.drawLine(x + 9, y + 5, x + 9, y + 5); + // draw highlights + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControl()); + else + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x + 1, y + 1, x + 13, y + 1); + g.drawLine(x + 1, y + 2, x + 1, y + 8); + g.drawLine(x + 2, y + 2, x + 2, y + 2); + g.drawLine(x + 6, y + 2, x + 6, y + 2); + g.drawLine(x + 10, y + 2, x + 10, y + 2); + + g.drawLine(x + 4, y + 4, x + 4, y + 4); + g.drawLine(x + 8, y + 4, x + 8, y + 4); - g.drawLine(x + 3, y + 7, x + 3, y + 7); - g.drawLine(x + 7, y + 7, x + 7, y + 7); - g.drawLine(x + 11, y + 7, x + 11, y + 7); + g.drawLine(x + 2, y + 6, x + 2, y + 6); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.drawLine(x + 10, y + 6, x + 10, y + 6); + } } } @@ -563,7 +1053,7 @@ public class MetalIconFactory implements Serializable * An icon used for the 'close' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameCloseIcon implements Icon, Serializable + private static class InternalFrameCloseIcon implements Icon, Serializable { /** The icon size in pixels. */ private int size; @@ -601,25 +1091,53 @@ public class MetalIconFactory implements Serializable /** * Paints the icon. * - * @param c the component. + * @param c the component (an {@link JInternalFrame} is expected). * @param g the graphics device. * @param x the x-coordinate. * @param y the y-coordinate. */ public void paintIcon(Component c, Graphics g, int x, int y) { - // draw the gray areas first - g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + Color savedColor = g.getColor(); + AbstractButton b = (AbstractButton) c; + + // fill the interior + if (b.getModel().isPressed()) + // FIXME: also need to take into account whether the internal frame is + // selected + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 2, 10, 10); + + // draw the outline box and the cross + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + { + // FIXME: also need to take into account whether the internal frame is + // selected + boolean selected = true; + if (selected) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + } g.drawLine(x + 1, y + 1, x + 13, y + 1); g.drawLine(x + 1, y + 2, x + 1, y + 12); g.drawLine(x + 1, y + 13, x + 13, y + 13); g.drawLine(x + 13, y + 2, x + 13, y + 12); + g.drawLine(x + 2, y + 12, x + 2, y + 12); + g.drawLine(x + 12, y + 2, x + 12, y + 2); g.fillRect(x + 4, y + 4, 2, 2); - g.fillRect(x + 4, y + 9, 2, 2); - g.fillRect(x + 9, y + 4, 2, 2); - g.fillRect(x + 9, y + 9, 2, 2); - g.fillRect(x + 5, y + 5, 5, 5); + g.fillRect(x + 5, y + 5, 4, 4); + g.drawLine(x + 9, y + 4, x + 10, y + 4); + g.drawLine(x + 9, y + 4, x + 9, y + 5); + g.drawLine(x + 4, y + 9, x + 4, y + 10); + g.drawLine(x + 4, y + 9, x + 5, y + 9); + g.drawLine(x + 9, y + 8, x + 9, y + 10); + g.drawLine(x + 8, y + 9, x + 10, y + 9); g.setColor(MetalLookAndFeel.getBlack()); g.drawLine(x, y, x + 13, y); @@ -635,20 +1153,24 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 1, y + 14, x + 14, y + 14); g.drawLine(x + 14, y + 1, x + 14, y + 14); - g.drawLine(x + 5, y + 10, x + 5, y + 10); - g.drawLine(x + 6, y + 9, x + 7, y + 9); - g.drawLine(x + 10, y + 5, x + 10, y + 5); - g.drawLine(x + 9, y + 6, x + 9, y + 7); - g.drawLine(x + 10, y + 10, x + 11, y + 10); - g.drawLine(x + 10, y + 11, x + 10, y + 11); + if (!b.getModel().isPressed()) + { + g.drawLine(x + 5, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 9, x + 7, y + 9); + g.drawLine(x + 10, y + 5, x + 10, y + 5); + g.drawLine(x + 9, y + 6, x + 9, y + 7); + g.drawLine(x + 10, y + 10, x + 11, y + 10); + g.drawLine(x + 10, y + 11, x + 10, y + 11); + } + g.setColor(savedColor); } } /** * The icon displayed at the top-left corner of a {@link JInternalFrame}. */ - private static class InternalFrameDefaultMenuIcon - implements Icon, Serializable + private static class InternalFrameDefaultMenuIcon + implements Icon, Serializable { /** @@ -656,6 +1178,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameDefaultMenuIcon() { + // Nothing to do here. } /** @@ -718,8 +1241,8 @@ public class MetalIconFactory implements Serializable * maximise an internal frame, this icon will replace the 'maximise' icon to * provide a 'restore' option. */ - private static class InternalFrameAltMaximizeIcon - implements Icon, Serializable + private static class InternalFrameAltMaximizeIcon + implements Icon, Serializable { /** The icon size in pixels. */ private int size; @@ -764,14 +1287,23 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + // fill the small box interior + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 6, 7, 7); + + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 12, y + 1, x + 13, y + 1); g.drawLine(x + 11, y + 2, x + 12, y + 2); g.drawLine(x + 10, y + 3, x + 11, y + 3); @@ -803,10 +1335,16 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 13, y + 6, x + 13, y + 6); g.drawLine(x + 8, y + 7, x + 13, y + 7); g.drawLine(x + 6, y + 5, x + 6, y + 5); - g.drawLine(x + 2, y + 6, x + 6, y + 6); - g.drawLine(x + 2, y + 6, x + 2, y + 11); g.drawLine(x + 10, y + 8, x + 10, y + 13); g.drawLine(x + 1, y + 14, x + 10, y + 14); + + if (!b.getModel().isPressed()) + { + g.drawLine(x + 2, y + 6, x + 6, y + 6); + g.drawLine(x + 2, y + 6, x + 2, y + 11); + } + + g.setColor(savedColor); } } @@ -814,8 +1352,7 @@ public class MetalIconFactory implements Serializable * An icon used for the 'maximize' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameMaximizeIcon - implements Icon, Serializable + private static class InternalFrameMaximizeIcon implements Icon, Serializable { /** @@ -823,6 +1360,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameMaximizeIcon() { + // Nothing to do here. } /** @@ -855,14 +1393,22 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + // fill the interior + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 6, 7, 7); + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 9, y + 1, x + 10, y + 1); g.fillRect(x + 11, y + 1, 3, 3); g.fillRect(x + 12, y + 4, 2, 2); @@ -897,9 +1443,12 @@ public class MetalIconFactory implements Serializable // draw white g.setColor(MetalLookAndFeel.getWhite()); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 2, y + 7, x + 2, y + 9); - g.drawLine(x + 4, y + 11, x + 7, y + 8); + if (!b.getModel().isPressed()) + { + g.drawLine(x + 2, y + 6, x + 5, y + 6); + g.drawLine(x + 2, y + 7, x + 2, y + 9); + g.drawLine(x + 4, y + 11, x + 7, y + 8); + } g.drawLine(x + 1, y + 14, x + 10, y + 14); g.drawLine(x + 10, y + 5, x + 10, y + 13); @@ -908,14 +1457,14 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 11, y + 4, x + 11, y + 5); g.drawLine(x + 13, y + 6, x + 14, y + 6); g.drawLine(x + 14, y + 1, x + 14, y + 5); + g.setColor(savedColor); } } /** * An icon used in the title frame of a {@link JInternalFrame}. */ - private static class InternalFrameMinimizeIcon - implements Icon, Serializable + private static class InternalFrameMinimizeIcon implements Icon, Serializable { /** @@ -923,6 +1472,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameMinimizeIcon() { + // Nothing to do here. } /** @@ -955,14 +1505,17 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + // FIXME: here the color depends on whether or not the internal frame + // is selected + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 12, y + 1, x + 13, y + 1); g.drawLine(x + 11, y + 2, x + 12, y + 2); g.drawLine(x + 10, y + 3, x + 11, y + 3); @@ -993,10 +1546,21 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 11, y + 4, x + 13, y + 2); g.drawLine(x + 13, y + 6, x + 13, y + 6); g.drawLine(x + 8, y + 7, x + 13, y + 7); - g.drawLine(x + 2, y + 9, x + 4, y + 9); - g.drawLine(x + 2, y + 10, x + 2, y + 11); g.drawLine(x + 7, y + 9, x + 7, y + 13); g.drawLine(x + 1, y + 14, x + 7, y + 14); + + if (b.getModel().isPressed()) + { + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.fillRect(x + 2, y + 9, 3, 3); + } + else + { + g.drawLine(x + 2, y + 9, x + 4, y + 9); + g.drawLine(x + 2, y + 10, x + 2, y + 11); + } + + g.setColor(savedColor); } } @@ -1004,13 +1568,14 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class VerticalSliderThumbIcon implements Icon, Serializable + private static class VerticalSliderThumbIcon implements Icon, Serializable { /** * Creates a new instance. */ public VerticalSliderThumbIcon() { + // Nothing to do here. } /** @@ -1045,13 +1610,19 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { + boolean enabled = false; boolean focus = false; - if (c != null) - focus = c.hasFocus(); - // TODO: pick up the colors from the look and feel + if (c != null) + { + enabled = c.isEnabled(); + focus = c.hasFocus(); + } // draw the outline - g.setColor(Color.black); + if (enabled) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); g.drawLine(x + 1, y, x + 7, y); g.drawLine(x + 8, y, x + 15, y + 7); g.drawLine(x + 14, y + 8, x + 8, y + 14); @@ -1059,8 +1630,11 @@ public class MetalIconFactory implements Serializable g.drawLine(x, y + 13, x, y + 1); // fill the icon - g.setColor(focus ? new Color(153, 153, 204) : new Color(204, 204, 204)); // medium - g.fillRect(x + 2, y + 2, 7, 12); + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x + 2, y + 1, 7, 13); g.drawLine(x + 9, y + 2, x + 9, y + 12); g.drawLine(x + 10, y + 3, x + 10, y + 11); g.drawLine(x + 11, y + 4, x + 11, y + 10); @@ -1068,41 +1642,51 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 13, y + 6, x + 13, y + 8); g.drawLine(x + 14, y + 7, x + 14, y + 7); - // draw highlights - g.setColor(focus ? new Color(204, 204, 255) : new Color(255, 255, 255)); // light - g.drawLine(x + 1, y + 1, x + 8, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 13); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 6, x + 2, y + 6); - g.drawLine(x + 2, y + 10, x + 2, y + 10); + // if the slider is enabled, draw dots and highlights + if (enabled) + { + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 3, x + 3, y + 3); + g.drawLine(x + 3, y + 7, x + 3, y + 7); + g.drawLine(x + 3, y + 11, x + 3, y + 11); - g.drawLine(x + 4, y + 4, x + 4, y + 4); - g.drawLine(x + 4, y + 8, x + 4, y + 8); + g.drawLine(x + 5, y + 5, x + 5, y + 5); + g.drawLine(x + 5, y + 9, x + 5, y + 9); - g.drawLine(x + 6, y + 2, x + 6, y + 2); - g.drawLine(x + 6, y + 6, x + 6, y + 6); - g.drawLine(x + 6, y + 10, x + 6, y + 10); + g.drawLine(x + 7, y + 3, x + 7, y + 3); + g.drawLine(x + 7, y + 7, x + 7, y + 7); + g.drawLine(x + 7, y + 11, x + 7, y + 11); - // draw dots - g.setColor(focus ? new Color(102, 102, 153) : Color.black); // dark - g.drawLine(x + 3, y + 3, x + 3, y + 3); - g.drawLine(x + 3, y + 7, x + 3, y + 7); - g.drawLine(x + 3, y + 11, x + 3, y + 11); + // draw highlights + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControl()); + else + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x + 1, y + 1, x + 8, y + 1); + g.drawLine(x + 1, y + 2, x + 1, y + 13); + g.drawLine(x + 2, y + 2, x + 2, y + 2); + g.drawLine(x + 2, y + 6, x + 2, y + 6); + g.drawLine(x + 2, y + 10, x + 2, y + 10); - g.drawLine(x + 5, y + 5, x + 5, y + 5); - g.drawLine(x + 5, y + 9, x + 5, y + 9); + g.drawLine(x + 4, y + 4, x + 4, y + 4); + g.drawLine(x + 4, y + 8, x + 4, y + 8); - g.drawLine(x + 7, y + 3, x + 7, y + 3); - g.drawLine(x + 7, y + 7, x + 7, y + 7); - g.drawLine(x + 7, y + 11, x + 7, y + 11); + g.drawLine(x + 6, y + 2, x + 6, y + 2); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.drawLine(x + 6, y + 10, x + 6, y + 10); + + } } } - + /** * A tree control icon. This icon can be in one of two states: expanded and * collapsed. */ - public static class TreeControlIcon implements Icon, Serializable + public static class TreeControlIcon implements Icon, Serializable { /** ???. */ @@ -1235,19 +1819,21 @@ public class MetalIconFactory implements Serializable /** * A tree folder icon. */ - public static class TreeFolderIcon extends FolderIcon16 + public static class TreeFolderIcon extends FolderIcon16 { /** * Creates a new instance. */ public TreeFolderIcon() - { + { + // Nothing to do here. } /** - * Returns the additional height (???). + * Returns the additional height for this icon, in this case <code>2</code> + * pixels. * - * @return The additional height. + * @return <code>2</code>. */ public int getAdditionalHeight() { @@ -1268,19 +1854,21 @@ public class MetalIconFactory implements Serializable /** * A tree leaf icon. */ - public static class TreeLeafIcon extends FileIcon16 + public static class TreeLeafIcon extends FileIcon16 { /** * Creates a new instance. */ public TreeLeafIcon() { + // Nothing to do here. } /** - * Returns the additional height (???). + * Returns the additional height for this icon, in this case <code>4</code> + * pixels. * - * @return The additional height. + * @return <code>4</code>. */ public int getAdditionalHeight() { @@ -1297,16 +1885,310 @@ public class MetalIconFactory implements Serializable return 2; } } + + /** + * An icon representing a hard disk. + * + * @see MetalIconFactory#getTreeHardDriveIcon() + */ + private static class TreeHardDriveIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeHardDriveIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 1, y + 4, x + 1, y + 5); + g.drawLine(x + 14, y + 4, x + 14, y + 5); + g.drawLine(x + 1, y + 7, x + 1, y + 8); + g.drawLine(x + 14, y + 7, x + 14, y + 8); + g.drawLine(x + 1, y + 10, x + 1, y + 11); + g.drawLine(x + 14, y + 10, x + 14, y + 11); + + g.drawLine(x + 2, y + 3, x + 3, y + 3); + g.drawLine(x + 12, y + 3, x + 13, y + 3); + g.drawLine(x + 2, y + 6, x + 3, y + 6); + g.drawLine(x + 12, y + 6, x + 13, y + 6); + g.drawLine(x + 2, y + 9, x + 3, y + 9); + g.drawLine(x + 12, y + 9, x + 13, y + 9); + g.drawLine(x + 2, y + 12, x + 3, y + 12); + g.drawLine(x + 12, y + 12, x + 13, y + 12); + + g.drawLine(x + 4, y + 2, x + 11, y + 2); + g.drawLine(x + 4, y + 7, x + 11, y + 7); + g.drawLine(x + 4, y + 10, x + 11, y + 10); + g.drawLine(x + 4, y + 13, x + 11, y + 13); + + g.setColor(MetalLookAndFeel.getWhite()); + g.fillRect(x + 4, y + 3, 2, 2); + g.drawLine(x + 6, y + 4, x + 6, y + 4); + g.drawLine(x + 7, y + 3, x + 9, y + 3); + g.drawLine(x + 8, y + 4, x + 8, y + 4); + g.drawLine(x + 11, y + 3, x + 11, y + 3); + g.fillRect(x + 2, y + 4, 2, 2); + g.fillRect(x + 2, y + 7, 2, 2); + g.fillRect(x + 2, y + 10, 2, 2); + g.drawLine(x + 4, y + 6, x + 4, y + 6); + g.drawLine(x + 4, y + 9, x + 4, y + 9); + g.drawLine(x + 4, y + 12, x + 4, y + 12); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 13, y + 4, x + 13, y + 4); + g.drawLine(x + 12, y + 5, x + 13, y + 5); + g.drawLine(x + 13, y + 7, x + 13, y + 7); + g.drawLine(x + 12, y + 8, x + 13, y + 8); + g.drawLine(x + 13, y + 10, x + 13, y + 10); + g.drawLine(x + 12, y + 11, x + 13, y + 11); + + g.drawLine(x + 10, y + 5, x + 10, y + 5); + g.drawLine(x + 7, y + 6, x + 7, y + 6); + g.drawLine(x + 9, y + 6, x + 9, y + 6); + g.drawLine(x + 11, y + 6, x + 11, y + 6); + + g.drawLine(x + 10, y + 8, x + 10, y + 8); + g.drawLine(x + 7, y + 9, x + 7, y + 9); + g.drawLine(x + 9, y + 9, x + 9, y + 9); + g.drawLine(x + 11, y + 9, x + 11, y + 9); + + g.drawLine(x + 10, y + 11, x + 10, y + 11); + g.drawLine(x + 7, y + 12, x + 7, y + 12); + g.drawLine(x + 9, y + 12, x + 9, y + 12); + g.drawLine(x + 11, y + 12, x + 11, y + 12); + + g.setColor(saved); + } + } + + /** + * An icon representing a floppy disk. + * + * @see MetalIconFactory#getTreeFloppyDriveIcon() + */ + private static class TreeFloppyDriveIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeFloppyDriveIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 1, y + 1, x + 13, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + 14); + g.drawLine(x + 1, y + 14, x + 14, y + 14); + g.drawLine(x + 14, y + 2, x + 14, y + 14); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 2, 12, 12); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(x + 5, y + 2, 6, 5); + g.drawLine(x + 4, y + 8, x + 11, y + 8); + g.drawLine(x + 3, y + 9, x + 3, y + 13); + g.drawLine(x + 12, y + 9, x + 12, y + 13); + + g.setColor(MetalLookAndFeel.getWhite()); + g.fillRect(x + 8, y + 3, 2, 3); + g.fillRect(x + 4, y + 9, 8, 5); + + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.drawLine(x + 5, y + 10, x + 9, y + 10); + g.drawLine(x + 5, y + 12, x + 8, y + 12); + + g.setColor(saved); + } + } + + /** + * An icon representing a computer. + * + * @see MetalIconFactory#getTreeComputerIcon() + */ + private static class TreeComputerIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeComputerIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 1, x + 12, y + 1); + g.drawLine(x + 2, y + 2, x + 2, y + 8); + g.drawLine(x + 13, y + 2, x + 13, y + 8); + g.drawLine(x + 3, y + 9, x + 3, y + 9); + g.drawLine(x + 12, y + 9, x + 12, y + 9); + g.drawRect(x + 1, y + 10, 13, 4); + g.drawLine(x + 5, y + 3, x + 10, y + 3); + g.drawLine(x + 5, y + 8, x + 10, y + 8); + g.drawLine(x + 4, y + 4, x + 4, y + 7); + g.drawLine(x + 11, y + 4, x + 11, y + 7); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 5, y + 4, 6, 4); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 6, y + 12, x + 8, y + 12); + g.drawLine(x + 10, y + 12, x + 12, y + 12); + g.setColor(saved); + } + } + /** The icon returned by {@link #getCheckBoxIcon()}. */ + private static Icon checkBoxIcon; + + /** The icon returned by {@link #getCheckBoxMenuItemIcon()}. */ + private static Icon checkBoxMenuItemIcon; + + /** The icon returned by {@link #getFileChooserDetailViewIcon()}. */ + private static Icon fileChooserDetailViewIcon; + + /** The icon returned by {@link #getFileChooserHomeFolderIcon()}. */ + private static Icon fileChooserHomeFolderIcon; + + /** The icon returned by {@link #getFileChooserListViewIcon()}. */ + private static Icon fileChooserListViewIcon; + + /** The icon returned by {@link #getFileChooserNewFolderIcon()}. */ + private static Icon fileChooserNewFolderIcon; + + /** The icon returned by {@link #getFileChooserUpFolderIcon()}. */ + private static Icon fileChooserUpFolderIcon; + /** The cached RadioButtonIcon instance. */ private static RadioButtonIcon radioButtonIcon; + /** The icon returned by {@link #getRadioButtonMenuItemIcon()}. */ + private static Icon radioButtonMenuItemIcon; + + /** The icon returned by {@link #getInternalFrameDefaultMenuIcon()}. */ + private static Icon internalFrameDefaultMenuIcon; + + /** The icon returned by {@link #getTreeComputerIcon()}. */ + private static Icon treeComputerIcon; + + /** The icon instance returned by {@link #getTreeFloppyDriveIcon()}. */ + private static Icon treeFloppyDriveIcon; + + /** The icon instance returned by {@link #getTreeHardDriveIcon()}. */ + private static Icon treeHardDriveIcon; + /** * Creates a new instance. All the methods are static, so creating an * instance isn't necessary. */ public MetalIconFactory() - { + { + // Nothing to do here. } /** @@ -1318,7 +2200,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getCheckBoxIcon() { - return new MetalCheckBoxIcon(); + if (checkBoxIcon == null) + checkBoxIcon = new MetalCheckBoxIcon(); + return checkBoxIcon; } /** @@ -1329,7 +2213,69 @@ public class MetalIconFactory implements Serializable */ public static Icon getCheckBoxMenuItemIcon() { - return new CheckBoxMenuItemIcon(); + if (checkBoxMenuItemIcon == null) + checkBoxMenuItemIcon = new CheckBoxMenuItemIcon(); + return checkBoxMenuItemIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserDetailViewIcon() + { + if (fileChooserDetailViewIcon == null) + fileChooserDetailViewIcon = new FileChooserDetailViewIcon(); + return fileChooserDetailViewIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserHomeFolderIcon() + { + if (fileChooserHomeFolderIcon == null) + fileChooserHomeFolderIcon = new FileChooserHomeFolderIcon(); + return fileChooserHomeFolderIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserListViewIcon() + { + if (fileChooserListViewIcon == null) + fileChooserListViewIcon = new FileChooserListViewIcon(); + return fileChooserListViewIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserNewFolderIcon() + { + if (fileChooserNewFolderIcon == null) + fileChooserNewFolderIcon = new FileChooserNewFolderIcon(); + return fileChooserNewFolderIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserUpFolderIcon() + { + if (fileChooserUpFolderIcon == null) + fileChooserUpFolderIcon = new FileChooserUpFolderIcon(); + return fileChooserUpFolderIcon; } /** @@ -1351,7 +2297,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getRadioButtonMenuItemIcon() { - return new RadioButtonMenuItemIcon(); + if (radioButtonMenuItemIcon == null) + radioButtonMenuItemIcon = new RadioButtonMenuItemIcon(); + return radioButtonMenuItemIcon; } /** @@ -1386,7 +2334,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getInternalFrameDefaultMenuIcon() { - return new InternalFrameDefaultMenuIcon(); + if (internalFrameDefaultMenuIcon == null) + internalFrameDefaultMenuIcon = new InternalFrameDefaultMenuIcon(); + return internalFrameDefaultMenuIcon; } /** @@ -1475,4 +2425,40 @@ public class MetalIconFactory implements Serializable return new TreeControlIcon(isCollapsed); } + /** + * Returns a <code>16x16</code> icon representing a computer. + * + * @return The icon. + */ + public static Icon getTreeComputerIcon() + { + if (treeComputerIcon == null) + treeComputerIcon = new TreeComputerIcon(); + return treeComputerIcon; + } + + /** + * Returns a <code>16x16</code> icon representing a floppy disk. + * + * @return The icon. + */ + public static Icon getTreeFloppyDriveIcon() + { + if (treeFloppyDriveIcon == null) + treeFloppyDriveIcon = new TreeFloppyDriveIcon(); + return treeFloppyDriveIcon; + } + + /** + * Returns a <code>16x16</code> icon representing a hard disk. + * + * @return The icon. + */ + public static Icon getTreeHardDriveIcon() + { + if (treeHardDriveIcon == null) + treeHardDriveIcon = new TreeHardDriveIcon(); + return treeHardDriveIcon; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java index 20526eba8cd..33eb3491ad0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java @@ -38,30 +38,208 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Insets; import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JInternalFrame; +import javax.swing.JLabel; import javax.swing.JMenu; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.basic.BasicInternalFrameTitlePane; + /** - * The title pane for a {@link JInternalFrame}. + * The title pane for a {@link JInternalFrame} (see + * {@link MetalInternalFrameUI#createNorthPane(JInternalFrame)}). This can + * be displayed in two styles: one for regular internal frames, and the other + * for "palette" style internal frames. */ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane { + /** + * A property change handler that listens for changes to the + * <code>JInternalFrame.isPalette</code> property and updates the title + * pane as appropriate. + */ + class MetalInternalFrameTitlePanePropertyChangeHandler + extends PropertyChangeHandler + { + /** + * Creates a new handler. + */ + public MetalInternalFrameTitlePanePropertyChangeHandler() + { + super(); + } + + /** + * Handles <code>JInternalFrame.isPalette</code> property changes, with all + * other property changes being passed to the superclass. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("JInternalFrame.isPalette")) + { + if (e.getNewValue().equals(Boolean.TRUE)) + setPalette(true); + else + setPalette(false); + } + else + super.propertyChange(e); + } + } + + /** + * A layout manager for the title pane. + * + * @see #createLayout() + */ + private class MetalTitlePaneLayout implements LayoutManager + { + /** + * Creates a new <code>TitlePaneLayout</code> object. + */ + public MetalTitlePaneLayout() + { + // Do nothing. + } + + /** + * Adds a Component to the Container. + * + * @param name The name to reference the added Component by. + * @param c The Component to add. + */ + public void addLayoutComponent(String name, Component c) + { + // Do nothing. + } + + /** + * This method is called to lay out the children of the Title Pane. + * + * @param c The Container to lay out. + */ + public void layoutContainer(Container c) + { + + Dimension size = c.getSize(); + Insets insets = c.getInsets(); + int width = size.width - insets.left - insets.right; + int height = size.height - insets.top - insets.bottom; + + + int loc = width - insets.right - 1; + int top = insets.top + 2; + int buttonHeight = height - 4; + if (closeButton.isVisible()) + { + int buttonWidth = closeIcon.getIconWidth(); + loc -= buttonWidth + 2; + closeButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 6; + } + + if (maxButton.isVisible()) + { + int buttonWidth = maxIcon.getIconWidth(); + loc -= buttonWidth + 4; + maxButton.setBounds(loc, top, buttonWidth, buttonHeight); + } + + if (iconButton.isVisible()) + { + int buttonWidth = minIcon.getIconWidth(); + loc -= buttonWidth + 4; + iconButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 2; + } + + Dimension titlePreferredSize = title.getPreferredSize(); + title.setBounds(insets.left + 5, insets.top, + Math.min(titlePreferredSize.width, loc - insets.left - 10), + height); + + } + + /** + * This method returns the minimum size of the given Container given the + * children that it has. + * + * @param c The Container to get a minimum size for. + * + * @return The minimum size of the Container. + */ + public Dimension minimumLayoutSize(Container c) + { + return preferredLayoutSize(c); + } + + /** + * Returns the preferred size of the given Container taking + * into account the children that it has. + * + * @param c The Container to lay out. + * + * @return The preferred size of the Container. + */ + public Dimension preferredLayoutSize(Container c) + { + if (isPalette) + return new Dimension(paletteTitleHeight, paletteTitleHeight); + else + return new Dimension(22, 22); + } + + /** + * Removes a Component from the Container. + * + * @param c The Component to remove. + */ + public void removeLayoutComponent(Component c) + { + // Nothing to do here. + } + } + + /** A flag indicating whether the title pane uses the palette style. */ protected boolean isPalette; - protected Icon paletteCloseIcon = MetalIconFactory.getInternalFrameCloseIcon(16); + /** + * The icon used for the close button - this is fetched from the look and + * feel defaults using the key <code>InternalFrame.paletteCloseIcon</code>. + */ + protected Icon paletteCloseIcon; + + /** + * The height of the title pane when <code>isPalette</code> is + * <code>true</code>. This value is fetched from the look and feel defaults + * using the key <code>InternalFrame.paletteTitleHeight</code>. + */ + protected int paletteTitleHeight; + + /** The label used to display the title for the internal frame. */ + JLabel title; - protected int paletteTitleHeight = 12; - /** - * Creates a new title pane. + * Creates a new title pane for the specified frame. * * @param f the internal frame. */ @@ -81,6 +259,15 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane selectedTitleColor = MetalLookAndFeel.getWindowTitleBackground(); notSelectedTextColor = MetalLookAndFeel.getInactiveControlTextColor(); notSelectedTitleColor = MetalLookAndFeel.getWindowTitleInactiveBackground(); + + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + paletteTitleHeight = defaults.getInt("InternalFrame.paletteTitleHeight"); + paletteCloseIcon = defaults.getIcon("InternalFrame.paletteCloseIcon"); + minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16); + + title = new JLabel(frame.getTitle(), + MetalIconFactory.getInternalFrameDefaultMenuIcon(), + SwingConstants.LEFT); } /** @@ -93,6 +280,9 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane selectedTitleColor = null; notSelectedTextColor = null; notSelectedTitleColor = null; + paletteCloseIcon = null; + minIcon = null; + title = null; } /** @@ -128,44 +318,123 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane } /** - * Creates a layout manager for the components in the title pane. + * Adds the sub components of the title pane. + */ + protected void addSubComponents() + { + // FIXME: this method is probably overridden to only add the required + // buttons + add(title); + add(closeButton); + add(iconButton); + add(maxButton); + } + + /** + * Creates a new instance of {@link MetalTitlePaneLayout}. * - * @return A layout manager. - */ + * @return A new instance of {@link MetalTitlePaneLayout}. + */ protected LayoutManager createLayout() { - return new TitlePaneLayout() - { - public Dimension preferredLayoutSize(Container c) - { - return new Dimension(24, 24); - } - }; + return new MetalTitlePaneLayout(); } + /** + * Draws the title pane in the palette style. + * + * @param g the graphics device. + * + * @see #paintComponent(Graphics) + */ public void paintPalette(Graphics g) { - // FIXME: needs implementing - // most likely this is equivalent to paintComponent(g) when the isPalette - // flag is true + Color savedColor = g.getColor(); + Rectangle b = SwingUtilities.getLocalBounds(this); + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.fillRect(b.x, b.y, b.width, b.height); + MetalUtils.fillMetalPattern(this, g, b.x + 4, b.y + 2, b.width + - paletteCloseIcon.getIconWidth() - 13, b.height - 5, + MetalLookAndFeel.getPrimaryControlHighlight(), + MetalLookAndFeel.getBlack()); + + // draw a line separating the title pane from the frame content + Dimension d = getSize(); + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + + g.setColor(savedColor); } - + + /** + * Paints a representation of the current state of the internal frame. + * + * @param g the graphics device. + */ public void paintComponent(Graphics g) { - // probably need to check the isPalette flag here, if true pass over to - // paintPalette(Graphics) - super.paintComponent(g); - Dimension d = getSize(); - if (frame.isSelected()) - g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + Color savedColor = g.getColor(); + if (isPalette) + paintPalette(g); else - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + { + paintTitleBackground(g); + paintChildren(g); + Dimension d = getSize(); + if (frame.isSelected()) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // put a dot in each of the top corners + g.drawLine(0, 0, 0, 0); + g.drawLine(d.width - 1, 0, d.width - 1, 0); + + g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + + // draw the metal pattern + Rectangle b = title.getBounds(); + int startX = b.x + b.width + 5; + int endX = startX; + if (iconButton.isVisible()) + endX = Math.max(iconButton.getX(), endX); + else if (maxButton.isVisible()) + endX = Math.max(maxButton.getX(), endX); + else if (closeButton.isVisible()) + endX = Math.max(closeButton.getX(), endX); + endX -= 7; + if (endX > startX) + MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray); + } + g.setColor(savedColor); } + /** + * Sets the flag that controls whether the title pane is drawn in the + * palette style or the regular style. + * + * @param b the new value of the flag. + */ public void setPalette(boolean b) { - isPalette = b; + isPalette = b; + title.setVisible(!isPalette); + iconButton.setVisible(!isPalette && frame.isIconifiable()); + maxButton.setVisible(!isPalette && frame.isMaximizable()); + if (isPalette) + closeButton.setIcon(paletteCloseIcon); + else + closeButton.setIcon(closeIcon); + } + + /** + * Creates and returns a property change handler for the title pane. + * + * @return The property change handler. + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new MetalInternalFrameTitlePanePropertyChangeHandler(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java index 7c7cb92e549..6be573f4bac 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java @@ -38,24 +38,31 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JInternalFrame; -import javax.swing.border.EmptyBorder; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicInternalFrameUI; - +/** + * A UI delegate for the {@link JInternalFrame} component. + */ public class MetalInternalFrameUI extends BasicInternalFrameUI { - - /** The instances of MetalInternalFrameUI*/ - private static HashMap instances; + /** + * The key (<code>JInternalFrame.isPalette</code>) for the client property + * that controls whether the internal frame is displayed using the palette + * style. + */ + protected static String IS_PALETTE = "JInternalFrame.isPalette"; /** - * Constructs a new instance of MetalInternalFrameUI. + * Constructs a new instance of <code>MetalInternalFrameUI</code>. + * + * @param frame the frame. */ public MetalInternalFrameUI(JInternalFrame frame) { @@ -63,37 +70,96 @@ public class MetalInternalFrameUI } /** - * Returns an instance of MetalInternalFrameUI. + * Returns an instance of <code>MetalInternalFrameUI</code>. * - * @param component the component for which we return an UI instance + * @param component the internal frame. * - * @return an instance of MetalInternalFrameUI + * @return an instance of <code>MetalInternalFrameUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - - Object o = instances.get(component); - MetalInternalFrameUI instance; - if (o == null) - { - instance = new MetalInternalFrameUI((JInternalFrame) component); - instances.put(component, instance); - } - else - instance = (MetalInternalFrameUI) o; - - return instance; + return new MetalInternalFrameUI((JInternalFrame) component); } + /** + * Sets the fields and properties for the component. + * + * @param c the component. + */ + public void installUI(JComponent c) + { + super.installUI(c); + JInternalFrame f = (JInternalFrame) c; + boolean isPalette = false; + Boolean p = (Boolean) f.getClientProperty(IS_PALETTE); + if (p != null) + isPalette = p.booleanValue(); + setPalette(isPalette); + } + + /** + * Creates and returns the component that will be used for the north pane + * of the {@link JInternalFrame}. + * + * @param w the internal frame. + * + * @return A new instance of {@link MetalInternalFrameTitlePane}. + */ protected JComponent createNorthPane(JInternalFrame w) { titlePane = new MetalInternalFrameTitlePane(w); - titlePane.setBorder(new EmptyBorder(2, 2, 2, 2)); return titlePane; } - + /** + * Sets the state of the {@link JInternalFrame} to reflect whether or not + * it is using the palette style. When a frame is displayed as a palette, + * it uses a different border and the title pane is drawn differently. + * + * @param isPalette use the palette style? + */ + public void setPalette(boolean isPalette) + { + MetalInternalFrameTitlePane title = (MetalInternalFrameTitlePane) northPane; + title.setPalette(isPalette); + if (isPalette) + frame.setBorder(new MetalBorders.PaletteBorder()); + else + frame.setBorder(new MetalBorders.InternalFrameBorder()); + } + + /** A listener that is used to handle IS_PALETTE property changes. */ + private PropertyChangeListener paletteListener; + + /** + * Adds the required listeners. + */ + protected void installListeners() + { + super.installListeners(); + paletteListener = new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(IS_PALETTE)) + { + if (Boolean.TRUE.equals(e.getNewValue())) + setPalette(true); + else + setPalette(false); + } + } + }; + frame.addPropertyChangeListener(paletteListener); + } + + /** + * Removes the listeners used. + */ + protected void uninstallListeners() + { + super.uninstallListeners(); + frame.removePropertyChangeListener(IS_PALETTE, paletteListener); + paletteListener = null; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java index fe8a9e4e4ea..e4eaa71724d 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java @@ -50,17 +50,17 @@ import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicLabelUI; /** - * A UI delegate used for {@link JLabel}s in the {@link MetalLookAndFeel}. + * A UI delegate for the {@link JLabel} component. */ public class MetalLabelUI extends BasicLabelUI { - /** The shared UI instance for JLabels. */ + /** The shared instance of the UI delegate. */ protected static MetalLabelUI metalLabelUI; /** - * Constructs a new instance of MetalLabelUI. + * Constructs a new instance of <code>MetalLabelUI</code>. */ public MetalLabelUI() { @@ -68,11 +68,11 @@ public class MetalLabelUI } /** - * Returns an instance of MetalLabelUI. + * Returns a shared instance of <code>MetalLabelUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalLabelUI + * @return A shared instance of <code>MetalLabelUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index d9cf7c6220c..ec8014b17b3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -42,11 +42,14 @@ import java.awt.Color; import java.awt.Font; import java.awt.Insets; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.BorderUIResource.LineBorderUIResource; import javax.swing.plaf.basic.BasicLookAndFeel; @@ -69,7 +72,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - createDefaultTheme(); + if (theme == null) + createDefaultTheme(); } /** @@ -601,12 +605,21 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Sets the current theme for the look and feel. + * Sets the current theme for the look and feel. Note that the theme must be + * set <em>before</em> the look and feel is installed. To change the theme + * for an already running application that is using the + * {@link MetalLookAndFeel}, first set the theme with this method, then + * create a new instance of {@link MetalLookAndFeel} and install it in the + * usual way (see {@link UIManager#setLookAndFeel(LookAndFeel)}). * - * @param theme the theme. + * @param theme the theme (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>theme</code> is <code>null</code>. */ public static void setCurrentTheme(MetalTheme theme) { + if (theme == null) + throw new NullPointerException("Null 'theme' not permitted."); MetalLookAndFeel.theme = theme; } @@ -759,62 +772,206 @@ public class MetalLookAndFeel extends BasicLookAndFeel { super.initComponentDefaults(defaults); Object[] myDefaults = new Object[] { - "Button.background", new ColorUIResource(getControl()), + "Button.background", getControl(), "Button.border", MetalBorders.getButtonBorder(), - "Button.darkShadow", new ColorUIResource(getControlDarkShadow()), - "Button.disabledText", new ColorUIResource(getControlDisabled()), - "Button.focus", new ColorUIResource(getFocusColor()), + "Button.darkShadow", getControlDarkShadow(), + "Button.disabledText", getInactiveControlTextColor(), + "Button.focus", getFocusColor(), "Button.font", getControlTextFont(), - "Button.foreground", new ColorUIResource(getSystemTextColor()), - "Button.highlight", new ColorUIResource(getControlHighlight()), - "Button.light", new ColorUIResource(getControlHighlight()), - "Button.margin", new Insets(2, 14, 2, 14), - "Button.select", new ColorUIResource(getPrimaryControlShadow()), - "Button.shadow", new ColorUIResource(getPrimaryControlShadow()), - "CheckBox.background", new ColorUIResource(getControl()), + "Button.foreground", getControlTextColor(), + "Button.highlight", getControlHighlight(), + "Button.light", getControlHighlight(), + "Button.margin", new InsetsUIResource(2, 14, 2, 14), + "Button.select", getControlShadow(), + "Button.shadow", getControlShadow(), + + "CheckBox.background", getControl(), "CheckBox.border", MetalBorders.getButtonBorder(), + "CheckBox.disabledText", getInactiveControlTextColor(), + "CheckBox.focus", getFocusColor(), + "CheckBox.font", new FontUIResource("Dialog", Font.BOLD, 12), + "CheckBox.foreground", getControlTextColor(), "CheckBox.icon", new UIDefaults.ProxyLazyValue ("javax.swing.plaf.metal.MetalCheckBoxIcon"), "CheckBox.checkIcon", new UIDefaults.ProxyLazyValue ("javax.swing.plaf.metal.MetalCheckBoxIcon"), - "CheckBoxMenuItem.background", new ColorUIResource(getControl()), + "Checkbox.select", getControlShadow(), + + "CheckBoxMenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "CheckBoxMenuItem.acceleratorForeground", getAcceleratorForeground(), + "CheckBoxMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "CheckBoxMenuItem.background", getMenuBackground(), + "CheckBoxMenuItem.borderPainted", new Boolean(true), + "CheckBoxMenuItem.commandSound", "sounds/MenuItemCommand.wav", "CheckBoxMenuItem.checkIcon", MetalIconFactory.getCheckBoxMenuItemIcon(), - "ToolBar.background", new ColorUIResource(getControl()), - "Panel.background", new ColorUIResource(getControl()), - "Slider.background", new ColorUIResource(getControl()), - "OptionPane.background", new ColorUIResource(getControl()), - "ProgressBar.background", new ColorUIResource(getControl()), - "ScrollPane.border", new MetalBorders.ScrollPaneBorder(), - "TabbedPane.background", new ColorUIResource(getControl()), + "CheckBoxMenuItem.disabledForeground", getMenuDisabledForeground(), + "CheckBoxMenuItem.font", new FontUIResource("Dialog", Font.BOLD, 12), + "CheckBoxMenuItem.foreground", getMenuForeground(), + "CheckBoxMenuItem.selectionBackground", getMenuSelectedBackground(), + "CheckBoxMenuItem.selectionForeground", getMenuSelectedForeground(), + + "ColorChooser.background", getControl(), + "ColorChooser.foreground", getControlTextColor(), + "ColorChooser.rgbBlueMnemonic", new Integer(0), + "ColorChooser.rgbGreenMnemonic", new Integer(0), + "ColorChooser.rgbRedMnemonic", new Integer(0), + "ColorChooser.swatchesDefaultRecentColor", getControl(), + + "ComboBox.background", getControl(), + "ComboBox.buttonBackground", getControl(), + "ComboBox.buttonDarkShadow", getControlDarkShadow(), + "ComboBox.buttonHighlight", getControlHighlight(), + "ComboBox.buttonShadow", getControlShadow(), + "ComboBox.disabledBackground", getControl(), + "ComboBox.disabledForeground", getInactiveSystemTextColor(), + "ComboBox.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ComboBox.foreground", getControlTextColor(), + "ComboBox.selectionBackground", getPrimaryControlShadow(), + "ComboBox.selectionForeground", getControlTextColor(), + + "Desktop.background", getDesktopColor(), + + "DesktopIcon.background", getControl(), + "DesktopIcon.foreground", getControlTextColor(), + "DesktopIcon.width", new Integer(160), + "DesktopIcon.border", MetalBorders.getDesktopIconBorder(), + + "EditorPane.background", getWindowBackground(), + "EditorPane.caretForeground", getUserTextColor(), + "EditorPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "EditorPane.foreground", getUserTextColor(), + "EditorPane.inactiveForeground", getInactiveSystemTextColor(), + "EditorPane.selectionBackground", getTextHighlightColor(), + "EditorPane.selectionForeground", getHighlightedTextColor(), + + "FormattedTextField.background", getWindowBackground(), + "FormattedTextField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "FormattedTextField.caretForeground", getUserTextColor(), + "FormattedTextField.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "FormattedTextField.foreground", getUserTextColor(), + "FormattedTextField.inactiveBackground", getControl(), + "FormattedTextField.inactiveForeground", getInactiveSystemTextColor(), + "FormattedTextField.selectionBackground", getTextHighlightColor(), + "FormattedTextField.selectionForeground", getHighlightedTextColor(), + + "FileView.computerIcon", MetalIconFactory.getTreeComputerIcon(), + "FileView.directoryIcon", MetalIconFactory.getTreeFolderIcon(), + "FileView.fileIcon", MetalIconFactory.getTreeLeafIcon(), + "FileView.floppyDriveIcon", MetalIconFactory.getTreeFloppyDriveIcon(), + "FileView.hardDriveIcon", MetalIconFactory.getTreeHardDriveIcon(), + + "InternalFrame.activeTitleBackground", getWindowTitleBackground(), + "InternalFrame.activeTitleForeground", getWindowTitleForeground(), "InternalFrame.border", new MetalBorders.InternalFrameBorder(), + "InternalFrame.borderColor", getControl(), + "InternalFrame.borderDarkShadow", getControlDarkShadow(), + "InternalFrame.borderHighlight", getControlHighlight(), + "InternalFrame.borderLight", getControlHighlight(), + "InternalFrame.borderShadow", getControlShadow(), "InternalFrame.icon", MetalIconFactory.getInternalFrameDefaultMenuIcon(), "InternalFrame.closeIcon", MetalIconFactory.getInternalFrameCloseIcon(16), + "InternalFrame.inactiveTitleBackground", getWindowTitleInactiveBackground(), + "InternalFrame.inactiveTitleForeground", getWindowTitleInactiveForeground(), "InternalFrame.maximizeIcon", MetalIconFactory.getInternalFrameMaximizeIcon(16), "InternalFrame.iconifyIcon", MetalIconFactory.getInternalFrameMinimizeIcon(16), - "Label.background", new ColorUIResource(getControl()), + "InternalFrame.paletteBorder", new MetalBorders.PaletteBorder(), + "InternalFrame.paletteCloseIcon", new MetalIconFactory.PaletteCloseIcon(), + "InternalFrame.paletteTitleHeight", new Integer(11), + + "Label.background", getControl(), + "Label.disabledForeground", getInactiveSystemTextColor(), + "Label.disabledShadow", getControlShadow(), "Label.font", getControlTextFont(), - "Label.disabledForeground", new ColorUIResource(getInactiveControlTextColor()), - "Label.foreground", new ColorUIResource(getControlTextColor()), - "Menu.background", new ColorUIResource(getControl()), + "Label.foreground", getSystemTextColor(), + + "List.font", getControlTextFont(), + "List.background", getWindowBackground(), + "List.foreground", getUserTextColor(), + "List.selectionBackground", getTextHighlightColor(), + "List.selectionForeground", getHighlightedTextColor(), + "List.focusCellHighlightBorder", + new LineBorderUIResource(MetalLookAndFeel.getFocusColor()), + + "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "Menu.acceleratorForeground", getAcceleratorForeground(), + "Menu.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "Menu.background", getMenuBackground(), "Menu.border", new MetalBorders.MenuItemBorder(), "Menu.borderPainted", Boolean.TRUE, + "Menu.disabledForeground", getMenuDisabledForeground(), "Menu.font", getControlTextFont(), + "Menu.foreground", getMenuForeground(), "Menu.selectionBackground", getMenuSelectedBackground(), "Menu.selectionForeground", getMenuSelectedForeground(), - "MenuBar.background", new ColorUIResource(getControl()), + + "MenuBar.background", getMenuBackground(), "MenuBar.border", new MetalBorders.MenuBarBorder(), "MenuBar.font", getControlTextFont(), - "MenuItem.background", new ColorUIResource(getControl()), + "MenuBar.foreground", getMenuForeground(), + "MenuBar.highlight", getControlHighlight(), + "MenuBar.shadow", getControlShadow(), + + "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "MenuItem.acceleratorForeground", getAcceleratorForeground(), + "MenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "MenuItem.background", getMenuBackground(), "MenuItem.border", new MetalBorders.MenuItemBorder(), + "MenuItem.disabledForeground", getMenuDisabledForeground(), "MenuItem.font", getControlTextFont(), + "MenuItem.foreground", getMenuForeground(), "MenuItem.selectionBackground", getMenuSelectedBackground(), "MenuItem.selectionForeground", getMenuSelectedForeground(), - "Panel.background", new ColorUIResource(getControl()), + + "OptionPane.background", getControl(), + "OptionPane.errorDialog.border.background", new ColorUIResource(153, 51, 51), + "OptionPane.errorDialog.titlePane.background", new ColorUIResource(255, 153, 153), + "OptionPane.errorDialog.titlePane.foreground", new ColorUIResource(51, 0, 0), + "OptionPane.errorDialog.titlePane.shadow", new ColorUIResource(204, 102, 102), + "OptionPane.foreground", getControlTextColor(), + "OptionPane.messageForeground", getControlTextColor(), + "OptionPane.questionDialog.border.background", new ColorUIResource(51, 102, 51), + "OptionPane.questionDialog.titlePane.background", new ColorUIResource(153, 204, 153), + "OptionPane.questionDialog.titlePane.foreground", new ColorUIResource(0, 51, 0), + "OptionPane.questionDialog.titlePane.shadow", new ColorUIResource(102, 153, 102), + "OptionPane.warningDialog.border.background", new ColorUIResource(153, 102, 51), + "OptionPane.warningDialog.titlePane.background", new ColorUIResource(255, 204, 153), + "OptionPane.warningDialog.titlePane.foreground", new ColorUIResource(102, 51, 0), + "OptionPane.warningDialog.titlePane.shadow", new ColorUIResource(204, 153, 102), + + "Panel.background", getControl(), + "Panel.foreground", getUserTextColor(), + + "PasswordField.background", getWindowBackground(), + "PasswordField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "PasswordField.caretForeground", getUserTextColor(), + "PasswordField.foreground", getUserTextColor(), + "PasswordField.inactiveBackground", getControl(), + "PasswordField.inactiveForeground", getInactiveSystemTextColor(), + "PasswordField.selectionBackground", getTextHighlightColor(), + "PasswordField.selectionForeground", getHighlightedTextColor(), + + "PopupMenu.background", getMenuBackground(), + "PopupMenu.border", new MetalBorders.PopupMenuBorder(), + "PopupMenu.font", new FontUIResource("Dialog", Font.BOLD, 12), + "PopupMenu.foreground", getMenuForeground(), + + "ProgressBar.background", getControl(), + "ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), + "ProgressBar.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ProgressBar.foreground", getPrimaryControlShadow(), + "ProgressBar.selectionBackground", getPrimaryControlDarkShadow(), + "ProgressBar.selectionForeground", getControl(), + + "RadioButton.background", getControl(), + "RadioButton.darkShadow", getControlDarkShadow(), + "RadioButton.disabledText", getInactiveControlTextColor(), "RadioButton.icon", new UIDefaults.LazyValue() { @@ -823,80 +980,199 @@ public class MetalLookAndFeel extends BasicLookAndFeel return MetalIconFactory.getRadioButtonIcon(); } }, - + "RadioButton.focus", MetalLookAndFeel.getFocusColor(), + "RadioButton.font", MetalLookAndFeel.getControlTextFont(), + "RadioButton.foreground", getControlTextColor(), + "RadioButton.highlight", getControlHighlight(), + "RadioButton.light", getControlHighlight(), + "RadioButton.select", getControlShadow(), + "RadioButton.shadow", getControlShadow(), + + "RadioButtonMenuItem.acceleratorFont", new Font("Dialog", Font.PLAIN, 10), + "RadioButtonMenuItem.acceleratorForeground", getAcceleratorForeground(), + "RadioButtonMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "RadioButtonMenuItem.background", getMenuBackground(), "RadioButtonMenuItem.border", new MetalBorders.MenuItemBorder(), "RadioButtonMenuItem.borderPainted", Boolean.TRUE, "RadioButtonMenuItem.checkIcon", MetalIconFactory.getRadioButtonMenuItemIcon(), + "RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(), "RadioButtonMenuItem.font", MetalLookAndFeel.getControlTextFont(), + "RadioButtonMenuItem.foreground", getMenuForeground(), "RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2), "RadioButtonMenuItem.selectionBackground", MetalLookAndFeel.getMenuSelectedBackground(), "RadioButtonMenuItem.selectionForeground", MetalLookAndFeel.getMenuSelectedForeground(), - "ScrollBar.background", new ColorUIResource(getControl()), - "ScrollBar.shadow", new ColorUIResource(getControlShadow()), - "ScrollBar.thumb", new ColorUIResource(getPrimaryControlShadow()), - "ScrollBar.thumbDarkShadow", - new ColorUIResource(getPrimaryControlDarkShadow()), - "ScrollBar.thumbHighlight", - new ColorUIResource(getPrimaryControl()), + "ScrollBar.background", getControl(), + "ScrollBar.darkShadow", getControlDarkShadow(), + "ScrollBar.foreground", getControl(), + "ScrollBar.highlight", getControlHighlight(), + "ScrollBar.shadow", getControlShadow(), + "ScrollBar.thumb", getPrimaryControlShadow(), + "ScrollBar.thumbDarkShadow", getControlDarkShadow(), + "ScrollBar.thumbHighlight", getPrimaryControl(), + "ScrollBar.thumbShadow", getPrimaryControlDarkShadow(), + "ScrollBar.track", getControl(), + "ScrollBar.trackHighlight", getControlDarkShadow(), + "ScrollBar.width", new Integer(17), + + "ScrollPane.background", getControl(), + "ScrollPane.border", new MetalBorders.ScrollPaneBorder(), + "ScrollPane.foreground", getControlTextColor(), - "SplitPane.darkShadow", - new ColorUIResource(getControlDarkShadow()), - "SplitPane.highlight", - new ColorUIResource(getControlHighlight()), + "Separator.background", getSeparatorBackground(), + "Separator.foreground", getSeparatorForeground(), + "Separator.highlight", getControlHighlight(), + "Separator.shadow", getControlShadow(), + "Slider.background", getControl(), + "Slider.focus", getFocusColor(), "Slider.focusInsets", new InsetsUIResource(0, 0, 0, 0), + "Slider.foreground", getPrimaryControlShadow(), + "Slider.highlight", getControlHighlight(), "Slider.horizontalThumbIcon", MetalIconFactory.getHorizontalSliderThumbIcon(), + "Slider.majorTickLength", new Integer(6), + "Slider.shadow", getControlShadow(), + "Slider.trackWidth", new Integer(7), "Slider.verticalThumbIcon", MetalIconFactory.getVerticalSliderThumbIcon(), - "Slider.trackWidth", new Integer(7), - "Slider.majorTickLength", new Integer(6), - + + "Spinner.background", getControl(), + "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12), + "Spinner.foreground", getControl(), + + "SplitPane.background", getControl(), + "SplitPane.darkShadow", getControlDarkShadow(), + "SplitPane.dividerFocusColor", getPrimaryControl(), + "SplitPane.highlight", getControlHighlight(), + "SplitPane.shadow", getControlShadow(), + + "SplitPaneDivider.draggingColor", Color.DARK_GRAY, + + "TabbedPane.background", getControlShadow(), + "TabbedPane.darkShadow", getControlDarkShadow(), + "TabbedPane.focus", getPrimaryControlDarkShadow(), "TabbedPane.font", new FontUIResource("Dialog", Font.BOLD, 12), - "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + "TabbedPane.foreground", getControlTextColor(), + "TabbedPane.highlight", getControlHighlight(), + "TabbedPane.light", getControl(), + "TabbedPane.selected", getControl(), + "TabbedPane.selectHighlight", getControlHighlight(), "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1), + "TabbedPane.shadow", getControlShadow(), + "TabbedPane.tabAreaBackground", getControl(), "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), - - "ToggleButton.background", new ColorUIResource(getControl()), - "ToggleButton.border", MetalBorders.getButtonBorder(), - "ToggleButton.darkShadow", new ColorUIResource(getControlDarkShadow()), - "ToggleButton.disabledText", new ColorUIResource(getControlDisabled()), - "ToggleButton.focus", new ColorUIResource(getFocusColor()), + "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + + "Table.background", getWindowBackground(), + "Table.focusCellBackground", getWindowBackground(), + "Table.focusCellForeground", getControlTextColor(), + "Table.foreground", getControlTextColor(), + "Table.focusCellHighlightBorder", + new BorderUIResource.LineBorderUIResource(getControlShadow()), + "Table.focusCellBackground", getWindowBackground(), + "Table.gridColor", getControlDarkShadow(), + "Table.selectionBackground", new ColorUIResource(204, 204, 255), + "Table.selectionForeground", new ColorUIResource(0, 0, 0), + + "TableHeader.background", getControl(), + "TableHeader.cellBorder", new MetalBorders.TableHeaderBorder(), + "TableHeader.foreground", getControlTextColor(), + + "TextArea.background", getWindowBackground(), + "TextArea.caretForeground", getUserTextColor(), + "TextArea.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextArea.foreground", getUserTextColor(), + "TextArea.inactiveForeground", getInactiveSystemTextColor(), + "TextArea.selectionBackground", getTextHighlightColor(), + "TextArea.selectionForeground", getHighlightedTextColor(), + + "TextField.background", getWindowBackground(), + "TextField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "TextField.caretForeground", getUserTextColor(), + "TextField.darkShadow", getControlDarkShadow(), + "TextField.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextField.foreground", getUserTextColor(), + "TextField.highlight", getControlHighlight(), + "TextField.inactiveBackground", getControl(), + "TextField.inactiveForeground", getInactiveSystemTextColor(), + "TextField.light", getControlHighlight(), + "TextField.selectionBackground", getTextHighlightColor(), + "TextField.selectionForeground", getHighlightedTextColor(), + "TextField.shadow", getControlShadow(), + + "TextPane.background", getWindowBackground(), + "TextPane.caretForeground", getUserTextColor(), + "TextPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextPane.foreground", getUserTextColor(), + "TextPane.inactiveForeground", getInactiveSystemTextColor(), + "TextPane.selectionBackground", getTextHighlightColor(), + "TextPane.selectionForeground", getHighlightedTextColor(), + + "TitledBorder.font", new FontUIResource("Dialog", Font.BOLD, 12), + "TitledBorder.titleColor", getSystemTextColor(), + + "ToggleButton.background", getControl(), + "ToggleButton.border", MetalBorders.getToggleButtonBorder(), + "ToggleButton.darkShadow", getControlDarkShadow(), + "ToggleButton.disabledText", getInactiveControlTextColor(), + "ToggleButton.focus", getFocusColor(), "ToggleButton.font", getControlTextFont(), - "ToggleButton.foreground", new ColorUIResource(getSystemTextColor()), - "ToggleButton.highlight", new ColorUIResource(getControlHighlight()), - "ToggleButton.light", new ColorUIResource(getControlHighlight()), - "ToggleButton.margin", new Insets(2, 14, 2, 14), - "ToggleButton.select", new ColorUIResource(getPrimaryControlShadow()), - "ToggleButton.shadow", new ColorUIResource(getPrimaryControlShadow()), - - "Tree.openIcon", MetalIconFactory.getTreeFolderIcon(), + "ToggleButton.foreground", getControlTextColor(), + "ToggleButton.highlight", getControlHighlight(), + "ToggleButton.light", getControlHighlight(), + "ToggleButton.margin", new InsetsUIResource(2, 14, 2, 14), + "ToggleButton.select", getControlShadow(), + "ToggleButton.shadow", getControlShadow(), + + "ToolBar.background", getMenuBackground(), + "ToolBar.darkShadow", getControlDarkShadow(), + "ToolBar.dockingBackground", getMenuBackground(), + "ToolBar.dockingForeground", getPrimaryControlDarkShadow(), + "ToolBar.floatingBackground", getMenuBackground(), + "ToolBar.floatingForeground", getPrimaryControl(), + "ToolBar.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ToolBar.foreground", getMenuForeground(), + "ToolBar.highlight", getControlHighlight(), + "ToolBar.light", getControlHighlight(), + "ToolBar.shadow", getControlShadow(), + "ToolBar.border", new MetalBorders.ToolBarBorder(), + + "ToolTip.background", getPrimaryControl(), + "ToolTip.backgroundInactive", getControl(), + "ToolTip.border", new BorderUIResource.LineBorderUIResource(getPrimaryControlDarkShadow(), 1), + "ToolTip.borderInactive", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), + "ToolTip.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "ToolTip.foreground", getPrimaryControlInfo(), + "ToolTip.foregroundInactive", getControlDarkShadow(), + + "Tree.background", getWindowBackground(), "Tree.closedIcon", MetalIconFactory.getTreeFolderIcon(), - "Tree.leafIcon", MetalIconFactory.getTreeLeafIcon(), "Tree.collapsedIcon", MetalIconFactory.getTreeControlIcon(true), "Tree.expandedIcon", MetalIconFactory.getTreeControlIcon(false), - "Tree.font", new FontUIResource(new Font("Helvetica", Font.PLAIN, 12)), - "Tree.background", new ColorUIResource(Color.white), - "Tree.foreground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.hash", new ColorUIResource(new Color(204, 204, 255)), + "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "Tree.foreground", getUserTextColor(), + "Tree.hash", getPrimaryControl(), + "Tree.leafIcon", MetalIconFactory.getTreeLeafIcon(), "Tree.leftChildIndent", new Integer(7), + "Tree.line", getPrimaryControl(), + "Tree.openIcon", MetalIconFactory.getTreeFolderIcon(), "Tree.rightChildIndent", new Integer(13), "Tree.rowHeight", new Integer(20), "Tree.scrollsOnExpand", Boolean.TRUE, - "Tree.selectionBackground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.nonSelectionBackground", new ColorUIResource(Color.white), - "Tree.selectionBorderColor", new ColorUIResource(new Color(102, 102, 153)), + "Tree.selectionBackground", getTextHighlightColor(), "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(new Color(102, 102, 153)), - "Tree.nonSelectionBorder", new BorderUIResource.LineBorderUIResource(Color.white), - "Tree.selectionForeground", new ColorUIResource(Color.black), - "Tree.textBackground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.textForeground", new ColorUIResource(Color.black), - "Tree.selectionForeground", new ColorUIResource(Color.black), - "PopupMenu.border", new MetalBorders.PopupMenuBorder() + "Tree.selectionBorderColor", getFocusColor(), + "Tree.selectionForeground", getHighlightedTextColor(), + "Tree.textBackground", getWindowBackground(), + "Tree.textForeground", getUserTextColor(), + + "Viewport.background", getControl(), + "Viewport.foreground", getUserTextColor() }; defaults.putDefaults(myDefaults); } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java index ec9bf2b5586..44a2d3bcd6a 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java @@ -39,8 +39,12 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JPopupMenu; import javax.swing.plaf.ComponentUI; +/** + * A UI delegate for the {@link JPopupMenu.Separator} component. + */ public class MetalPopupMenuSeparatorUI extends MetalSeparatorUI { @@ -50,7 +54,7 @@ public class MetalPopupMenuSeparatorUI private static MetalPopupMenuSeparatorUI instance = null; /** - * Constructs a new instance of MetalPopupMenuSeparatorUI. + * Constructs a new instance of <code>MetalPopupMenuSeparatorUI</code>. */ public MetalPopupMenuSeparatorUI() { @@ -58,11 +62,11 @@ public class MetalPopupMenuSeparatorUI } /** - * Returns an instance of MetalPopupMenuSeparatorUI. + * Returns a shared instance of <code>MetalPopupMenuSeparatorUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalPopupMenuSeparatorUI + * @return A shared instance of <code>MetalPopupMenuSeparatorUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java index 96d1988fd3d..0f28818b96c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java @@ -38,20 +38,22 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Insets; + import javax.swing.JComponent; +import javax.swing.JProgressBar; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicProgressBarUI; -public class MetalProgressBarUI - extends BasicProgressBarUI -{ - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalProgressBarUIs */ - private static MetalProgressBarUI instance = null; - +/** + * A UI delegate for the {@link JProgressBar} component. + */ +public class MetalProgressBarUI extends BasicProgressBarUI +{ /** - * Constructs a new instance of MetalProgressBarUI. + * Constructs a new instance of <code>MetalProgressBarUI</code>. */ public MetalProgressBarUI() { @@ -59,16 +61,87 @@ public class MetalProgressBarUI } /** - * Returns an instance of MetalProgressBarUI. + * Returns a new instance of <code>MetalProgressBarUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalProgressBarUI + * @return A new instance of <code>MetalProgressBarUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalProgressBarUI(); - return instance; + return new MetalProgressBarUI(); + } + + /** + * Performs the painting for determinate progress bars. This calls the + * superclass behaviour and then adds some highlighting to the upper and left + * edge of the progress bar. + * + * @param g the graphics context + * @param c not used here + */ + public void paintDeterminate(Graphics g, JComponent c) + { + super.paintDeterminate(g, c); + Color saved = g.getColor(); + Insets i = progressBar.getInsets(); + int w = progressBar.getWidth(); + int h = progressBar.getHeight(); + int orientation = progressBar.getOrientation(); + + Color shadow = MetalLookAndFeel.getControlShadow(); + g.setColor(shadow); + + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, i.top, i.left, h - i.bottom); + int full = getAmountFull(i, w, h); + if (full > 0) + { + Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); + g.setColor(darkShadow); + if (orientation == JProgressBar.HORIZONTAL) + { + g.drawLine(i.left, i.top, i.left, h - i.bottom); + g.drawLine(i.left, i.top, i.left + full - 1, i.top); + } + else + { + if (full >= (h - i.top - i.bottom)) + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, h - i.bottom, i.left, h - i.bottom - full); + } + } + g.setColor(saved); + } + + /** + * Performs the painting for indeterminate progress bars. This calls the + * superclass behaviour and then adds some highlighting to the upper and left + * edge of the progress bar. + * + * @param g the graphics context + * @param c not used here + */ + public void paintIndeterminate(Graphics g, JComponent c) + { + super.paintIndeterminate(g, c); + Color saved = g.getColor(); + Insets i = progressBar.getInsets(); + int w = progressBar.getWidth(); + int h = progressBar.getHeight(); + Color shadow = MetalLookAndFeel.getControlShadow(); + g.setColor(shadow); + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, i.top, i.left, h - i.bottom); + + boxRect = getBox(boxRect); + Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); + g.setColor(darkShadow); + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.HORIZONTAL) + g.drawLine(boxRect.x, i.top, boxRect.x + boxRect.width - 1, i.top); + else + g.drawLine(i.left, boxRect.y, i.left, boxRect.y + boxRect.height - 1); + g.setColor(saved); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java index a668f914e43..f37626e630f 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -38,20 +38,38 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JRadioButton; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonUI; + +/** + * A UI delegate for the {@link JRadioButton} component. + */ public class MetalRadioButtonUI extends BasicRadioButtonUI { - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JRadioButtons. */ - private static MetalRadioButtonUI instance = null; - + /** Used to draw the focus rectangle. */ + protected Color focusColor; + + /** Used to fill the icon when the button is pressed. */ + protected Color selectColor; + + /** Used to draw disabled text. */ + protected Color disabledTextColor; + /** - * Constructs a new instance of MetalRadioButtonUI. + * Constructs a new instance of <code>MetalRadioButtonUI</code>. */ public MetalRadioButtonUI() { @@ -59,16 +77,108 @@ public class MetalRadioButtonUI } /** - * Returns an instance of MetalRadioButtonUI. + * Returns a new instance of <code>MetalRadioButtonUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalRadioButtonUI + * @return A new instance of <code>MetalRadioButtonUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalRadioButtonUI(); - return instance; + return new MetalRadioButtonUI(); + } + + /** + * Sets the default values for the specified button. + * + * @param b the button. + */ + public void installDefaults(AbstractButton b) + { + super.installDefaults(b); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + disabledTextColor = defaults.getColor("RadioButton.disabledText"); + focusColor = defaults.getColor("RadioButton.focus"); + selectColor = defaults.getColor("RadioButton.select"); + } + + /** + * Clears any defaults set in the installDefaults() method. + * + * @param b the {@link JRadioButton}. + */ + protected void uninstallDefaults(AbstractButton b) + { + super.uninstallDefaults(b); + disabledTextColor = null; + focusColor = null; + selectColor = null; + } + + /** + * Returns the color used to fill the {@link JRadioButton}'s icon when the + * button is pressed. The default color is obtained from the + * {@link UIDefaults} via an entry with the key + * <code>RadioButton.select</code>. + * + * @return The select color. + */ + protected Color getSelectColor() + { + return selectColor; + } + + /** + * Returns the color for the {@link JRadioButton}'s text when the button is + * disabled. The default color is obtained from the {@link UIDefaults} via + * an entry with the key <code>RadioButton.disabledText</code>. + * + * @return The disabled text color. + */ + protected Color getDisabledTextColor() + { + return disabledTextColor; + } + + /** + * Returns the color used to draw the focus rectangle when the + * {@link JRadioButton} has the focus. The default color is obtained from + * the {@link UIDefaults} via an entry with the key + * <code>RadioButton.focus</code>. + * + * @return The color used to draw the focus rectangle. + * + * @see #paintFocus(Graphics, Rectangle, Dimension) + */ + protected Color getFocusColor() + { + return focusColor; + } + + /** + * Paints the {@link JRadioButton}. + * + * @param g the graphics device. + * @param c the component (an instance of {@link JRadioButton}). + */ + public void paint(Graphics g, JComponent c) + { + super.paint(g, c); + // FIXME: disabled text isn't being drawn correctly, it's possible that + // it could be done here... + } + + /** + * Paints the focus rectangle for the {@link JRadioButton}. + * + * @param g the graphics device. + * @param t the bounding rectangle for the text. + * @param d ??? + */ + protected void paintFocus(Graphics g, Rectangle t, Dimension d) + { + g.setColor(focusColor); + g.drawRect(t.x - 1, t.y + 2, t.width + 2, t.height - 4); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java index 4196a4e477c..faed80382d0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java @@ -39,9 +39,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JRootPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRootPaneUI; +/** + * A UI delegate for the {@link JRootPane} component. This class is not fully + * implemented. + * + * @since 1.4 + */ public class MetalRootPaneUI extends BasicRootPaneUI { @@ -51,7 +58,7 @@ public class MetalRootPaneUI private static MetalRootPaneUI instance = null; /** - * Constructs a new instance of MetalRootPaneUI. + * Constructs a shared instance of <code>MetalRootPaneUI</code>. */ public MetalRootPaneUI() { @@ -59,11 +66,11 @@ public class MetalRootPaneUI } /** - * Returns an instance of MetalRootPaneUI. + * Returns a shared instance of <code>MetalRootPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalRootPaneUI + * @return A shared instance of <code>MetalRootPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java index 526dfb50ae2..9602ade9947 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java @@ -38,30 +38,106 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; -import java.util.HashMap; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollBarUI; -public class MetalScrollBarUI - extends BasicScrollBarUI +/** + * A UI delegate for the {@link JScrollBar} component. + */ +public class MetalScrollBarUI extends BasicScrollBarUI { + + /** + * A property change handler for the UI delegate that monitors for + * changes to the "JScrollBar.isFreeStanding" property, and updates + * the buttons and track rendering as appropriate. + */ + class MetalScrollBarPropertyChangeHandler + extends BasicScrollBarUI.PropertyChangeHandler + { + /** + * Creates a new handler. + * + * @see #createPropertyChangeListener() + */ + public MetalScrollBarPropertyChangeHandler() + { + // Nothing to do here. + } + + /** + * Handles a property change event. If the event name is + * <code>JSlider.isFreeStanding</code>, this method updates the + * delegate, otherwise the event is passed up to the super class. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(FREE_STANDING_PROP)) + { + Boolean prop = (Boolean) e.getNewValue(); + isFreeStanding = (prop == null ? true : prop.booleanValue()); + if (increaseButton != null) + increaseButton.setFreeStanding(isFreeStanding); + if (decreaseButton != null) + decreaseButton.setFreeStanding(isFreeStanding); + } + else + super.propertyChange(e); + } + } + + /** The name for the 'free standing' property. */ + public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding"; - /** The minimum thumb size */ - private static final Dimension MIN_THUMB_SIZE = new Dimension(18, 18); - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JScrollBars. */ - private static HashMap instances = null; + /** The minimum thumb size for a scroll bar that is not free standing. */ + private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15); + /** The minimum thumb size for a scroll bar that is free standing. */ + private static final Dimension MIN_THUMB_SIZE_FREE_STANDING + = new Dimension(17, 17); + + /** The button that increases the value in the scroll bar. */ + protected MetalScrollButton increaseButton; + + /** The button that decreases the value in the scroll bar. */ + protected MetalScrollButton decreaseButton; + + /** + * The scroll bar width. + */ + protected int scrollBarWidth; + + /** + * A flag that indicates whether the scroll bar is "free standing", which + * means it has complete borders and can be used anywhere in the UI. A + * scroll bar which is not free standing has borders missing from one + * side, and relies on being part of another container with its own borders + * to look right visually. */ + protected boolean isFreeStanding = true; + + /** + * The color for the scroll bar shadow (this is read from the UIDefaults in + * the installDefaults() method). + */ + Color scrollBarShadowColor; + /** - * Constructs a new instance of MetalScrollBarUI. + * Constructs a new instance of <code>MetalScrollBarUI</code>, with no + * specific initialisation. */ public MetalScrollBarUI() { @@ -69,28 +145,192 @@ public class MetalScrollBarUI } /** - * Returns an instance of MetalScrollBarUI. + * Returns a new instance of <code>MetalScrollBarUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalScrollBarUI + * @return An instance of MetalScrollBarUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); + return new MetalScrollBarUI(); + } + + /** + * Installs the defaults. + */ + protected void installDefaults() + { + // need to initialise isFreeStanding before calling the super class, + // so that the value is set when createIncreaseButton() and + // createDecreaseButton() are called (unless there is somewhere earlier + // that we can do this). + Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP); + isFreeStanding = (prop == null ? true : prop.booleanValue()); + scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow"); + super.installDefaults(); + } + + /** + * Creates a property change listener for the delegate to use. This + * overrides the method to provide a custom listener for the + * {@link MetalLookAndFeel} that can handle the + * <code>JScrollBar.isFreeStanding</code> property. + * + * @return A property change listener. + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new MetalScrollBarPropertyChangeHandler(); + } + + /** + * Creates a new button to use as the control at the lower end of the + * {@link JScrollBar}. + * + * @param orientation the orientation of the button ({@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). + * + * @return The button. + */ + protected JButton createDecreaseButton(int orientation) + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + scrollBarWidth = defaults.getInt("ScrollBar.width"); + decreaseButton = new MetalScrollButton(orientation, scrollBarWidth, + isFreeStanding); + return decreaseButton; + } - Object o = instances.get(component); - MetalScrollBarUI instance; - if (o == null) + /** + * Creates a new button to use as the control at the upper end of the + * {@link JScrollBar}. + * + * @param orientation the orientation of the button ({@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). + * + * @return The button. + */ + protected JButton createIncreaseButton(int orientation) + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + scrollBarWidth = defaults.getInt("ScrollBar.width"); + increaseButton = new MetalScrollButton(orientation, scrollBarWidth, + isFreeStanding); + return increaseButton; + } + + /** + * Paints the track for the scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param trackBounds the track bounds. + */ + protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) + { + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, + trackBounds.height); + if (scrollbar.getOrientation() == HORIZONTAL) + paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, + trackBounds.width, trackBounds.height); + else + paintTrackVertical(g, c, trackBounds.x, trackBounds.y, + trackBounds.width, trackBounds.height); + + } + + /** + * Paints the track for a horizontal scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param x the x-coordinate for the track bounds. + * @param y the y-coordinate for the track bounds. + * @param w the width for the track bounds. + * @param h the height for the track bounds. + */ + private void paintTrackHorizontal(Graphics g, JComponent c, + int x, int y, int w, int h) + { + if (c.isEnabled()) { - instance = new MetalScrollBarUI(); - instances.put(component, instance); + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + + g.setColor(scrollBarShadowColor); + g.drawLine(x + 1, y + 1, x + 1, y + h - 1); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + + if (isFreeStanding) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y + h - 2, x + w - 1, y + h - 2); + g.setColor(scrollBarShadowColor); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } } else - instance = (MetalScrollBarUI) o; - - return instance; + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + } + } + } + + /** + * Paints the track for a vertical scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param x the x-coordinate for the track bounds. + * @param y the y-coordinate for the track bounds. + * @param w the width for the track bounds. + * @param h the height for the track bounds. + */ + private void paintTrackVertical(Graphics g, JComponent c, + int x, int y, int w, int h) + { + if (c.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(scrollBarShadowColor); + g.drawLine(x + 1, y + 1, x + w - 1, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + if (isFreeStanding) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x + w - 2, y, x + w - 2, y + h - 1); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + } + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } + } } /** @@ -102,45 +342,139 @@ public class MetalScrollBarUI */ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - // first we fill the background - g.setColor(thumbColor); - g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, - thumbBounds.height); + // a disabled scrollbar has no thumb in the metal look and feel + if (!c.isEnabled()) + return; + if (scrollbar.getOrientation() == HORIZONTAL) + paintThumbHorizontal(g, c, thumbBounds); + else + paintThumbVertical(g, c, thumbBounds); - // draw the outer dark line - g.setColor(thumbDarkShadowColor); - g.drawRect(thumbBounds.x, thumbBounds.y, thumbBounds.width - 1, - thumbBounds.height - 1); + // draw the pattern + MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3, + thumbBounds.width - 6, thumbBounds.height - 6, + thumbHighlightColor, thumbLightShadowColor); + } - // draw the inner light line + /** + * Paints the thumb for a horizontal scroll bar. + * + * @param g the graphics device. + * @param c the scroll bar component. + * @param thumbBounds the thumb bounds. + */ + private void paintThumbHorizontal(Graphics g, JComponent c, + Rectangle thumbBounds) + { + int x = thumbBounds.x; + int y = thumbBounds.y; + int w = thumbBounds.width; + int h = thumbBounds.height; + + // first we fill the background + g.setColor(thumbColor); + if (isFreeStanding) + g.fillRect(x, y, w, h - 1); + else + g.fillRect(x, y, w, h); + + // then draw the dark box + g.setColor(thumbLightShadowColor); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 2); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y + h -1); + } + + // then the highlight g.setColor(thumbHighlightColor); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + 1, - thumbBounds.x + thumbBounds.width - 2, - thumbBounds.y + 1); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + 1, - thumbBounds.x + 1, - thumbBounds.y + thumbBounds.height - 2); - + if (isFreeStanding) + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + else + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 1); + } + // draw the shadow line UIDefaults def = UIManager.getLookAndFeelDefaults(); g.setColor(def.getColor("ScrollBar.shadow")); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + thumbBounds.height, - thumbBounds.x + thumbBounds.width, - thumbBounds.y + thumbBounds.height); + g.drawLine(x + w, y + 1, x + w, y + h - 1); - // draw the pattern - MetalUtils.fillMetalPattern(g, thumbBounds.x + 3, thumbBounds.y + 3, - thumbBounds.width - 6, thumbBounds.height - 6, - thumbHighlightColor, thumbDarkShadowColor); } - + /** - * This method returns the minimum thumb size. + * Paints the thumb for a vertical scroll bar. + * + * @param g the graphics device. + * @param c the scroll bar component. + * @param thumbBounds the thumb bounds. + */ + private void paintThumbVertical(Graphics g, JComponent c, + Rectangle thumbBounds) + { + int x = thumbBounds.x; + int y = thumbBounds.y; + int w = thumbBounds.width; + int h = thumbBounds.height; + + // first we fill the background + g.setColor(thumbColor); + if (isFreeStanding) + g.fillRect(x, y, w - 1, h); + else + g.fillRect(x, y, w, h); + + // then draw the dark box + g.setColor(thumbLightShadowColor); + if (isFreeStanding) + g.drawRect(x, y, w - 2, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } + + // then the highlight + g.setColor(thumbHighlightColor); + if (isFreeStanding) + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + else + { + g.drawLine(x + 1, y + 1, x + w - 1, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + + // draw the shadow line + UIDefaults def = UIManager.getLookAndFeelDefaults(); + g.setColor(def.getColor("ScrollBar.shadow")); + g.drawLine(x + 1, y + h, x + w - 2, y + h); + } + + /** + * Returns the minimum thumb size. For a free standing scroll bar the + * minimum size is <code>17 x 17</code> pixels, whereas for a non free + * standing scroll bar the minimum size is <code>15 x 15</code> pixels. * * @return The minimum thumb size. */ protected Dimension getMinimumThumbSize() { - return MIN_THUMB_SIZE; + if (isFreeStanding) + return MIN_THUMB_SIZE_FREE_STANDING; + else + return MIN_THUMB_SIZE; } + } + diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java new file mode 100644 index 00000000000..84f9cfe494e --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java @@ -0,0 +1,483 @@ +/* MetalScrollButton.java + Copyright (C) 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 javax.swing.plaf.metal; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicArrowButton; + +/** + * A button used by the {@link MetalScrollBarUI}. The button appearance + * varies according to the button direction, whether or not it is part of a + * "free standing" scroll bar, and the current state of the button. + */ +public class MetalScrollButton extends BasicArrowButton +{ + + /** + * The maximum size for buttons. + * @see #getMaximumSize() + */ + private static Dimension maximumSize; + + /** The width of the button. */ + private int buttonWidth; + + /** + * A flag that indicates whether the button is part of a free standing + * scroll bar. This affects how the border is drawn. + */ + private boolean freeStanding; + + /** + * Creates a new button. + * + * @param direction the direction (this should be one of {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}, but + * this is not enforced). + * @param width the button width. + * @param freeStanding a flag indicating whether the scroll button is free + * standing or not. + */ + public MetalScrollButton(int direction, int width, boolean freeStanding) + { + super(direction); + buttonWidth = width; + this.freeStanding = freeStanding; + } + + /** + * Returns the button width. + * + * @return The button width. + */ + public int getButtonWidth() + { + return buttonWidth; + } + + /** + * Sets the free standing flag. This controls how the button border is + * drawn. + * + * @param freeStanding the new value of the flag. + */ + public void setFreeStanding(boolean freeStanding) + { + this.freeStanding = freeStanding; + } + + /** + * Paints the button. + * + * @param g the graphics device. + */ + public void paint(Graphics g) + { + Rectangle bounds = SwingUtilities.getLocalBounds(this); + + // fill the background + if (getModel().isPressed()) + g.setColor(MetalLookAndFeel.getControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(0, 0, bounds.width, bounds.height); + + paintArrow(g, bounds.width, bounds.height); + + // paint a border manually - I tried using a real (custom) Border + // but couldn't get it to stay set for the button, something was + // overwriting it... + if (freeStanding) + { + if (direction == WEST) + paintWestBorderFreeStanding(g, bounds.width, bounds.height); + else if (direction == EAST) + paintEastBorderFreeStanding(g, bounds.width, bounds.height); + else if (direction == SOUTH) + paintSouthBorderFreeStanding(g, bounds.width, bounds.height); + else // asume NORTH + paintNorthBorderFreeStanding(g, bounds.width, bounds.height); + } + else + { + if (direction == WEST) + paintWestBorder(g, bounds.width, bounds.height); + else if (direction == EAST) + paintEastBorder(g, bounds.width, bounds.height); + else if (direction == SOUTH) + paintSouthBorder(g, bounds.width, bounds.height); + else // asume NORTH + paintNorthBorder(g, bounds.width, bounds.height); + } + } + + private void paintArrow(Graphics g, int w, int h) + { + if (isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + + if (direction == SOUTH) + { + int x = w / 2; + int y = h / 2 + 2; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y - i, x + i - 1, y - i); + } + else if (direction == EAST) + { + int x = w / 2 + 2; + int y = h / 2; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y - i, x - i, y + i - 1); + } + else if (direction == WEST) + { + int x = w / 2 - 3; + int y = h / 2; + for (int i = 1; i < 5; i++) + g.drawLine(x + i, y - i, x + i, y + i - 1); + } + else // assume NORTH + { + int x = w / 2; + int y = h / 2 - 3; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y + i, x + i - 1, y + i); + } + } + /** + * Paints the border for a button with a {@link #NORTH} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintNorthBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(2, h - 1, w - 2, h - 1); + g.drawLine(w - 2, 2, w - 2, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, 1, h - 2); + g.drawLine(1, 1, w - 3, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + g.drawLine(w - 2, 1, w - 2, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #SOUTH} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintSouthBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(2, h - 1, w - 2, h - 1); + g.drawLine(w - 2, 2, w - 2, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, 1, h - 1); + g.drawLine(1, 1, w - 1, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + g.drawLine(w - 1, 1, w - 1, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, h - 1, w - 1, h - 1); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with an {@link #EAST} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintEastBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(w - 2, 0, w - 2, h - 2); + g.drawLine(0, h - 2, w - 2, h - 2); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, w - 1, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(w - 2, 1, w - 2, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #WEST} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintWestBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 2); + g.drawLine(0, h - 2, w - 1, h - 2); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, w - 1, 1); + g.drawLine(1, 1, 1, h - 1); + g.drawLine(1, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 2, 1, h - 2); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #NORTH} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintNorthBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, 0, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 0, 1, h - 1); + g.drawLine(1, 0, w - 1, 0); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #SOUTH} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintSouthBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 0, 1, h - 1); + g.drawLine(1, 0, w - 1, 0); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with an {@link #EAST} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintEastBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 2, w - 1, h - 1); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, w - 2, 1); + g.drawLine(0, 1, 0, h - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + } + } + + /** + * Paints the border for a button with a {@link #WEST} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintWestBorder(Graphics g, int w, int h) + { + Rectangle bounds = SwingUtilities.getLocalBounds(this); + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, bounds.width - 1, 0); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, bounds.width - 1, 1); + g.drawLine(0, 1, 0, bounds.height - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, bounds.width - 1, 0); + } + } + + /** + * Returns the preferred size for the button, which varies depending on + * the direction of the button and whether or not it is free standing. + * + * @return The preferred size. + */ + public Dimension getPreferredSize() + { + int adj = 1; + if (!freeStanding) + adj = 2; + + if (direction == EAST) + return new Dimension(buttonWidth - adj, buttonWidth); + else if (direction == WEST) + return new Dimension(buttonWidth - 2, buttonWidth); + else if (direction == SOUTH) + return new Dimension(buttonWidth, buttonWidth - adj); + else // assume NORTH + return new Dimension(buttonWidth, buttonWidth - 2); + } + + /** + * Returns the minimum size for the button. + * + * @return The minimum size for the button. + */ + public Dimension getMinimumSize() + { + return getPreferredSize(); + } + + /** + * Returns the maximum size for the button. + * + * @return <code>Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>. + */ + public Dimension getMaximumSize() + { + if (maximumSize == null) + maximumSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + return maximumSize; + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java index 3e1198b398d..d5bf175f92d 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java @@ -39,19 +39,18 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JScrollPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; +/** + * A UI delegate for the {@link JScrollPane} component. + */ public class MetalScrollPaneUI extends BasicScrollPaneUI { - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JScrollPanes. */ - private static MetalScrollPaneUI instance = null; - /** - * Constructs a new instance of MetalScrollPaneUI. + * Constructs a new instance of <code>MetalScrollPaneUI</code>. */ public MetalScrollPaneUI() { @@ -59,16 +58,14 @@ public class MetalScrollPaneUI } /** - * Returns an instance of MetalScrollPaneUI. + * Returns a shared instance of <code>MetalScrollPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalScrollPaneUI + * @return A shared instance of <code>MetalScrollPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalScrollPaneUI(); - return instance; + return new MetalScrollPaneUI(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java index 6e78ccb7071..1d48e9be2b0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java @@ -38,10 +38,20 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + import javax.swing.JComponent; +import javax.swing.JSeparator; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSeparatorUI; +/** + * A UI delegate for the {@link JSeparator} component. + */ public class MetalSeparatorUI extends BasicSeparatorUI { @@ -51,7 +61,7 @@ public class MetalSeparatorUI private static MetalSeparatorUI instance = null; /** - * Constructs a new instance of MetalSeparatorUI. + * Constructs a new instance of <code>MetalSeparatorUI</code>. */ public MetalSeparatorUI() { @@ -59,11 +69,11 @@ public class MetalSeparatorUI } /** - * Returns an instance of MetalSeparatorUI. + * Returns a shared instance of <code>MetalSeparatorUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalSeparatorUI + * @return A shared instance of <code>MetalSeparatorUI</code>. */ public static ComponentUI createUI(JComponent component) { @@ -71,4 +81,51 @@ public class MetalSeparatorUI instance = new MetalSeparatorUI(); return instance; } + + /** + * The separator is made of two lines. The top line will be + * the Metal theme color separatorForeground (or left line if it's vertical). + * The bottom or right line will be the Metal theme color + * separatorBackground. + * The two lines will + * be centered inside the bounds box. If the separator is horizontal, + * then it will be vertically centered, or if it's vertical, it will + * be horizontally centered. + * + * @param g The Graphics object to paint with + * @param c The JComponent to paint. + */ + public void paint(Graphics g, JComponent c) + { + Rectangle r = new Rectangle(); + SwingUtilities.calculateInnerArea(c, r); + Color saved = g.getColor(); + Color c1 = UIManager.getColor("Separator.foreground"); + Color c2 = UIManager.getColor("Separator.background"); + JSeparator s; + if (c instanceof JSeparator) + s = (JSeparator) c; + else + return; + + if (s.getOrientation() == JSeparator.HORIZONTAL) + { + int midAB = r.height / 2; + g.setColor(c1); + g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1); + + g.setColor(c2); + g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB); + } + else + { + int midAD = r.height / 2 + r.y; + g.setColor(c1); + g.drawLine(r.x, r.y, r.x, r.y + r.height); + + g.setColor(c2); + g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height); + } + g.setColor(saved); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java index 4b52c4b0041..08fb99d216c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java @@ -42,8 +42,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.HashMap; import javax.swing.Icon; import javax.swing.JComponent; @@ -56,16 +56,59 @@ import javax.swing.plaf.basic.BasicSliderUI; /** * A UI delegate for the {@link JSlider} component. */ -public class MetalSliderUI - extends BasicSliderUI +public class MetalSliderUI extends BasicSliderUI { - // TODO: find a use for this + /** + * A property change handler that updates the rendered component in response + * to specific property change events. This custom handler is used to + * intercept the "JSlider.isFilled" property, which is only recognised by + * the {@link MetalLookAndFeel}. + */ + protected class MetalPropertyListener + extends BasicSliderUI.PropertyChangeHandler + { + /** + * Creates a new listener. + */ + protected MetalPropertyListener() + { + // Nothing to do here. + } + + /** + * Handles property change events. Events with the name "JSlider.isFilled" + * are handled here, and other events are passed to the superclass. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(SLIDER_FILL)) + { + Boolean b = (Boolean) e.getNewValue(); + if (b == null) + filledSlider = false; + else + filledSlider = b.booleanValue(); + } + else + super.propertyChange(e); + } + } + + /** The thumb color (unused, because an icon is used to draw the thumb). */ protected static Color thumbColor; - // TODO: find a use for this + /** + * The highlight color used for drawing the track rect when the slider is + * enabled. + */ protected static Color highlightColor; - // TODO: find a use for this + /** + * The shadow color used for drawing the track rect when the slider is + * enabled. + */ protected static Color darkShadowColor; /** The track width. */ @@ -85,17 +128,14 @@ public class MetalSliderUI /** The gap between the track and the tick marks. */ protected final int TICK_BUFFER = 4; + /** A key to look up the filledSlider setting in the {@link UIManager}. */ + protected final String SLIDER_FILL = "JSlider.isFilled"; + /** * A flag that controls whether or not the track is filled up to the value * of the slider. */ protected boolean filledSlider; - - /** A key to look up the filledSlider setting in the {@link UIManager}. */ - protected final String SLIDER_FILL = "JSlider.isFilled"; - - /** The UI instances for MetalSliderUIs */ - private static HashMap instances; /** * Constructs a new instance. @@ -104,33 +144,27 @@ public class MetalSliderUI { super(null); filledSlider = UIManager.getBoolean(SLIDER_FILL); + darkShadowColor = MetalLookAndFeel.getControlDarkShadow(); + highlightColor = MetalLookAndFeel.getControlHighlight(); } /** - * Returns an instance of MetalSliderUI. + * Returns a new instance of <code>MetalSliderUI</code>. * - * @param component the component for which we return an UI instance + * @param component the component (ignored). * - * @return an instance of MetalSliderUI + * @return A new instance of <code>MetalSliderUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalSliderUI instance; - if (o == null) - { - instance = new MetalSliderUI(); - instances.put(component, instance); - } - else - instance = (MetalSliderUI) o; - - return instance; + return new MetalSliderUI(); } + /** + * Installs the default for this UI delegate in the supplied component. + * + * @param c the component. + */ public void installUI(JComponent c) { super.installUI(c); @@ -140,6 +174,18 @@ public class MetalSliderUI } /** + * Creates a property change listener for the slider. + * + * @param slider the slider. + * + * @return A new instance of {@link MetalPropertyListener}. + */ + protected PropertyChangeListener createPropertyChangeListener(JSlider slider) + { + return new MetalPropertyListener(); + } + + /** * Paints the thumb icon for the slider. * * @param g the graphics device. @@ -153,46 +199,79 @@ public class MetalSliderUI } /** - * Creates a property change listener for the slider. - * - * @param slider the slider. - */ - protected PropertyChangeListener createPropertyChangeListener(JSlider slider) - { - // TODO: try to figure out why it might be necessary to override this - // method as is done in Sun's implementation - return super.createPropertyChangeListener(slider); - } - - /** * Paints the track along which the thumb control moves. * * @param g the graphics device. */ public void paintTrack(Graphics g) { + Color shadowColor = MetalLookAndFeel.getControlShadow(); if (slider.getOrientation() == JSlider.HORIZONTAL) - { - if (filledSlider) { - // TODO: fill the track + int trackX = trackRect.x; + int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2; + int trackW = trackRect.width - 1; + int trackH = getTrackWidth(); + + // draw border + if (slider.isEnabled()) + BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, + darkShadowColor, shadowColor, darkShadowColor, highlightColor); + else + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawRect(trackX, trackY, trackW - 2, trackH - 2); + } + + // fill track (if required) + if (filledSlider) + { + int xPos = xPositionForValue(slider.getValue()); + int x = (slider.getInverted() ? xPos : trackRect.x); + int w = (slider.getInverted() ? trackX + trackW - xPos + : xPos - trackRect.x); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3); + if (slider.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1); + g.drawLine(x + 1, trackY + 1, x + 1, + trackY + getTrackWidth() - 3); + } + } } - BasicGraphicsUtils.drawEtchedRect(g, trackRect.x, trackRect.y - + (trackRect.height - getTrackWidth()) / 2, trackRect.width - 1, - getTrackWidth(), Color.darkGray, Color.gray, Color.darkGray, - Color.white); - } else - { - if (filledSlider) { - // TODO: fill the track + int trackX = trackRect.x + (trackRect.width - getTrackWidth()) / 2; + int trackY = trackRect.y; + int trackW = getTrackWidth(); + int trackH = trackRect.height - 1; + if (slider.isEnabled()) + BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, + darkShadowColor, shadowColor, darkShadowColor, highlightColor); + else + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawRect(trackX, trackY, trackW - 2, trackH - 2); + } + + if (filledSlider) + { + int yPos = yPositionForValue(slider.getValue()); + int y = (slider.getInverted() ? trackY : yPos); + int h = (slider.getInverted() ? yPos - trackY + : trackY + trackH - yPos); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3); + if (slider.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1); + g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3); + } + } } - BasicGraphicsUtils.drawEtchedRect(g, trackRect.x + (trackRect.width - - getTrackWidth()) / 2, trackRect.y, getTrackWidth(), - trackRect.height - 1, Color.darkGray, Color.gray, Color.darkGray, - Color.white); - } } /** @@ -262,12 +341,13 @@ public class MetalSliderUI */ protected int getThumbOverhang() { - // TODO: figure out what this is used for + // FIXME: for what might this method be used? return 0; } protected void scrollDueToClickInTrack(int dir) { + // FIXME: for what might this method be overridden? super.scrollDueToClickInTrack(dir); } @@ -283,8 +363,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2); } @@ -300,8 +382,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength); } @@ -317,8 +401,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y); } @@ -334,8 +420,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y); } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 60e9c055952..016e09557d6 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -78,7 +78,7 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider { //super.paint(g); Dimension s = getSize(); - MetalUtils.fillMetalPattern(g, 2, 2, s.width - 4, s.height - 4, + MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, light, dark); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java index b7ea8984b43..b39fb23366e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java @@ -39,24 +39,22 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; -import java.util.HashMap; import javax.swing.JComponent; +import javax.swing.JSplitPane; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; -public class MetalSplitPaneUI - extends BasicSplitPaneUI +/** + * A UI delegate for the {@link JSplitPane} component. + */ +public class MetalSplitPaneUI extends BasicSplitPaneUI { - - /** The UI instances for MetalSplitPaneUIs */ - private static HashMap instances; - /** - * Constructs a new instance of MetalSplitPaneUI. + * Constructs a new instance of <code>MetalSplitPaneUI</code>. */ public MetalSplitPaneUI() { @@ -64,28 +62,15 @@ public class MetalSplitPaneUI } /** - * Returns an instance of MetalSplitPaneUI. + * Returns a new instance of <code>MetalSplitPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalSplitPaneUI + * @return A new instance of <code>MetalSplitPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalSplitPaneUI instance; - if (o == null) - { - instance = new MetalSplitPaneUI(); - instances.put(component, instance); - } - else - instance = (MetalSplitPaneUI) o; - - return instance; + return new MetalSplitPaneUI(); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index 1b5fe144f6c..b1e02c7a6b0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -40,7 +40,6 @@ package javax.swing.plaf.metal; import java.awt.Graphics; import java.awt.LayoutManager; -import java.util.HashMap; import javax.swing.JComponent; import javax.swing.JTabbedPane; @@ -48,11 +47,9 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; /** - * A UI delegate used for the {@link JTabbedPane} component in the - * {@link MetalLookAndFeel}. + * A UI delegate for the {@link JTabbedPane} component. */ -public class MetalTabbedPaneUI - extends BasicTabbedPaneUI +public class MetalTabbedPaneUI extends BasicTabbedPaneUI { /** @@ -65,13 +62,14 @@ public class MetalTabbedPaneUI * public for compatibility. */ public class TabbedPaneLayout - extends BasicTabbedPaneUI.TabbedPaneLayout + extends BasicTabbedPaneUI.TabbedPaneLayout { /** * Creates a new instance of the layout manager. */ public TabbedPaneLayout() { + // Nothing to do here. } /** @@ -102,9 +100,6 @@ public class MetalTabbedPaneUI } } - /** The shared UI instance for JTabbedPanes. */ - private static HashMap instances = null; - /** * Constructs a new instance of MetalTabbedPaneUI. */ @@ -122,20 +117,7 @@ public class MetalTabbedPaneUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalTabbedPaneUI instance; - if (o == null) - { - instance = new MetalTabbedPaneUI(); - instances.put(component, instance); - } - else - instance = (MetalTabbedPaneUI) o; - - return instance; + return new MetalTabbedPaneUI(); } /** @@ -145,7 +127,7 @@ public class MetalTabbedPaneUI */ protected LayoutManager createLayoutManager() { - return new TabbedPaneLayout(); + return super.createLayoutManager(); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java index d6e50e12239..6984daeccbe 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java @@ -38,19 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; - import javax.swing.JComponent; +import javax.swing.JTextField; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTextFieldUI; -public class MetalTextFieldUI - extends BasicTextFieldUI +/** + * A UI delegate for the {@link JTextField} component. + */ +public class MetalTextFieldUI extends BasicTextFieldUI { - - /** The UI instances for MetalTextFieldUIs */ - private static HashMap instances = null; - /** * Constructs a new instance of MetalTextFieldUI. */ @@ -60,27 +57,14 @@ public class MetalTextFieldUI } /** - * Returns an instance of MetalTextFieldUI. + * Returns a new instance of <code>MetalTextFieldUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalTextFieldUI + * @return A new instance of <code>MetalTextFieldUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalTextFieldUI instance; - if (o == null) - { - instance = new MetalTextFieldUI(); - instances.put(component, instance); - } - else - instance = (MetalTextFieldUI) o; - - return instance; + return new MetalTextFieldUI(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java index be6d0c39ec8..46a19bdbe9e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java @@ -39,13 +39,24 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicToggleButtonUI; +/** + * A UI delegate for the {@link JToggleButton} component. + */ public class MetalToggleButtonUI extends BasicToggleButtonUI { @@ -59,21 +70,26 @@ public class MetalToggleButtonUI /** The color for disabled button labels. */ protected Color disabledTextColor; - /** The shared UI instance for MetalToggleButtonUIs */ - private static MetalToggleButtonUI instance = null; + /** + * Returns a new instance of <code>MetalToggleButtonUI</code>. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of <code>MetalToggleButtonUI</code>. + */ + public static ComponentUI createUI(JComponent component) + { + return new MetalToggleButtonUI(); + } /** - * Constructs a new instance of MetalToggleButtonUI. + * Constructs a new instance of <code>MetalToggleButtonUI</code>. */ public MetalToggleButtonUI() { super(); - focusColor = getFocusColor(); - selectColor = getSelectColor(); - disabledTextColor = getDisabledTextColor(); } - /** * Returns the color for the focus border. * @@ -81,8 +97,7 @@ public class MetalToggleButtonUI */ protected Color getFocusColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".focus"); + return focusColor; } /** @@ -92,32 +107,98 @@ public class MetalToggleButtonUI */ protected Color getSelectColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".select"); + return selectColor; } /** - * Returns the color for the text label of disabled buttons. + * Returns the color for the text label of disabled buttons. The value + * is initialised in the {@link #installDefaults(AbstractButton)} method + * by reading the <code>ToggleButton.disabledText</code> item from the UI + * defaults. * - * @return the color for the text label of disabled buttons + * @return The color for the text label of disabled buttons. */ protected Color getDisabledTextColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".disabledText"); + return disabledTextColor; } /** - * Returns an instance of MetalToggleButtonUI. - * - * @param component the component for which we return an UI instance - * - * @return an instance of MetalToggleButtonUI + * Updates the button with the defaults for this look and feel. + * + * @param b the button. */ - public static ComponentUI createUI(JComponent component) + public void installDefaults(AbstractButton b) + { + super.installDefaults(b); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + focusColor = defaults.getColor(getPropertyPrefix() + "focus"); + selectColor = defaults.getColor(getPropertyPrefix() + "select"); + disabledTextColor = defaults.getColor(getPropertyPrefix() + "disabledText"); + } + + /** + * Paints the button background when it is pressed/selected. + * + * @param g the graphics device. + * @param b the button. + */ + protected void paintButtonPressed(Graphics g, AbstractButton b) + { + if (b.isContentAreaFilled() && b.isOpaque()) + { + Color saved = g.getColor(); + Rectangle bounds = SwingUtilities.getLocalBounds(b); + g.setColor(selectColor); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.setColor(saved); + } + } + + /** + * Paints the text for the button. + * + * @param g the graphics device. + * @param c the component. + * @param textRect the bounds for the text. + * @param text the text. + * + * @deprecated 1.4 Use {@link BasicButtonUI#paintText(java.awt.Graphics, + * javax.swing.AbstractButton, java.awt.Rectangle, java.lang.String)}. + */ + protected void paintText(Graphics g, JComponent c, Rectangle textRect, + String text) + { + Font savedFont = g.getFont(); + Color savedColor = g.getColor(); + g.setFont(c.getFont()); + if (c.isEnabled()) + g.setColor(c.getForeground()); + else + g.setColor(disabledTextColor); + FontMetrics fm = g.getFontMetrics(c.getFont()); + int ascent = fm.getAscent(); + g.drawString(text, textRect.x, textRect.y + ascent); + g.setFont(savedFont); + g.setColor(savedColor); + } + + /** + * Draws the focus highlight around the text and icon. + * + * @param g the graphics device. + * @param b the button. + */ + protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, + Rectangle textRect, Rectangle iconRect) { - if (instance == null) - instance = new MetalToggleButtonUI(); - return instance; + if (!b.hasFocus()) + return; + Color saved = g.getColor(); + g.setColor(focusColor); + Rectangle fr = iconRect.union(textRect); + g.drawRect(fr.x - 1, fr.y - 1, fr.width + 1, fr.height + 1); + g.setColor(saved); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java index 39af0011ae6..c5ca91399ab 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java @@ -38,20 +38,73 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.event.ContainerListener; +import java.beans.PropertyChangeListener; + import javax.swing.JComponent; +import javax.swing.JToolBar; +import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; -public class MetalToolBarUI - extends BasicToolBarUI +/** + * A UI delegate for the {@link JToolBar} component. + */ +public class MetalToolBarUI extends BasicToolBarUI { + + /** + * A listener (no longer used) that responds when components are added to or + * removed from the {@link JToolBar}. The required behaviour is now + * handled in the super class. + * + * @see MetalToolBarUI#createContainerListener() + */ + protected class MetalContainerListener + extends BasicToolBarUI.ToolBarContListener + { + /** + * Creates a new instance. + */ + protected MetalContainerListener() + { + // Nothing to do here. + } + } - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalToolBarUIs */ - private static MetalToolBarUI instance = null; + /** + * A listener (no longer used) that responds to property change events in a + * {@link JToolBar} component. The required behaviour is now handled in the + * super class. + * + * @see MetalToolBarUI#createRolloverListener() + */ + protected class MetalRolloverListener + extends BasicToolBarUI.PropertyListener + { + /** + * Creates a new instance. + */ + protected MetalRolloverListener() + { + // Nothing to do here. + } + } + + /** + * The container listener (an implementation specific field, according to the + * spec, and not used in GNU Classpath). + */ + protected ContainerListener contListener; + + /** + * The rollover listener (an implementation specific field, according to the + * spec, and not used in GNU Classpath). + */ + protected PropertyChangeListener rolloverListener; /** - * Constructs a new instance of MetalToolBarUI. + * Creates a new instance of this UI delegate. */ public MetalToolBarUI() { @@ -59,16 +112,51 @@ public class MetalToolBarUI } /** - * Returns an instance of MetalToolBarUI. + * Returns a new instance of <code>MetalToolBarUI</code>. * - * @param component the component for which we return an UI instance + * @param component the component for which we return an UI instance * - * @return an instance of MetalToolBarUI + * @return A new instance of <code>MetalToolBarUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalToolBarUI(); - return instance; + return new MetalToolBarUI(); + } + + /** + * Returns <code>null</code> as permitted by recent versions of the API + * specification. Originally it seems this method returned a new instance of + * {@link MetalRolloverListener}, but this is now redundant. + * + * @return <code>null</code>. + */ + protected PropertyChangeListener createRolloverListener() + { + return null; + } + + /** + * Returns <code>null</code> as permitted by recent versions of the API + * specification. Originally it seems this method returned a new instance of + * {@link MetalContainerListener}, but this is now redundant. + * + * @return <code>null</code>. + */ + protected ContainerListener createContainerListener() + { + return null; + } + + /** + * Returns a border with no rollover effect for buttons in the tool bar. + * + * @return A border. + * + * @see MetalBorders#getToolbarButtonBorder() + */ + protected Border createNonRolloverBorder() + { + return MetalBorders.getToolbarButtonBorder(); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java index c88b6534ab7..5085d170ac2 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java @@ -38,32 +38,92 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JToolTip; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicToolTipUI; +/** + * A UI delegate for the {@link JToolTip} component. + */ public class MetalToolTipUI extends BasicToolTipUI { + /** + * The amount of space between the tool tip text and the accelerator + * description (if visible). + */ + public static final int padSpaceBetweenStrings = 12; - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalToolTipUIs */ + /** The shared UI instance. */ private static MetalToolTipUI instance = null; - + + /** A flag controlling the visibility of the accelerator (if there is one). */ + private boolean isAcceleratorHidden; + + /** A string representing the accelerator key for the component. */ + private String acceleratorString; + + /** + * The delimiter for the accelerator string. + */ + private String acceleratorDelimiter; + + /** The font for the accelerator string. */ + private Font acceleratorFont; + + /** The color for the accelerator string. */ + private Color acceleratorForeground; + + /** The active border. */ + private Border activeBorder; + + /** The inactive border. */ + private Border inactiveBorder; + /** - * Constructs a new instance of MetalToolTipUI. + * Constructs a new instance of <code>MetalToolTipUI</code>. */ public MetalToolTipUI() { super(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + activeBorder = defaults.getBorder("ToolTip.border"); + inactiveBorder = defaults.getBorder("ToolTip.borderInactive"); + isAcceleratorHidden = defaults.getBoolean("ToolTip.hideAccelerator"); + acceleratorFont = defaults.getFont("MenuItem.acceleratorFont"); + acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground"); + acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter"); } /** - * Returns an instance of MetalToolTipUI. + * Returns a shared instance of the <code>MetalToolTipUI</code> class. + * Although this UI delegate does maintain state information, there is never + * more than one tool tip visible, so it is OK to use a shared instance. * - * @param component the component for which we return an UI instance + * @param component the component (a {@link JToolTip}). * - * @return an instance of MetalToolTipUI + * @return A shared instance of the <code>MetalToolTipUI</code> class. */ public static ComponentUI createUI(JComponent component) { @@ -71,4 +131,202 @@ public class MetalToolTipUI instance = new MetalToolTipUI(); return instance; } + + /** + * Returns a string representing the accelerator key (if there is one) for + * the component that the tool tip belongs to. + * + * @return A string representing the accelerator key. + */ + public String getAcceleratorString() + { + return acceleratorString; + } + + /** + * Installs the UI for the specified component (a {@link JToolTip}). + * + * @param c the {@link JToolTip} component. + */ + public void installUI(JComponent c) + { + super.installUI(c); + Border existingBorder = c.getBorder(); + if (existingBorder == null || existingBorder instanceof UIResource) + { + if (c.isEnabled()) + c.setBorder(activeBorder); + else + c.setBorder(inactiveBorder); + } + } + + /** + * Clears the defaults set in {@link #installUI(JComponent)}. + * + * @param c the component. + */ + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + if (c.getBorder() instanceof UIResource) + c.setBorder(null); + } + + /** + * Returns <code>true</code> if the accelerator string is hidden, and + * <code>false</code> otherwise. This setting is controlled by the + * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table. + * + * @return A boolean. + */ + protected boolean isAcceleratorHidden() + { + return isAcceleratorHidden; + } + + /** + * Returns the preferred size for the {@link JToolTip} component. + * + * @param c the component (a {@link JToolTip}). + * + * @return The preferred size. + */ + public Dimension getPreferredSize(JComponent c) + { + if (isAcceleratorHidden()) + return super.getPreferredSize(c); + else + { + Insets insets = c.getInsets(); + JToolTip tt = (JToolTip) c; + String tipText = tt.getTipText(); + if (tipText != null) + { + FontMetrics fm = c.getFontMetrics(c.getFont()); + int prefH = fm.getHeight() + insets.top + insets.bottom; + int prefW = fm.stringWidth(tipText) + insets.left + insets.right; + + // this seems to be the first opportunity we have to get the + // accelerator string from the component (if it has one) + acceleratorString = fetchAcceleratorString(c); + if (acceleratorString != null) + { + prefW += padSpaceBetweenStrings; + fm = c.getFontMetrics(acceleratorFont); + prefW += fm.stringWidth(acceleratorString); + } + return new Dimension(prefW, prefH); + } + else return new Dimension(0, 0); + } + } + + /** + * Paints the tool tip. + * + * @param g the graphics context. + * @param c the {@link JToolTip} component. + */ + public void paint(Graphics g, JComponent c) + { + JToolTip tip = (JToolTip) c; + + String text = tip.getTipText(); + Toolkit t = tip.getToolkit(); + if (text == null) + return; + + Rectangle vr = new Rectangle(); + vr = SwingUtilities.calculateInnerArea(tip, vr); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + FontMetrics fm = t.getFontMetrics(tip.getFont()); + int ascent = fm.getAscent(); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, + SwingConstants.CENTER, SwingConstants.LEFT, + SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0); + Color saved = g.getColor(); + g.setColor(Color.BLACK); + + g.drawString(text, vr.x, vr.y + ascent); + + // paint accelerator + if (acceleratorString != null) + { + g.setFont(acceleratorFont); + g.setColor(acceleratorForeground); + fm = t.getFontMetrics(acceleratorFont); + int width = fm.stringWidth(acceleratorString); + g.drawString(acceleratorString, vr.x + vr.width - width - padSpaceBetweenStrings/2, + vr.y + vr.height - fm.getDescent()); + } + + g.setColor(saved); + } + + /** + * Returns a string representing the accelerator for the component, or + * <code>null</code> if the component has no accelerator. + * + * @param c the component. + * + * @return A string representing the accelerator (possibly + * <code>null</code>). + */ + private String fetchAcceleratorString(JComponent c) + { + String result = null; + if (c instanceof JToolTip) + { + JToolTip toolTip = (JToolTip) c; + JComponent component = toolTip.getComponent(); + KeyStroke ks = null; + int mne = 0; + if (component instanceof JMenuItem) + { + JMenuItem item = (JMenuItem) component; + ks = item.getAccelerator(); + if (ks == null) + mne = item.getMnemonic(); + } + else if (component instanceof AbstractButton) + { + AbstractButton button = (AbstractButton) component; + mne = button.getMnemonic(); + } + if (mne > 0) + ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne), + InputEvent.ALT_MASK, false); + if (ks != null) + result = acceleratorToString(ks); + } + return result; + } + + /** + * Returns a string representing an accelerator. + * + * @param accelerator the accelerator (<code>null</code> not permitted). + * + * @return A string representing an accelerator. + */ + private String acceleratorToString(KeyStroke accelerator) + { + // convert keystroke into string format + String modifiersText = ""; + int modifiers = accelerator.getModifiers(); + char keyChar = accelerator.getKeyChar(); + int keyCode = accelerator.getKeyCode(); + + if (modifiers != 0) + modifiersText = KeyEvent.getKeyModifiersText(modifiers) + + acceleratorDelimiter; + + if (keyCode == KeyEvent.VK_UNDEFINED) + return modifiersText + keyChar; + else + return modifiersText + KeyEvent.getKeyText(keyCode); + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java index 8d16f7463fe..0ffa0d17470 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java @@ -38,21 +38,50 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ComponentListener; +import java.awt.event.FocusListener; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.beans.PropertyChangeListener; +import java.util.Hashtable; import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.tree.TreeCellEditor; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import javax.swing.event.CellEditorListener; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTreeUI; -public class MetalTreeUI - extends BasicTreeUI +/** + * A UI delegate for the {@link JTree} component. + */ +public class MetalTreeUI extends BasicTreeUI { - /** The UI instances for MetalTreeUIs */ - private static HashMap instances = null; - + /** Listeners */ + private PropertyChangeListener propertyChangeListener; + private FocusListener focusListener; + private TreeSelectionListener treeSelectionListener; + private MouseListener mouseListener; + private KeyListener keyListener; + private PropertyChangeListener selectionModelPropertyChangeListener; + private ComponentListener componentListener; + private CellEditorListener cellEditorListener; + private TreeExpansionListener treeExpansionListener; + private TreeModelListener treeModelListener; + /** - * Constructs a new instance of MetalTreeUI. + * Constructs a new instance of <code>MetalTreeUI</code>. */ public MetalTreeUI() { @@ -60,27 +89,238 @@ public class MetalTreeUI } /** - * Returns an instance of MetalTreeUI. + * Returns a new instance of <code>MetalTreeUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalTreeUI + * @return A new instance of <code>MetalTreeUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); + return new MetalTreeUI(); + } + + /** + * The horizontal element of legs between nodes starts at the right of the + * left-hand side of the child node by default. This method makes the + * leg end before that. + */ + protected int getHorizontalLegBuffer() + { + return super.getHorizontalLegBuffer(); + } + + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should completely + * configure the component for the look and feel, including the following: + * 1. Install any default property values for color, fonts, borders, icons, + * opacity, etc. on the component. Whenever possible, property values + * initialized by the client program should not be overridden. + * 2. Install a LayoutManager on the component if necessary. + * 3. Create/add any required sub-components to the component. + * 4. Create/install event listeners on the component. + * 5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + * 7. Initialize any appropriate instance data. + */ + public void installUI(JComponent c) + { + tree = (JTree) c; + configureLayoutCache(); + + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + tree.setFont(defaults.getFont("Tree.font")); + tree.setForeground(defaults.getColor("Tree.foreground")); + tree.setBackground(defaults.getColor("Tree.background")); + tree.setOpaque(true); + tree.setScrollsOnExpand(defaults.getBoolean("Tree.scrollsOnExpand")); + rightChildIndent = defaults.getInt("Tree.rightChildIndent"); + leftChildIndent = defaults.getInt("Tree.leftChildIndent"); + setRowHeight(defaults.getInt("Tree.rowHeight")); + tree.setRowHeight(defaults.getInt("Tree.rowHeight")); + tree.requestFocusInWindow(false); + + setExpandedIcon(defaults.getIcon("Tree.expandedIcon")); + setCollapsedIcon(defaults.getIcon("Tree.collapsedIcon")); + + currentCellRenderer = createDefaultCellRenderer(); + rendererPane = createCellRendererPane(); + createdRenderer = true; + setCellEditor(createDefaultCellEditor()); + createdCellEditor = true; + TreeModel mod = tree.getModel(); + setModel(mod); + + treeSelectionModel = tree.getSelectionModel(); + drawingCache = new Hashtable(); + nodeDimensions = createNodeDimensions(); + + propertyChangeListener = createPropertyChangeListener(); + focusListener = createFocusListener(); + treeSelectionListener = createTreeSelectionListener(); + mouseListener = createMouseListener(); + keyListener = createKeyListener(); + selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener(); + componentListener = createComponentListener(); + cellEditorListener = createCellEditorListener(); + treeExpansionListener = createTreeExpansionListener(); + treeModelListener = createTreeModelListener(); - Object o = instances.get(component); - MetalTreeUI instance; - if (o == null) + editingRow = -1; + lastSelectedRow = -1; + + installKeyboardActions(); + + tree.addPropertyChangeListener(propertyChangeListener); + tree.addFocusListener(focusListener); + tree.addTreeSelectionListener(treeSelectionListener); + tree.addMouseListener(mouseListener); + tree.addKeyListener(keyListener); + tree.addPropertyChangeListener(selectionModelPropertyChangeListener); + tree.addComponentListener(componentListener); + tree.addTreeExpansionListener(treeExpansionListener); + if (treeModel != null) + treeModel.addTreeModelListener(treeModelListener); + + if (mod != null) { - instance = new MetalTreeUI(); - instances.put(component, instance); + TreePath path = new TreePath(mod.getRoot()); + if (!tree.isExpanded(path)) + toggleExpandState(path); } - else - instance = (MetalTreeUI) o; + + completeUIInstall(); + } + + /** + * Reverses configuration which was done on the specified component during + * installUI. This method is invoked when this UIComponent instance is being + * removed as the UI delegate for the specified component. This method should + * undo the configuration performed in installUI, being careful to leave the + * JComponent instance in a clean state (no extraneous listeners, + * look-and-feel-specific property objects, etc.). This should include + * the following: + * 1. Remove any UI-set borders from the component. + * 2. Remove any UI-set layout managers on the component. + * 3. Remove any UI-added sub-components from the component. + * 4. Remove any UI-added event/property listeners from the component. + * 5. Remove any UI-installed keyboard UI from the component. + * 6. Nullify any allocated instance data objects to allow for GC. + */ + public void uninstallUI(JComponent c) + { + tree.setFont(null); + tree.setForeground(null); + tree.setBackground(null); + + uninstallKeyboardActions(); + + tree.removePropertyChangeListener(propertyChangeListener); + tree.removeFocusListener(focusListener); + tree.removeTreeSelectionListener(treeSelectionListener); + tree.removeMouseListener(mouseListener); + tree.removeKeyListener(keyListener); + tree.removePropertyChangeListener(selectionModelPropertyChangeListener); + tree.removeComponentListener(componentListener); + tree.removeTreeExpansionListener(treeExpansionListener); + + TreeCellEditor tce = tree.getCellEditor(); + if (tce != null) + tce.removeCellEditorListener(cellEditorListener); + TreeModel tm = tree.getModel(); + if (tm != null) + tm.removeTreeModelListener(treeModelListener); + + tree = null; + uninstallComponents(); + completeUIUninstall(); + } + + /** + * This function converts between the string passed into the client + * property and the internal representation (currently an int). + * + * @param lineStyleFlag - String representation + */ + protected void decodeLineStyle(Object lineStyleFlag) + { + // FIXME: not implemented + } - return instance; + /** + * Checks if the location is in expand control. + * + * @param row - current row + * @param rowLevel - current level + * @param mouseX - current x location of the mouse click + * @param mouseY - current y location of the mouse click + */ + protected boolean isLocationInExpandControl(int row, int rowLevel, + int mouseX, int mouseY) + { + return super.isLocationInExpandControl(tree.getPathForRow(row), + mouseX, mouseY); + } + + /** + * Paints the specified component appropriate for the look and feel. + * This method is invoked from the ComponentUI.update method when the + * specified component is being painted. Subclasses should override this + * method and use the specified Graphics object to render the content of + * the component. + * + * @param g - the current graphics configuration. + * @param c - the current component to draw + */ + public void paint(Graphics g, JComponent c) + { + // Calls BasicTreeUI's paint since it takes care of painting all + // types of icons. + super.paint(g, c); + } + + /** + * Paints the horizontal separators. + * + * @param g - the current graphics configuration. + * @param c - the current component to draw + */ + protected void paintHorizontalSeparators(Graphics g, JComponent c) + { + // FIXME: not implemented + } + + + /** + * Paints the vertical part of the leg. The receiver should NOT modify + * clipBounds, insets. + * + * @param g - the current graphics configuration. + * @param clipBounds - + * @param insets - + * @param path - the current path + */ + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, TreePath path) + { + super.paintVerticalPartOfLeg(g, clipBounds, insets, path); + } + + /** + * Paints the horizontal part of the leg. The receiver should NOT \ + * modify clipBounds, or insets. + * NOTE: parentRow can be -1 if the root is not visible. + */ + protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, + isExpanded, hasBeenExpanded, isLeaf); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java index a342ee02bd3..c0b4e657676 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java @@ -1,4 +1,4 @@ -/* Metaltils.java +/* MetalUtils.java Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,7 +38,12 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Component; import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.TexturePaint; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; /** * Some utility and helper methods for the Metal Look & Feel. @@ -49,6 +54,21 @@ class MetalUtils { /** + * The typical metal pattern for use with Graphics2D. + */ + static BufferedImage pattern2D; + + /** + * The light color to draw the pattern. + */ + static Color lightColor; + + /** + * The dark color to draw to draw the pattern. + */ + static Color darkColor; + + /** * Fills a rectangle with the typical Metal pattern. * * @param g the <code>Graphics</code> context to use @@ -57,31 +77,78 @@ class MetalUtils * @param y the Y coordinate of the upper left corner of the rectangle to * fill * @param w the width of the rectangle to fill - * @param w the height of the rectangle to fill + * @param h the height of the rectangle to fill * @param light the light color to use * @param dark the dark color to use */ - static void fillMetalPattern(Graphics g, int x, int y, int w, int h, + static void fillMetalPattern(Component c, Graphics g, int x, int y, int w, int h, Color light, Color dark) { - int xOff = 0; - for (int mY = y; mY < (y + h); mY++) + if (g instanceof Graphics2D) + fillMetalPattern2D((Graphics2D) g, x, y, w, h, light, dark); + else { - // set color alternating with every line - if ((mY % 2) == 0) - g.setColor(light); - else - g.setColor(dark); - - for (int mX = x + (xOff); mX < (x + w); mX += 4) + int xOff = 0; + for (int mY = y; mY < (y + h); mY++) { - g.drawLine(mX, mY, mX, mY); + // set color alternating with every line + if (((mY - y) % 2) == 0) + g.setColor(light); + else + g.setColor(dark); + + for (int mX = x + (xOff); mX < (x + w); mX += 4) + { + g.drawLine(mX, mY, mX, mY); + } + + // increase x offset + xOff++; + if (xOff > 3) + xOff = 0; } + } + } + + /** + * Fills a rectangle with the typical Metal pattern using Java2D. + * + * @param g2d the <code>Graphics2D</code> context to use + * @param x the X coordinate of the upper left corner of the rectangle to + * fill + * @param y the Y coordinate of the upper left corner of the rectangle to + * fill + * @param w the width of the rectangle to fill + * @param h the height of the rectangle to fill + */ + static void fillMetalPattern2D(Graphics2D g2d, int x, int y, int w, int h, + Color light, Color dark) + { + if (pattern2D == null || !darkColor.equals(dark) || !lightColor.equals(light)) + initializePattern(light, dark); + + // Prepare the texture. + TexturePaint texture = + new TexturePaint(pattern2D, new Rectangle2D.Double(0., 0., 4., 4.)); + g2d.setPaint(texture); + g2d.fillRect(x, y, w, h); + } - // increase x offset - xOff++; - if (xOff > 3) - xOff = 0; - } + /** + * Initializes the pattern image. + */ + static void initializePattern(Color light, Color dark) + { + pattern2D = new BufferedImage(4, 4, BufferedImage.TYPE_INT_ARGB); + lightColor = light; + darkColor = dark; + Graphics g = pattern2D.getGraphics(); + g.setColor(light); + g.fillRect(0, 0, 1, 1); + g.fillRect(2, 2, 1, 1); + g.setColor(dark); + g.fillRect(1, 1, 1, 1); + g.fillRect(3, 3, 1, 1); + g.dispose(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java new file mode 100644 index 00000000000..85a8cb1ff86 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java @@ -0,0 +1,209 @@ +/* DefaultMetalTheme.java -- A modern theme for the Metal L&F + Copyright (C) 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 javax.swing.plaf.metal; + +import javax.swing.UIDefaults; +import javax.swing.plaf.ColorUIResource; + +/** + * A modern theme for the Metal Look & Feel. + * @since 1.5 + * + * @author Roman Kennke (roman@kennke.org) + */ +public class OceanTheme extends DefaultMetalTheme +{ + /** + * The OceanTheme value for black. + */ + static final ColorUIResource BLACK = new ColorUIResource(51, 51, 51); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY1 = new ColorUIResource(99, 130, 191); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY2 = new ColorUIResource(163, 184, 204); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY3 = new ColorUIResource(184, 207, 229); + + /** + * The OceanTheme value for secondary1. + */ + static final ColorUIResource SECONDARY1 = new ColorUIResource(122, 138, 153); + + /** + * The OceanTheme value for secondary2. + */ + static final ColorUIResource SECONDARY2 = new ColorUIResource(184, 207, 229); + + /** + * The OceanTheme value for secondary3. + */ + static final ColorUIResource SECONDARY3 = new ColorUIResource(238, 238, 238); + + /** + * The OceanTheme value for inactive control text. + */ + static final ColorUIResource INACTIVE_CONTROL_TEXT = + new ColorUIResource(153, 153, 153); + + /** + * Returns the name of this theme, "Ocean" + */ + public String getName() + { + return "Ocean"; + } + + /** + * Returns the color for control text, which is the + * value of the theme's black value. + */ + public ColorUIResource getControlTextColor() + { + return getBlack(); + } + + /** + * Returns the desktop color, which is the theme's white color. + */ + public ColorUIResource getDesktopColor() + { + return getWhite(); + } + + /** + * Returns the color for inactive control text, which is the + * RGB value (153, 153, 153). + */ + public ColorUIResource getInactiveControlTextColor() + { + return INACTIVE_CONTROL_TEXT; + } + + /** + * Returns the OceanTheme's color for disabled menu foreground, + * + */ + public ColorUIResource getMenuDisabledForeground() + { + return INACTIVE_CONTROL_TEXT; + } + + + /** + * Returns the OceanTheme's color for black, the RGB value + * (51, 51, 51). + * + * @return Returns the OceanTheme's value for black + */ + protected ColorUIResource getBlack() + { + return BLACK; + } + + /** + * Return the OceanTheme's value for primary 1, the RGB value + * (99, 130, 191). + */ + protected ColorUIResource getPrimary1() + { + return PRIMARY1; + } + + /** + * Return the OceanTheme's value for primary 2, the RGB value + * (163, 184, 204). + */ + protected ColorUIResource getPrimary2() + { + return PRIMARY2; + } + + /** + * Return the OceanTheme's value for primary 1, the RGB value + * (184, 207, 229). + */ + protected ColorUIResource getPrimary3() + { + return PRIMARY3; + } + + /** + * Return the OceanTheme's value for secondary 1, the RGB value + * (122, 138, 153). + */ + protected ColorUIResource getSecondary1() + { + return SECONDARY1; + } + + /** + * Return the OceanTheme's value for secondary 2, the RGB value + * (184, 207, 229). + */ + protected ColorUIResource getSecondary2() + { + return SECONDARY2; + } + /** + * Return the OceanTheme's value for secondary 3, the RGB value + * (238, 238, 238). + */ + protected ColorUIResource getSecondary3() + { + return SECONDARY3; + } + + /** + * Adds customized entries to the UIDefaults table. + * + * @param defaults the UI defaults table + */ + public void addCustomEntriesToTable(UIDefaults defaults) + { + defaults.put("Button.rollover", Boolean.TRUE); + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/package.html b/libjava/classpath/javax/swing/plaf/metal/package.html index 2ea787bb5e2..8675493b68c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/package.html +++ b/libjava/classpath/javax/swing/plaf/metal/package.html @@ -40,7 +40,16 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.swing.plaf.metal</title></head> <body> -<p>Provides a cross-platform look and feel known as "Metal".</p> - +<p>Provides a cross-platform look and feel known as "Metal". To install this +look and feel, add the following code (or something similar) +near the start of your application:</p> +<pre>try + { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + } +catch (UnsupportedLookAndFeelException e) + { + e.printStackTrace(); + }</pre> </body> </html> diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java index a70a8ff690e..2bd358dd01e 100644 --- a/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java @@ -56,6 +56,7 @@ public class MultiLookAndFeel extends LookAndFeel { */ public MultiLookAndFeel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java index 349f4baad12..a187d74a686 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java +++ b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java @@ -43,8 +43,10 @@ import java.awt.Component; import java.awt.Rectangle; import java.io.Serializable; +import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JTable; +import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.JTextField; @@ -64,10 +66,21 @@ public class DefaultTableCellRenderer extends JLabel { public UIResource() { + super(); } } /** + * Stores the color set by setForeground(). + */ + Color foreground; + + /** + * Stores the color set by setBackground(). + */ + Color background; + + /** * Creates a default table cell renderer with an empty border. */ public DefaultTableCellRenderer() @@ -83,6 +96,7 @@ public class DefaultTableCellRenderer extends JLabel public void setForeground(Color c) { super.setForeground(c); + foreground = c; } /** @@ -93,6 +107,7 @@ public class DefaultTableCellRenderer extends JLabel public void setBackground(Color c) { super.setBackground(c); + background = c; } /** @@ -104,6 +119,8 @@ public class DefaultTableCellRenderer extends JLabel public void updateUI() { super.updateUI(); + background = null; + foreground = null; } /** @@ -137,17 +154,41 @@ public class DefaultTableCellRenderer extends JLabel if (isSelected) { - setBackground(table.getSelectionBackground()); - setForeground(table.getSelectionForeground()); + super.setBackground(table.getSelectionBackground()); + super.setForeground(table.getSelectionForeground()); } else { - setBackground(table.getBackground()); - setForeground(table.getForeground()); + if (background != null) + super.setBackground(background); + else + super.setBackground(table.getBackground()); + if (foreground != null) + super.setForeground(foreground); + else + super.setForeground(table.getForeground()); } + if (hasFocus) + { + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + if (table.isCellEditable(row, column)) + { + super.setBackground(UIManager.getColor("Table.focusCellBackground")); + super.setForeground(UIManager.getColor("Table.focusCellForeground")); + } + } + else + setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + setEnabled(table.isEnabled()); setFont(table.getFont()); + + // If the current background is equal to the table's background, then we + // can avoid filling the background by setting the renderer opaque. + Color back = getBackground(); + setOpaque(back != null && back.equals(table.getBackground())); + return this; } diff --git a/libjava/classpath/javax/swing/table/JTableHeader.java b/libjava/classpath/javax/swing/table/JTableHeader.java index 45586da2009..163509a45c2 100644 --- a/libjava/classpath/javax/swing/table/JTableHeader.java +++ b/libjava/classpath/javax/swing/table/JTableHeader.java @@ -61,9 +61,14 @@ import javax.accessibility.AccessibleValue; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableColumnModelListener; import javax.swing.plaf.TableHeaderUI; public class JTableHeader extends JComponent + implements TableColumnModelListener, Accessible { protected class AccessibleJTableHeader extends AccessibleJComponent { @@ -305,11 +310,6 @@ public class JTableHeader extends JComponent private static final long serialVersionUID = 5144633983372967710L; /** - * The accessibleContext property. - */ - AccessibleContext accessibleContext; - - /** * The columnModel property. */ protected TableColumnModel columnModel; @@ -373,17 +373,8 @@ public class JTableHeader extends JComponent */ public JTableHeader(TableColumnModel cm) { - accessibleContext = new AccessibleJTableHeader(); columnModel = cm == null ? createDefaultColumnModel() : cm; - draggedColumn = null; - draggedDistance = 0; - opaque = true; - reorderingAllowed = true; - resizingAllowed = true; - resizingColumn = null; - table = null; - updateTableInRealTime = true; - cellRenderer = createDefaultRenderer(); + initializeLocalVars(); updateUI(); } @@ -504,7 +495,9 @@ public class JTableHeader extends JComponent */ public void setColumnModel(TableColumnModel c) { + columnModel.removeColumnModelListener(this); columnModel = c; + columnModel.addColumnModelListener(this); } /** @@ -619,7 +612,7 @@ public class JTableHeader extends JComponent public Rectangle getHeaderRect(int column) { - Rectangle r = getTable().getCellRect(-1, column, true); + Rectangle r = getTable().getCellRect(-1, column, false); r.height = getHeight(); return r; } @@ -665,4 +658,88 @@ public class JTableHeader extends JComponent return -1; } + + /** + * Receives notification when a column is added to the column model. + * + * @param event the table column model event + */ + public void columnAdded(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column margin changes in the column model. + * + * @param event the table column model event + */ + public void columnMarginChanged(ChangeEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column is moved within the column model. + * + * @param event the table column model event + */ + public void columnMoved(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column is removed from the column model. + * + * @param event the table column model event + */ + public void columnRemoved(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when the column selection has changed. + * + * @param event the table column model event + */ + public void columnSelectionChanged(ListSelectionEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Validates the layout of this table header and repaints it. This is + * equivalent to <code>revalidate()</code> followed by + * <code>repaint()</code>. + */ + public void resizeAndRepaint() + { + revalidate(); + repaint(); + } + + /** + * Initializes the fields and properties of this class with default values. + * This is called by the constructors. + */ + protected void initializeLocalVars() + { + accessibleContext = new AccessibleJTableHeader(); + draggedColumn = null; + draggedDistance = 0; + opaque = true; + reorderingAllowed = true; + resizingAllowed = true; + resizingColumn = null; + table = null; + updateTableInRealTime = true; + cellRenderer = createDefaultRenderer(); + } } diff --git a/libjava/classpath/javax/swing/table/TableColumn.java b/libjava/classpath/javax/swing/table/TableColumn.java index 9c36bb05ab0..9f06c5b7fcc 100644 --- a/libjava/classpath/javax/swing/table/TableColumn.java +++ b/libjava/classpath/javax/swing/table/TableColumn.java @@ -402,7 +402,11 @@ public class TableColumn if (width == oldWidth) return; - firePropertyChange(COLUMN_WIDTH_PROPERTY, oldWidth, width); + // We do have a constant field COLUMN_WIDTH_PROPERTY, + // however, tests show that the actual fired property name is 'width' + // and even Sun's API docs say that this constant field is obsolete and + // not used. + firePropertyChange("width", oldWidth, width); } /** @@ -422,12 +426,16 @@ public class TableColumn */ public void setPreferredWidth(int preferredWidth) { + int oldPrefWidth = this.preferredWidth; + if (preferredWidth < minWidth) this.preferredWidth = minWidth; else if (preferredWidth > maxWidth) this.preferredWidth = maxWidth; else this.preferredWidth = preferredWidth; + + firePropertyChange("preferredWidth", oldPrefWidth, this.preferredWidth); } /** diff --git a/libjava/classpath/javax/swing/table/TableColumnModel.java b/libjava/classpath/javax/swing/table/TableColumnModel.java index 76a145604db..b006f9ad4bb 100644 --- a/libjava/classpath/javax/swing/table/TableColumnModel.java +++ b/libjava/classpath/javax/swing/table/TableColumnModel.java @@ -50,6 +50,7 @@ import javax.swing.event.TableColumnModelListener; * * @author Andrew Selkirk */ +// FIXME: The API documentation in this class is incomplete. public interface TableColumnModel { /** @@ -107,7 +108,7 @@ public interface TableColumnModel * @throws IllegalArgumentException if <code>identifier</code> is * <code>null</code> or there is no column with that identifier. */ - int getColumnIndex(Object columnIdentifier); + int getColumnIndex(Object identifier); /** * Returns the <code>TableColumn</code> at the specified index. @@ -169,7 +170,6 @@ public interface TableColumnModel /** * getSelectionModel - * @param column TableColumn */ ListSelectionModel getSelectionModel(); diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index 3c9a4d497a5..baf8608b888 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -65,11 +65,10 @@ import javax.swing.undo.UndoableEdit; * @author original author unknown * @author Roman Kennke (roman@kennke.org) */ -public abstract class AbstractDocument - implements Document, Serializable +public abstract class AbstractDocument implements Document, Serializable { - /** The serial version UID for this class as of JDK1.4. */ - private static final long serialVersionUID = -116069779446114664L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 6842927725919637215L; /** * Standard error message to indicate a bad location. @@ -128,7 +127,28 @@ public abstract class AbstractDocument * Manages event listeners for this <code>Document</code>. */ protected EventListenerList listenerList = new EventListenerList(); + + /** + * Stores the current writer thread. Used for locking. + */ + private Thread currentWriter = null; + + /** + * The number of readers. Used for locking. + */ + private int numReaders = 0; + + /** + * Tells if there are one or more writers waiting. + */ + private int numWritersWaiting = 0; + + /** + * A condition variable that readers and writers wait on. + */ + Object documentCV = new Object(); + /** * Creates a new <code>AbstractDocument</code> with the specified * {@link Content} model. @@ -332,7 +352,7 @@ public abstract class AbstractDocument * @see GapContent * @see StringContent */ - protected Content getContent() + protected final Content getContent() { return content; } @@ -348,8 +368,7 @@ public abstract class AbstractDocument */ protected Thread getCurrentWriter() { - // FIXME: Implement locking! - return null; + return currentWriter; } /** @@ -516,13 +535,18 @@ public abstract class AbstractDocument // Just return when no text to insert was given. if (text == null || text.length() == 0) return; - DefaultDocumentEvent event = new DefaultDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT); - content.insertString(offset, text); + + writeLock(); + UndoableEdit undo = content.insertString(offset, text); insertUpdate(event, attributes); + writeUnlock(); + fireInsertUpdate(event); + if (undo != null) + fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); } /** @@ -566,10 +590,28 @@ public abstract class AbstractDocument } /** - * Blocks until a read lock can be obtained. + * Blocks until a read lock can be obtained. Must block if there is + * currently a writer modifying the <code>Document</code>. */ public void readLock() { + if (currentWriter != null && currentWriter.equals(Thread.currentThread())) + return; + synchronized (documentCV) + { + while (currentWriter != null || numWritersWaiting > 0) + { + try + { + documentCV.wait(); + } + catch (InterruptedException ie) + { + throw new Error("interrupted trying to get a readLock"); + } + } + numReaders++; + } } /** @@ -578,6 +620,40 @@ public abstract class AbstractDocument */ public void readUnlock() { + // Note we could have a problem here if readUnlock was called without a + // prior call to readLock but the specs simply warn users to ensure that + // balance by using a finally block: + // readLock() + // try + // { + // doSomethingHere + // } + // finally + // { + // readUnlock(); + // } + + // All that the JDK seems to check for is that you don't call unlock + // more times than you've previously called lock, but it doesn't make + // sure that the threads calling unlock were the same ones that called lock + + // FIXME: the reference implementation throws a + // javax.swing.text.StateInvariantError here + if (numReaders == 0) + throw new IllegalStateException("document lock failure"); + + synchronized (documentCV) + { + // If currentWriter is not null, the application code probably had a + // writeLock and then tried to obtain a readLock, in which case + // numReaders wasn't incremented + if (currentWriter == null) + { + numReaders --; + if (numReaders == 0 && numWritersWaiting != 0) + documentCV.notify(); + } + } } /** @@ -595,10 +671,42 @@ public abstract class AbstractDocument DefaultDocumentEvent event = new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.REMOVE); + + // Here we set up the parameters for an ElementChange, if one + // needs to be added to the DocumentEvent later + Element root = getDefaultRootElement(); + int start = root.getElementIndex(offset); + int end = root.getElementIndex(offset + length); + + Element[] removed = new Element[end - start + 1]; + for (int i = start; i <= end; i++) + removed[i - start] = root.getElement(i); + removeUpdate(event); - content.remove(offset, length); + + Element[] added = new Element[1]; + added[0] = root.getElement(start); + boolean shouldFire = content.getString(offset, length).length() != 0; + + writeLock(); + UndoableEdit temp = content.remove(offset, length); + writeUnlock(); + postRemoveUpdate(event); - fireRemoveUpdate(event); + + GapContent.UndoRemove changes = null; + if (content instanceof GapContent) + changes = (GapContent.UndoRemove) temp; + + if (changes != null && !(start == end)) + { + // We need to add an ElementChange to our DocumentEvent + ElementEdit edit = new ElementEdit (root, start, removed, added); + event.addEdit(edit); + } + + if (shouldFire) + fireRemoveUpdate(event); } /** @@ -713,7 +821,15 @@ public abstract class AbstractDocument */ public void render(Runnable runnable) { - // FIXME: Implement me! + readLock(); + try + { + runnable.run(); + } + finally + { + readUnlock(); + } } /** @@ -725,6 +841,7 @@ public abstract class AbstractDocument */ public void setAsynchronousLoadPriority(int p) { + // TODO: Implement this properly. } /** @@ -739,11 +856,30 @@ public abstract class AbstractDocument } /** - * Blocks until a write lock can be obtained. + * Blocks until a write lock can be obtained. Must wait if there are + * readers currently reading or another thread is currently writing. */ protected void writeLock() { - // FIXME: Implement me. + if (currentWriter!= null && currentWriter.equals(Thread.currentThread())) + return; + synchronized (documentCV) + { + numWritersWaiting++; + while (numReaders > 0) + { + try + { + documentCV.wait(); + } + catch (InterruptedException ie) + { + throw new Error("interruped while trying to obtain write lock"); + } + } + numWritersWaiting --; + currentWriter = Thread.currentThread(); + } } /** @@ -752,7 +888,14 @@ public abstract class AbstractDocument */ protected void writeUnlock() { - // FIXME: Implement me. + synchronized (documentCV) + { + if (Thread.currentThread().equals(currentWriter)) + { + currentWriter = null; + documentCV.notifyAll(); + } + } } /** @@ -970,8 +1113,8 @@ public abstract class AbstractDocument public abstract class AbstractElement implements Element, MutableAttributeSet, TreeNode, Serializable { - /** The serial version UID for AbstractElement. */ - private static final long serialVersionUID = 1265312733007397733L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 1712240033321461704L; /** The number of characters that this Element spans. */ int count; @@ -1231,6 +1374,9 @@ public abstract class AbstractDocument /** * Returns the resolve parent of this element. + * This is taken from the AttributeSet, but if this is null, + * this method instead returns the Element's parent's + * AttributeSet * * @return the resolve parent of this element * @@ -1238,7 +1384,9 @@ public abstract class AbstractDocument */ public AttributeSet getResolveParent() { - return attributes.getResolveParent(); + if (attributes.getResolveParent() != null) + return attributes.getResolveParent(); + return element_parent.getAttributes(); } /** @@ -1355,49 +1503,6 @@ public abstract class AbstractDocument public abstract int getStartOffset(); /** - * Prints diagnostic information to the specified stream. - * - * @param stream the stream to dump to - * @param indent the indentation level - * @param element the element to be dumped - */ - private void dumpElement(PrintStream stream, String indent, - Element element) - { - // FIXME: Should the method be removed? - System.out.println(indent + "<" + element.getName() +">"); - - if (element.isLeaf()) - { - int start = element.getStartOffset(); - int end = element.getEndOffset(); - String text = ""; - try - { - text = getContent().getString(start, end - start); - } - catch (BadLocationException e) - { - AssertionError error = - new AssertionError("BadLocationException should not be " - + "thrown here. start = " + start - + ", end = " + end); - error.initCause(e); - throw error; - } - System.out.println(indent + " [" - + start + "," - + end + "][" - + text + "]"); - } - else - { - for (int i = 0; i < element.getElementCount(); ++i) - dumpElement(stream, indent + " ", element.getElement(i)); - } - } - - /** * Prints diagnostic output to the specified stream. * * @param stream the stream to write to @@ -1405,10 +1510,66 @@ public abstract class AbstractDocument */ public void dump(PrintStream stream, int indent) { - String indentStr = ""; + StringBuffer b = new StringBuffer(); for (int i = 0; i < indent; ++i) - indentStr += " "; - dumpElement(stream, indentStr, this); + b.append(' '); + b.append('<'); + b.append(getName()); + // Dump attributes if there are any. + if (getAttributeCount() > 0) + { + b.append('\n'); + Enumeration attNames = getAttributeNames(); + while (attNames.hasMoreElements()) + { + for (int i = 0; i < indent + 2; ++i) + b.append(' '); + Object attName = attNames.nextElement(); + b.append(attName); + b.append('='); + Object attribute = getAttribute(attName); + b.append(attribute); + b.append('\n'); + } + } + b.append(">\n"); + + // Dump element content for leaf elements. + if (isLeaf()) + { + for (int i = 0; i < indent + 2; ++i) + b.append(' '); + int start = getStartOffset(); + int end = getEndOffset(); + b.append('['); + b.append(start); + b.append(','); + b.append(end); + b.append("]["); + try + { + b.append(getDocument().getText(start, end - start)); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("BadLocationException " + + "must not be thrown " + + "here."); + err.initCause(ex); + throw err; + } + b.append("]\n"); + } + stream.print(b.toString()); + + // Dump child elements if any. + int count = getElementCount(); + for (int i = 0; i < count; ++i) + { + Element el = getElement(i); + if (el instanceof AbstractElement) + ((AbstractElement) el).dump(stream, indent + 2); + } } } @@ -1418,8 +1579,8 @@ public abstract class AbstractDocument */ public class BranchElement extends AbstractElement { - /** The serial version UID for BranchElement. */ - private static final long serialVersionUID = -8595176318868717313L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6037216547466333183L; /** The child elements of this BranchElement. */ private Element[] children = new Element[0]; @@ -1503,19 +1664,30 @@ public abstract class AbstractDocument */ public int getElementIndex(int offset) { - // If we have no children, return -1. - if (getElementCount() == 0) - return - 1; - + // If offset is less than the start offset of our first child, + // return 0 + if (offset < getStartOffset()) + return 0; + // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length; ++index) + for (int index = 0; index < children.length - 1; ++index) { Element elem = children[index]; if ((elem.getStartOffset() <= offset) && (offset < elem.getEndOffset())) return index; + // If the next element's start offset is greater than offset + // then we have to return the closest Element, since no Elements + // will contain the offset + if (children[index + 1].getStartOffset() > offset) + { + if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset)) + return index + 1; + else + return index; + } } // If offset is greater than the index of the last element, return @@ -1642,8 +1814,8 @@ public abstract class AbstractDocument public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent { - /** The serial version UID of DefaultDocumentEvent. */ - private static final long serialVersionUID = -7406103236022413522L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 5230037221564563284L; /** The starting offset of the change. */ private int offset; @@ -1748,7 +1920,7 @@ public abstract class AbstractDocument return (DocumentEvent.ElementChange) changes.get(elem); } } - + /** * An implementation of {@link DocumentEvent.ElementChange} to be added * to {@link DefaultDocumentEvent}s. @@ -1843,8 +2015,8 @@ public abstract class AbstractDocument */ public class LeafElement extends AbstractElement { - /** The serial version UID of LeafElement. */ - private static final long serialVersionUID = 5115368706941283802L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -8906306331347768017L; /** Manages the start offset of this element. */ Position startPos; diff --git a/libjava/classpath/javax/swing/text/AttributeSet.java b/libjava/classpath/javax/swing/text/AttributeSet.java index 2f1f1890bae..01d148c067b 100644 --- a/libjava/classpath/javax/swing/text/AttributeSet.java +++ b/libjava/classpath/javax/swing/text/AttributeSet.java @@ -58,6 +58,7 @@ public interface AttributeSet */ static interface CharacterAttribute { + // This interface is a marker interface and has no methods. } /** @@ -65,6 +66,7 @@ public interface AttributeSet */ static interface ColorAttribute { + // This interface is a marker interface and has no methods. } /** @@ -72,6 +74,7 @@ public interface AttributeSet */ static interface FontAttribute { + // This interface is a marker interface and has no methods. } /** @@ -79,6 +82,7 @@ public interface AttributeSet */ static interface ParagraphAttribute { + // This interface is a marker interface and has no methods. } /** @@ -99,7 +103,7 @@ public interface AttributeSet * <code>false</code> otherwise. * * @param name the name of the requested attribute - * @param the value of the requested attribute + * @param value the value of the requested attribute * * @return <code>true</code> if this <code>AttributeSet</code> contains * an attribute with the specified <code>name</code> and diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java index 0f8ba1ce15e..f201045dbdb 100644 --- a/libjava/classpath/javax/swing/text/BoxView.java +++ b/libjava/classpath/javax/swing/text/BoxView.java @@ -155,8 +155,9 @@ public class BoxView * automatically when any of the child view changes its preferences * via {@link #preferenceChanged(View, boolean, boolean)}. * - * The layout will be updated the next time when {@link #setSize()} is - * called, typically from within the {@link #paint()} method. + * The layout will be updated the next time when + * {@link #setSize(float, float)} is called, typically from within the + * {@link #paint(Graphics, Shape)} method. * * Valid values for the axis are {@link View#X_AXIS} and * {@link View#Y_AXIS}. @@ -216,12 +217,11 @@ public class BoxView * @param alloc the allocated region for the child to paint into * @param index the index of the child to be painted * - * @see {@link #childAllocation} + * @see #childAllocation(int, Rectangle) */ protected void paintChild(Graphics g, Rectangle alloc, int index) { View child = getView(index); - childAllocation(index, alloc); child.paint(g, alloc); } @@ -301,18 +301,15 @@ public class BoxView setSize(bounds.width, bounds.height); Rectangle inside = getInsideAllocation(a); - Rectangle copy = new Rectangle(inside); int count = getViewCount(); for (int i = 0; i < count; ++i) { - // TODO: Figure out if the parameter to paintChild is meant to - // be the child allocation or the allocation of this BoxView. - // I assume the second option here. - // We pass this method a copy of the inside rectangle here because - // it modifies the actual values. copy.setBounds(inside); - paintChild(g, copy, i); + childAllocation(i, copy); + if (!copy.isEmpty() + && g.hitClip(copy.x, copy.y, copy.width, copy.height)) + paintChild(g, copy, i); } } @@ -362,6 +359,24 @@ public class BoxView } /** + * Calculates the layout of the children of this <code>BoxView</code> along + * the specified axis. + * + * @param span the target span + * @param axis the axis that is examined + * @param offsets an empty array, filled with the offsets of the children + * @param spans an empty array, filled with the spans of the children + */ + protected void baselineLayout(int span, int axis, int[] offsets, + int[] spans) + { + if (axis == myAxis) + layoutMajorAxis(span, axis, offsets, spans); + else + layoutMinorAxis(span, axis, offsets, spans); + } + + /** * Calculates the size requirements of this <code>BoxView</code> along * its major axis, that is the axis specified in the constructor. * @@ -375,27 +390,8 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - if (sr == null) - sr = new SizeRequirements(); - else - { - sr.maximum = 0; - sr.minimum = 0; - sr.preferred = 0; - sr.alignment = 0.5F; - } - - int count = getViewCount(); - - // Sum up the sizes of the children along the specified axis. - for (int i = 0; i < count; ++i) - { - View child = getView(i); - sr.minimum += child.getMinimumSpan(axis); - sr.preferred += child.getPreferredSpan(axis); - sr.maximum += child.getMaximumSpan(axis); - } - return sr; + SizeRequirements[] childReqs = getChildRequirements(axis); + return SizeRequirements.getTiledSizeRequirements(childReqs); } /** @@ -413,48 +409,8 @@ public class BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sr) { - if (sr == null) - sr = new SizeRequirements(); - else - { - sr.maximum = 0; - sr.minimum = 0; - sr.preferred = 0; - sr.alignment = 0.5F; - } - - int count = getViewCount(); - - int aboveBaseline = 0; - int belowBaseline = 0; - int aboveBaselineMin = 0; - int belowBaselineMin = 0; - int aboveBaselineMax = 0; - int belowBaselineMax = 0; - - for (int i = 0; i < count; ++i) - { - View child = getView(i); - float align = child.getAlignment(axis); - int pref = (int) child.getPreferredSpan(axis); - int min = (int) child.getMinimumSpan(axis); - int max = (int) child.getMaximumSpan(axis); - aboveBaseline += (int) (align * pref); - belowBaseline += (int) ((1.F - align) * pref); - aboveBaselineMin += (int) (align * min); - belowBaselineMin += (int) ((1.F - align) * min); - aboveBaselineMax += (int) (align * max); - belowBaselineMax += (int) ((1.F - align) * max); - } - sr.minimum = aboveBaselineMin + belowBaselineMin; - sr.maximum = aboveBaselineMax + belowBaselineMax; - sr.preferred = aboveBaseline + belowBaseline; - if (aboveBaseline == 0) - sr.alignment = 1.0F; - else - sr.alignment = (float) (sr.preferred / aboveBaseline); - - return sr; + SizeRequirements[] childReqs = getChildRequirements(axis); + return SizeRequirements.getAlignedSizeRequirements(childReqs); } /** @@ -569,19 +525,8 @@ public class BoxView */ protected void layout(int width, int height) { - this.width = width; - this.height = height; - - if (myAxis == X_AXIS) - { - layoutMajorAxis(width, X_AXIS, offsetsX, spansX); - layoutMinorAxis(height, Y_AXIS, offsetsY, spansY); - } - else - { - layoutMajorAxis(height, Y_AXIS, offsetsY, spansY); - layoutMinorAxis(width, X_AXIS, offsetsX, spansX); - } + baselineLayout(width, X_AXIS, offsetsX, spansX); + baselineLayout(height, Y_AXIS, offsetsY, spansY); } /** @@ -591,28 +536,16 @@ public class BoxView * to layout the children * @param axis the axis along which the layout is performed * @param offsets the array that holds the offsets of the children on exit - * @param offsets the array that holds the spans of the children on exit + * @param spans the array that holds the spans of the children on exit */ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - // Allocate SizeRequirements for each child view. - int count = getViewCount(); - SizeRequirements[] childReqs = new SizeRequirements[count]; - for (int i = 0; i < count; ++i) - { - View view = getView(i); - childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), - (int) view.getPreferredSpan(axis), - (int) view.getMaximumSpan(axis), - view.getAlignment(axis)); - } - + SizeRequirements[] childReqs = getChildRequirements(axis); // Calculate the spans and offsets using the SizeRequirements uility // methods. SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs, offsets, spans); - validateLayout(axis); } @@ -623,26 +556,21 @@ public class BoxView * to layout the children * @param axis the axis along which the layout is performed * @param offsets the array that holds the offsets of the children on exit - * @param offsets the array that holds the spans of the children on exit + * @param spans the array that holds the spans of the children on exit */ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - // Allocate SizeRequirements for each child view. - int count = getViewCount(); - SizeRequirements[] childReqs = new SizeRequirements[count]; - for (int i = 0; i < count; ++i) - { - View view = getView(i); - childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), - (int) view.getPreferredSpan(axis), - (int) view.getMaximumSpan(axis), - view.getAlignment(axis)); - } - + SizeRequirements[] childReqs = getChildRequirements(axis); // Calculate the spans and offsets using the SizeRequirements uility // methods. - SizeRequirements.calculateAlignedPositions(targetSpan, null, childReqs, + // TODO: This might be an opportunity for performance optimization. Here + // we could use a cached instance of SizeRequirements instead of passing + // null to baselineRequirements. However, this would involve rewriting + // the baselineRequirements() method to not use the SizeRequirements + // utility method, since they cannot reuse a cached instance. + SizeRequirements total = baselineRequirements(axis, null); + SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs, offsets, spans); validateLayout(axis); } @@ -692,6 +620,9 @@ public class BoxView layoutChanged(X_AXIS); if (this.height != (int) height) layoutChanged(Y_AXIS); + + this.width = (int) width; + this.height = (int) height; Rectangle outside = new Rectangle(0, 0, this.width, this.height); Rectangle inside = getInsideAllocation(outside); @@ -711,4 +642,99 @@ public class BoxView if (axis == Y_AXIS) yLayoutValid = true; } + + /** + * Returns the size requirements of this view's children for the major + * axis. + * + * @return the size requirements of this view's children for the major + * axis + */ + SizeRequirements[] getChildRequirements(int axis) + { + // Allocate SizeRequirements for each child view. + int count = getViewCount(); + SizeRequirements[] childReqs = new SizeRequirements[count]; + for (int i = 0; i < count; ++i) + { + View view = getView(i); + childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), + (int) view.getPreferredSpan(axis), + (int) view.getMaximumSpan(axis), + view.getAlignment(axis)); + } + return childReqs; + } + + /** + * Returns the span for the child view with the given index for the specified + * axis. + * + * @param axis the axis to examine, either <code>X_AXIS</code> or + * <code>Y_AXIS</code> + * @param childIndex the index of the child for for which to return the span + * + * @return the span for the child view with the given index for the specified + * axis + */ + protected int getSpan(int axis, int childIndex) + { + if (axis == X_AXIS) + return spansX[childIndex]; + else + return spansY[childIndex]; + } + + /** + * Returns the offset for the child view with the given index for the + * specified axis. + * + * @param axis the axis to examine, either <code>X_AXIS</code> or + * <code>Y_AXIS</code> + * @param childIndex the index of the child for for which to return the span + * + * @return the offset for the child view with the given index for the + * specified axis + */ + protected int getOffset(int axis, int childIndex) + { + if (axis == X_AXIS) + return offsetsX[childIndex]; + else + return offsetsY[childIndex]; + } + + /** + * Returns the alignment for this box view for the specified axis. The + * axis that is tiled (the major axis) will be requested to be aligned + * centered (0.5F). The minor axis alignment depends on the child view's + * total alignment. + * + * @param axis the axis which is examined + * + * @return the alignment for this box view for the specified axis + */ + public float getAlignment(int axis) + { + if (axis == myAxis) + return 0.5F; + else + return baselineRequirements(axis, null).alignment; + } + + /** + * Called by a child View when its preferred span has changed. + * + * @param width indicates that the preferred width of the child changed. + * @param height indicates that the preferred height of the child changed. + * @param child the child View. + */ + public void preferenceChanged (View child, boolean width, boolean height) + { + if (width) + xLayoutValid = false; + if (height) + yLayoutValid = false; + super.preferenceChanged(child, width, height); + } } diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java index f6feda21513..16112c8f4de 100644 --- a/libjava/classpath/javax/swing/text/ComponentView.java +++ b/libjava/classpath/javax/swing/text/ComponentView.java @@ -1,5 +1,5 @@ /* ComponentView.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,65 +41,125 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Shape; +import javax.swing.SwingConstants; + +/** + * A {@link View} implementation that is able to render arbitrary + * {@link Component}s. This uses the attribute + * {@link StyleConstants#ComponentAttribute} to determine the + * <code>Component</code> that should be rendered. This <code>Component</code> + * becomes a direct child of the <code>JTextComponent</code> that contains + * this <code>ComponentView</code>, so this view must not be shared between + * multiple <code>JTextComponent</code>s. + * + * @author original author unknown + * @author Roman Kennke (roman@kennke.org) + */ +// FIXME: This class is a complete stub and needs to be implemented properly. public class ComponentView extends View { - public ComponentView(Element elem) - { - super(elem); - } - - protected Component createComponent() - { - return null; - } - - public float getAlignment(int axis) - { - return 0; - } - - public final Component getComponent() - { - return null; - } - - public float getMaximumSpan(int axis) - { - return 0; - } - - public float getMinimumSpan(int axis) - { - return 0; - } - - public float getPreferredSpan(int axis) - { - return 0; - } - - public Shape modelToView(int pos, Shape a, Position.Bias b) - throws BadLocationException - { - return null; - } - - public void paint(Graphics g, Shape a) - { - } + /** + * Creates a new instance of <code>ComponentView</code> for the specified + * <code>Element</code>. + * + * @param elem the element that this <code>View</code> is rendering + */ + public ComponentView(Element elem) + { + super(elem); + } + + /** + * Creates the <code>Component</code> that this <code>View</code> is + * rendering. The <code>Component</code> is determined using + * the {@link StyleConstants#ComponentAttribute} of the associated + * <code>Element</code>. + * + * @return the component that is rendered + */ + protected Component createComponent() + { + return StyleConstants.getComponent(getElement().getAttributes()); + } + + /** + * Returns the alignment of this <code>View</code> along the specified axis. + * + * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS} + * + * @return the alignment of this <code>View</code> along the specified axis + */ + public float getAlignment(int axis) + { + return 0; + } + + /** + * Returns the <code>Component</code> that is rendered by this + * <code>ComponentView</code>. + * + * @return the <code>Component</code> that is rendered by this + * <code>ComponentView</code> + */ + public final Component getComponent() + { + return null; + } + + /** + * Returns the maximum span of this <code>View</code> along the specified + * axis. + * + * This will return {@link Component#getMaximumSize()} for the specified + * axis. + * + * @return the maximum span of this <code>View</code> along the specified + * axis + */ + public float getMaximumSpan(int axis) + { + return 0; + } + + public float getMinimumSpan(int axis) + { + // TODO: Implement this properly. + return 0; + } + + public float getPreferredSpan(int axis) + { + // TODO: Implement this properly. + return 0; + } + + public Shape modelToView(int pos, Shape a, Position.Bias b) + throws BadLocationException + { + // TODO: Implement this properly. + return null; + } - public void setParent(View p) - { - } + public void paint(Graphics g, Shape a) + { + // TODO: Implement this properly. + } + + public void setParent(View p) + { + // TODO: Implement this properly. + } - public void setSize(float width, float height) - { - } + public void setSize(float width, float height) + { + // TODO: Implement this properly. + } - public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) - { - return 0; - } + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) + { + // TODO: Implement this properly. + return 0; + } /** * Maps coordinates from the <code>View</code>'s space into a position @@ -118,4 +178,34 @@ public class ComponentView extends View // FIXME: Implement this properly. return 0; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index 6776c95727a..bc626a40696 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -62,7 +62,7 @@ public abstract class CompositeView /** * The allocation of this <code>View</code> minus its insets. This is * initialized in {@link #getInsideAllocation} and reused and modified in - * {@link childAllocation}. + * {@link #childAllocation(int, Rectangle)}. */ Rectangle insideAllocation; @@ -221,20 +221,17 @@ public abstract class CompositeView if (childIndex != -1) { View child = getView(childIndex); - Shape result = child.modelToView(pos, a, bias); + Rectangle r = a.getBounds(); + childAllocation(childIndex, r); + Shape result = child.modelToView(pos, r, bias); if (result == null) throw new AssertionError("" + child.getClass().getName() + ".modelToView() must not return null"); return result; } else - { - // FIXME: Handle the case when we have no child view for the given - // position. - throw new AssertionError("No child views found where child views are " - + "expected. pos = " + pos + ", bias = " - + bias); - } + throw new BadLocationException("No child view for the specified location", + pos); } /** @@ -314,6 +311,7 @@ public abstract class CompositeView */ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { int retVal = -1; switch (direction) @@ -433,10 +431,16 @@ public abstract class CompositeView */ protected int getViewIndexAtPosition(int pos) { - // We have one child view allocated for each child element in - // loadChildren(), so this should work. - Element el = getElement(); - int index = el.getElementIndex(pos); + int index = -1; + for (int i = 0; i < children.length; i++) + { + if (children[i].getStartOffset() <= pos + && children[i].getEndOffset() > pos) + { + index = i; + break; + } + } return index; } @@ -473,8 +477,8 @@ public abstract class CompositeView insideAllocation = inside; } } - inside.x = alloc.x - insets.left; - inside.y = alloc.y - insets.top; + inside.x = alloc.x + insets.left; + inside.y = alloc.y + insets.top; inside.width = alloc.width - insets.left - insets.right; inside.height = alloc.height - insets.top - insets.bottom; return inside; @@ -594,6 +598,7 @@ public abstract class CompositeView protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { // FIXME: Implement this correctly. return pos; @@ -627,6 +632,7 @@ public abstract class CompositeView protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { // FIXME: Implement this correctly. return pos; @@ -649,4 +655,34 @@ public abstract class CompositeView { return false; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 33c3ae3bf28..66e2f4723cf 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -40,15 +40,24 @@ package javax.swing.text; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.EventListener; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.event.EventListenerList; /** @@ -60,12 +69,164 @@ import javax.swing.event.EventListenerList; public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener { + + /** + * Controls the blinking of the caret. + * + * @author Roman Kennke (kennke@aicas.com) + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + private class BlinkTimerListener implements ActionListener + { + /** + * Forces the next event to be ignored. The next event should be ignored + * if we force the caret to appear. We do not know how long will it take + * to fire the comming event; this may be near immediately. Better to leave + * the caret visible one iteration longer. + */ + boolean ignoreNextEvent; + + /** + * Receives notification when the blink timer fires and updates the visible + * state of the caret. + * + * @param event the action event + */ + public void actionPerformed(ActionEvent event) + { + if (ignoreNextEvent) + ignoreNextEvent = false; + else + { + visible = !visible; + repaint(); + } + } + } + + /** + * Listens for changes in the text component's document and updates the + * caret accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class DocumentHandler implements DocumentListener + { + /** + * Receives notification that some text attributes have changed. No action + * is taken here. + * + * @param event the document event + */ + public void changedUpdate(DocumentEvent event) + { + // Nothing to do here. + } + + /** + * Receives notification that some text has been inserted from the text + * component. The caret is moved forward accordingly. + * + * @param event the document event + */ + public void insertUpdate(DocumentEvent event) + { + if (policy == ALWAYS_UPDATE || + (SwingUtilities.isEventDispatchThread() && + policy == UPDATE_WHEN_ON_EDT)) + { + int dot = getDot(); + setDot(dot + event.getLength()); + } + } + + /** + * Receives notification that some text has been removed into the text + * component. The caret is moved backwards accordingly. + * + * @param event the document event + */ + public void removeUpdate(DocumentEvent event) + { + if (policy == ALWAYS_UPDATE || + (SwingUtilities.isEventDispatchThread() && + policy == UPDATE_WHEN_ON_EDT)) + { + int dot = getDot(); + setDot(dot - event.getLength()); + } + else if (policy == NEVER_UPDATE) + { + int docLength = event.getDocument().getLength(); + if (getDot() > docLength) + setDot(docLength); + } + } + } + + /** + * Listens for property changes on the text document. This is used to add and + * remove our document listener, if the document of the text component has + * changed. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class PropertyChangeHandler implements PropertyChangeListener + { + + /** + * Receives notification when a property has changed on the text component. + * This adds/removes our document listener from the text component's + * document when the document changes. + * + * @param e the property change event + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals("document")) + { + Document oldDoc = (Document) e.getOldValue(); + oldDoc.removeDocumentListener(documentListener); + Document newDoc = (Document) e.getNewValue(); + newDoc.addDocumentListener(documentListener); + } + } + + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 4325555698756477346L; + /** - * The serial version UID for DefaultCaret. + * Indicates the Caret position should always be updated after Document + * changes even if the updates are not performed on the Event Dispatching + * thread. + * + * @since 1.5 */ - private static final long serialVersionUID = 228155774675466193L; + public static final int ALWAYS_UPDATE = 2; /** + * Indicates the Caret position should not be changed unless the Document + * length becomes less than the Caret position, in which case the Caret + * is moved to the end of the Document. + * + * @since 1.5 + */ + public static final int NEVER_UPDATE = 1; + + /** + * Indicates the Caret position should be updated only if Document changes + * are made on the Event Dispatcher thread. + * + * @since 1.5 + */ + public static final int UPDATE_WHEN_ON_EDT = 0; + + /** Keeps track of the current update policy **/ + int policy = UPDATE_WHEN_ON_EDT; + + /** * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. */ protected ChangeEvent changeEvent = new ChangeEvent(this); @@ -76,6 +237,16 @@ public class DefaultCaret extends Rectangle protected EventListenerList listenerList = new EventListenerList(); /** + * Our document listener. + */ + DocumentListener documentListener; + + /** + * Our property listener. + */ + PropertyChangeListener propertyChangeListener; + + /** * The text component in which this caret is installed. */ private JTextComponent textComponent; @@ -106,15 +277,65 @@ public class DefaultCaret extends Rectangle private Point magicCaretPosition = null; /** - * Indicates if this <code>Caret</code> is currently visible or not. + * Indicates if this <code>Caret</code> is currently visible or not. This is + * package private to avoid an accessor method. */ - private boolean visible = true; + boolean visible = false; /** * The current highlight entry. */ private Object highlightEntry; + private Timer blinkTimer; + + private BlinkTimerListener blinkListener; + + /** + * Creates a new <code>DefaultCaret</code> instance. + */ + public DefaultCaret() + { + // Nothing to do here. + } + + /** + * Sets the Caret update policy. + * + * @param policy the new policy. Valid values are: + * ALWAYS_UPDATE: always update the Caret position, even when Document + * updates don't occur on the Event Dispatcher thread. + * NEVER_UPDATE: don't update the Caret position unless the Document + * length becomes less than the Caret position (then update the + * Caret to the end of the Document). + * UPDATE_WHEN_ON_EDT: update the Caret position when the + * Document updates occur on the Event Dispatcher thread. This is the + * default. + * + * @since 1.5 + * @throws IllegalArgumentException if policy is not one of the above. + */ + public void setUpdatePolicy (int policy) + { + if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE + && policy != UPDATE_WHEN_ON_EDT) + throw new + IllegalArgumentException + ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT"); + this.policy = policy; + } + + /** + * Gets the caret update policy. + * + * @return the caret update policy. + * @since 1.5 + */ + public int getUpdatePolicy () + { + return policy; + } + /** * Moves the caret position when the mouse is dragged over the text * component, modifying the selection accordingly. @@ -123,7 +344,7 @@ public class DefaultCaret extends Rectangle */ public void mouseDragged(MouseEvent event) { - // FIXME: Implement this properly. + moveCaret(event); } /** @@ -153,7 +374,7 @@ public class DefaultCaret extends Rectangle */ public void mouseClicked(MouseEvent event) { - // FIXME: Implement this properly. + // TODO: Implement double- and triple-click behaviour here. } /** @@ -175,6 +396,7 @@ public class DefaultCaret extends Rectangle */ public void mouseExited(MouseEvent event) { + // Nothing to do here. } /** @@ -187,7 +409,7 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - // FIXME: Implement this properly. + positionCaret(event); } /** @@ -208,6 +430,7 @@ public class DefaultCaret extends Rectangle */ public void focusGained(FocusEvent event) { + setVisible(true); } /** @@ -217,6 +440,8 @@ public class DefaultCaret extends Rectangle */ public void focusLost(FocusEvent event) { + if (event.isTemporary() == false) + setVisible(false); } /** @@ -227,7 +452,8 @@ public class DefaultCaret extends Rectangle */ protected void moveCaret(MouseEvent event) { - // FIXME: Implement this properly. + int newDot = getComponent().viewToModel(event.getPoint()); + moveDot(newDot); } /** @@ -238,7 +464,8 @@ public class DefaultCaret extends Rectangle */ protected void positionCaret(MouseEvent event) { - // FIXME: Implement this properly. + int newDot = getComponent().viewToModel(event.getPoint()); + setDot(newDot); } /** @@ -253,7 +480,16 @@ public class DefaultCaret extends Rectangle textComponent.removeFocusListener(this); textComponent.removeMouseListener(this); textComponent.removeMouseMotionListener(this); + textComponent.getDocument().removeDocumentListener(documentListener); + documentListener = null; + textComponent.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; textComponent = null; + + // Deinstall blink timer if present. + if (blinkTimer != null) + blinkTimer.stop(); + blinkTimer = null; } /** @@ -269,6 +505,11 @@ public class DefaultCaret extends Rectangle textComponent.addFocusListener(this); textComponent.addMouseListener(this); textComponent.addMouseMotionListener(this); + propertyChangeListener = new PropertyChangeHandler(); + textComponent.addPropertyChangeListener(propertyChangeListener); + documentListener = new DocumentHandler(); + textComponent.getDocument().addDocumentListener(documentListener); + repaint(); } @@ -376,10 +617,7 @@ public class DefaultCaret extends Rectangle */ protected final void repaint() { - // FIXME: Is this good? This possibly causes alot of the component - // hierarchy to be repainted on every caret blink. - if (textComponent != null) - textComponent.repaint(); + getComponent().repaint(x, y, width, height); } /** @@ -390,7 +628,8 @@ public class DefaultCaret extends Rectangle */ public void paint(Graphics g) { - if (textComponent == null) + JTextComponent comp = getComponent(); + if (comp == null) return; int dot = getDot(); @@ -398,25 +637,33 @@ public class DefaultCaret extends Rectangle try { - rect = textComponent.modelToView(dot); + rect = textComponent.modelToView(dot); } catch (BadLocationException e) { - // This should never happen as dot should be always valid. - return; + assert false : "Unexpected bad caret location: " + dot; + return; } if (rect == null) return; - - // First we need to delete the old caret. - // FIXME: Implement deleting of old caret. - + + // Check if paint has possibly been called directly, without a previous + // call to damage(). In this case we need to do some cleanup first. + if ((x != rect.x) || (y != rect.y)) + { + repaint(); // Erase previous location of caret. + x = rect.x; + y = rect.y; + width = 1; + height = rect.height; + } + // Now draw the caret on the new position if visible. if (visible) { - g.setColor(textComponent.getCaretColor()); - g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); + g.setColor(textComponent.getCaretColor()); + g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); } } @@ -507,6 +754,8 @@ public class DefaultCaret extends Rectangle */ public void setBlinkRate(int rate) { + if (blinkTimer != null) + blinkTimer.setDelay(rate); blinkRate = rate; } @@ -534,7 +783,8 @@ public class DefaultCaret extends Rectangle { this.dot = dot; handleHighlight(); - repaint(); + adjustVisibility(this); + appear(); } /** @@ -551,8 +801,45 @@ public class DefaultCaret extends Rectangle this.dot = dot; this.mark = dot; handleHighlight(); - repaint(); + adjustVisibility(this); + appear(); } + + /** + * Show the caret (may be hidden due blinking) and adjust the timer not to + * hide it (possibly immediately). + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + void appear() + { + // All machinery is only required if the carret is blinking. + if (blinkListener != null) + { + blinkListener.ignoreNextEvent = true; + + // If the caret is visible, erase the current position by repainting + // over. + if (visible) + repaint(); + + // Draw the caret in the new position. + visible = true; + + Rectangle area = null; + try + { + area = getComponent().modelToView(getDot()); + } + catch (BadLocationException ex) + { + assert false : "Unexpected bad caret location: " + getDot(); + } + if (area != null) + damage(area); + } + repaint(); + } /** * Returns <code>true</code> if this <code>Caret</code> is currently visible, @@ -574,8 +861,33 @@ public class DefaultCaret extends Rectangle */ public void setVisible(boolean v) { - visible = v; - repaint(); + if (v != visible) + { + visible = v; + if (visible) + if (textComponent.isEnabled() && textComponent.isEditable()) + { + if (blinkTimer == null) + initBlinkTimer(); + blinkTimer.start(); + } + else + { + if (blinkTimer != null) + blinkTimer.stop(); + } + Rectangle area = null; + try + { + area = getComponent().modelToView(getDot()); + } + catch (BadLocationException ex) + { + assert false: "Unexpected bad caret location: " + getDot(); + } + if (area != null) + damage(area); + } } /** @@ -589,4 +901,47 @@ public class DefaultCaret extends Rectangle { return DefaultHighlighter.DefaultPainter; } + + /** + * Updates the carets rectangle properties to the specified rectangle and + * repaints the caret. + * + * @param r the rectangle to set as the caret rectangle + */ + protected void damage(Rectangle r) + { + if (r == null) + return; + x = r.x; + y = r.y; + width = 1; + // height is normally set in paint and we leave it untouched. However, we + // must set a valid value here, since otherwise the painting mechanism + // sets a zero clip and never calls paint. + if (height <= 0) + height = getComponent().getHeight(); + repaint(); + } + + /** + * Adjusts the text component so that the caret is visible. This default + * implementation simply calls + * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component. + * Subclasses may wish to change this. + */ + protected void adjustVisibility(Rectangle rect) + { + getComponent().scrollRectToVisible(rect); + } + + /** + * Initializes the blink timer. + */ + private void initBlinkTimer() + { + // Setup the blink timer. + blinkListener = new BlinkTimerListener(); + blinkTimer = new Timer(getBlinkRate(), blinkListener); + blinkTimer.setRepeats(true); + } } diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java index a14f3ff4fe0..3b3fc1f7238 100644 --- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java +++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java @@ -66,8 +66,7 @@ public class DefaultEditorKit extends EditorKit * * @see Toolkit#beep() */ - public static class BeepAction - extends TextAction + public static class BeepAction extends TextAction { /** * Creates a new <code>BeepAction</code>. @@ -95,8 +94,7 @@ public class DefaultEditorKit extends EditorKit * @see CutAction * @see PasteAction */ - public static class CopyAction - extends TextAction + public static class CopyAction extends TextAction { /** @@ -128,8 +126,7 @@ public class DefaultEditorKit extends EditorKit * @see CopyAction * @see PasteAction */ - public static class CutAction - extends TextAction + public static class CutAction extends TextAction { /** @@ -159,8 +156,7 @@ public class DefaultEditorKit extends EditorKit * @see CopyAction * @see CutAction */ - public static class PasteAction - extends TextAction + public static class PasteAction extends TextAction { /** @@ -226,9 +222,6 @@ public class DefaultEditorKit extends EditorKit { t.getDocument().insertString(t.getCaret().getDot(), event.getActionCommand(), null); - t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1, - t.getDocument().getEndPosition() - .getOffset())); } catch (BadLocationException be) { @@ -243,8 +236,7 @@ public class DefaultEditorKit extends EditorKit * of the text component. This is typically triggered by hitting * ENTER on the keyboard. */ - public static class InsertBreakAction - extends TextAction + public static class InsertBreakAction extends TextAction { /** @@ -273,8 +265,7 @@ public class DefaultEditorKit extends EditorKit */ // FIXME: Figure out what this Action is supposed to do. Obviously text // that is entered by the user is inserted through DefaultKeyTypedAction. - public static class InsertContentAction - extends TextAction + public static class InsertContentAction extends TextAction { /** @@ -292,14 +283,15 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { + // FIXME: Figure out what this Action is supposed to do. Obviously text + // that is entered by the user is inserted through DefaultKeyTypedAction. } } /** * Inserts a TAB character into the text editor. */ - public static class InsertTabAction - extends TextAction + public static class InsertTabAction extends TextAction { /** @@ -699,6 +691,7 @@ public class DefaultEditorKit extends EditorKit */ public DefaultEditorKit() { + // Nothing to do here. } /** @@ -954,15 +947,23 @@ public class DefaultEditorKit extends EditorKit * @param offset the beginning offset from where to write * @param len the length of the fragment to write * - * @throws BadLocationException if <code>offset</code> or - * <code>offset + len</code>is an invalid location inside - * <code>document</code> + * @throws BadLocationException if <code>offset</code> is an + * invalid location inside <code>document</code>. * @throws IOException if something goes wrong while writing to * <code>out</code> */ public void write(Writer out, Document document, int offset, int len) - throws BadLocationException, IOException + throws BadLocationException, IOException { - // TODO: Implement this properly. + // Throw a BLE if offset is invalid + if (offset < 0 || offset > document.getLength()) + throw new BadLocationException("Tried to write to invalid location", + offset); + + // If they gave an overly large len, just adjust it + if (offset + len > document.getLength()) + len = document.getLength() - offset; + + out.write(document.getText(offset, len)); } } diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java index c97d90703c7..f9e0f10e654 100644 --- a/libjava/classpath/javax/swing/text/DefaultFormatter.java +++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java @@ -57,8 +57,7 @@ import javax.swing.JFormattedTextField; * * @author Roman Kennke (roman@kennke.org) */ -public class DefaultFormatter - extends JFormattedTextField.AbstractFormatter +public class DefaultFormatter extends JFormattedTextField.AbstractFormatter implements Cloneable, Serializable { @@ -156,9 +155,6 @@ public class DefaultFormatter * Checks if the value in the input field is valid. If the * property allowsInvalid is set to <code>false</code>, then * the string in the input field is not allowed to be entered. - * - * @param doc the document of the input field - * @param value the current (old) value of the input field */ private void checkValidInput() { @@ -179,15 +175,18 @@ public class DefaultFormatter catch (ParseException pe) { // if that happens, something serious must be wrong - throw new AssertionError("values must be parseable"); + AssertionError ae; + ae = new AssertionError("values must be parseable"); + ae.initCause(pe); + throw ae; } } } } } - /** The serialVersoinUID. */ - private static final long serialVersionUID = -7369196326612908900L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -355018354457785329L; /** * Indicates if the value should be committed after every diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java index c8d874caa51..40ea4f80aab 100644 --- a/libjava/classpath/javax/swing/text/DefaultHighlighter.java +++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java @@ -168,6 +168,7 @@ public class DefaultHighlighter extends LayeredHighlighter public DefaultHighlighter() { + // Nothing to do here. } public boolean getDrawsLayeredHighlights() @@ -238,6 +239,7 @@ public class DefaultHighlighter extends LayeredHighlighter Shape viewBounds, JTextComponent editor, View view) { + // TODO: Implement this properly. } public void paint(Graphics g) diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java index 3545e52c453..eb56bb0f8e5 100644 --- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java +++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java @@ -1,5 +1,5 @@ /* DefaultStyledDocument.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,8 +41,14 @@ package javax.swing.text; import java.awt.Color; import java.awt.Font; import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.UndoableEdit; /** * The default implementation of {@link StyledDocument}. @@ -60,12 +66,359 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDocument { /** + * An {@link UndoableEdit} that can undo attribute changes to an element. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public static class AttributeUndoableEdit + extends AbstractUndoableEdit + { + /** + * A copy of the old attributes. + */ + protected AttributeSet copy; + + /** + * The new attributes. + */ + protected AttributeSet newAttributes; + + /** + * If the new attributes replaced the old attributes or if they only were + * added to them. + */ + protected boolean isReplacing; + + /** + * The element that has changed. + */ + protected Element element; + + /** + * Creates a new <code>AttributeUndoableEdit</code>. + * + * @param el the element that changes attributes + * @param newAtts the new attributes + * @param replacing if the new attributes replace the old or only append to + * them + */ + public AttributeUndoableEdit(Element el, AttributeSet newAtts, + boolean replacing) + { + element = el; + newAttributes = newAtts; + isReplacing = replacing; + copy = el.getAttributes().copyAttributes(); + } + + /** + * Undos the attribute change. The <code>copy</code> field is set as + * attributes on <code>element</code>. + */ + public void undo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + mutable.removeAttributes(atts); + mutable.addAttributes(copy); + } + } + + /** + * Redos an attribute change. This adds <code>newAttributes</code> to the + * <code>element</code>'s attribute set, possibly clearing all attributes + * if <code>isReplacing</code> is true. + */ + public void redo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + if (isReplacing) + mutable.removeAttributes(atts); + mutable.addAttributes(newAttributes); + } + } + } + + /** + * Carries specification information for new {@link Element}s that should + * be created in {@link ElementBuffer}. This allows the parsing process + * to be decoupled from the <code>Element</code> creation process. + */ + public static class ElementSpec + { + /** + * This indicates a start tag. This is a possible value for + * {@link #getType}. + */ + public static final short StartTagType = 1; + + /** + * This indicates an end tag. This is a possible value for + * {@link #getType}. + */ + public static final short EndTagType = 2; + + /** + * This indicates a content element. This is a possible value for + * {@link #getType}. + */ + public static final short ContentType = 3; + + /** + * This indicates that the data associated with this spec should be joined + * with what precedes it. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinPreviousDirection = 4; + + /** + * This indicates that the data associated with this spec should be joined + * with what follows it. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinNextDirection = 5; + + /** + * This indicates that the data associated with this spec should be used + * to create a new element. This is a possible value for + * {@link #getDirection}. + */ + public static final short OriginateDirection = 6; + + /** + * This indicates that the data associated with this spec should be joined + * to the fractured element. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinFractureDirection = 7; + + /** + * The type of the tag. + */ + short type; + + /** + * The direction of the tag. + */ + short direction; + + /** + * The offset of the content. + */ + int offset; + + /** + * The length of the content. + */ + int length; + + /** + * The actual content. + */ + char[] content; + + /** + * The attributes for the tag. + */ + AttributeSet attributes; + + /** + * Creates a new <code>ElementSpec</code> with no content, length or + * offset. This is most useful for start and end tags. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + */ + public ElementSpec(AttributeSet a, short type) + { + this(a, type, 0); + } + + /** + * Creates a new <code>ElementSpec</code> that specifies the length but + * not the offset of an element. Such <code>ElementSpec</code>s are + * processed sequentially from a known starting point. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + * @param len the length of the element + */ + public ElementSpec(AttributeSet a, short type, int len) + { + this(a, type, null, 0, len); + } + + /** + * Creates a new <code>ElementSpec</code> with document content. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + * @param txt the actual content + * @param offs the offset into the <code>txt</code> array + * @param len the length of the element + */ + public ElementSpec(AttributeSet a, short type, char[] txt, int offs, + int len) + { + attributes = a; + this.type = type; + offset = offs; + length = len; + content = txt; + direction = OriginateDirection; + } + + /** + * Sets the type of the element. + * + * @param type the type of the element to be set + */ + public void setType(short type) + { + this.type = type; + } + + /** + * Returns the type of the element. + * + * @return the type of the element + */ + public short getType() + { + return type; + } + + /** + * Sets the direction of the element. + * + * @param dir the direction of the element to be set + */ + public void setDirection(short dir) + { + direction = dir; + } + + /** + * Returns the direction of the element. + * + * @return the direction of the element + */ + public short getDirection() + { + return direction; + } + + /** + * Returns the attributes of the element. + * + * @return the attributes of the element + */ + public AttributeSet getAttributes() + { + return attributes; + } + + /** + * Returns the actual content of the element. + * + * @return the actual content of the element + */ + public char[] getArray() + { + return content; + } + + /** + * Returns the offset of the content. + * + * @return the offset of the content + */ + public int getOffset() + { + return offset; + } + + /** + * Returns the length of the content. + * + * @return the length of the content + */ + public int getLength() + { + return length; + } + + /** + * Returns a String representation of this <code>ElementSpec</code> + * describing the type, direction and length of this + * <code>ElementSpec</code>. + * + * @return a String representation of this <code>ElementSpec</code> + */ + public String toString() + { + StringBuilder b = new StringBuilder(); + b.append('<'); + switch (type) + { + case StartTagType: + b.append("StartTag"); + break; + case EndTagType: + b.append("EndTag"); + break; + case ContentType: + b.append("Content"); + break; + default: + b.append("??"); + break; + } + + b.append(':'); + + switch (direction) + { + case JoinPreviousDirection: + b.append("JoinPrevious"); + break; + case JoinNextDirection: + b.append("JoinNext"); + break; + case OriginateDirection: + b.append("Originate"); + break; + case JoinFractureDirection: + b.append("Fracture"); + break; + default: + b.append("??"); + break; + } + + b.append(':'); + b.append(length); + + return b.toString(); + } + } + + /** * Performs all <em>structural</code> changes to the <code>Element</code> * hierarchy. */ - public class ElementBuffer - implements Serializable + public class ElementBuffer implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 1688745877691146623L; + /** The root element of the hierarchy. */ private Element root; @@ -76,6 +429,19 @@ public class DefaultStyledDocument extends AbstractDocument private int length; /** + * Holds fractured elements during insertion of end and start tags. + * Inserting an end tag may lead to fracturing of the current paragraph + * element. The elements that have been cut off may be added to the + * next paragraph that is created in the next start tag. + */ + Element[] fracture; + + /** + * The ElementChange that describes the latest changes. + */ + DefaultDocumentEvent documentEvent; + + /** * Creates a new <code>ElementBuffer</code> for the specified * <code>root</code> element. * @@ -114,6 +480,7 @@ public class DefaultStyledDocument extends AbstractDocument { this.offset = offset; this.length = length; + documentEvent = ev; changeUpdate(); } @@ -142,38 +509,352 @@ public class DefaultStyledDocument extends AbstractDocument void split(Element el, int offset) { if (el instanceof AbstractElement) - { - AbstractElement ael = (AbstractElement) el; - int startOffset = ael.getStartOffset(); - int endOffset = ael.getEndOffset(); - int len = endOffset - startOffset; - if (startOffset != offset && endOffset != offset) - { - Element paragraph = ael.getParentElement(); - if (paragraph instanceof BranchElement) - { - BranchElement par = (BranchElement) paragraph; - Element child1 = createLeafElement(par, ael, startOffset, - offset); - Element child2 = createLeafElement(par, ael, offset, - endOffset); - int index = par.getElementIndex(startOffset); - par.replace(index, 1, new Element[]{ child1, child2 }); - } + { + AbstractElement ael = (AbstractElement) el; + int startOffset = ael.getStartOffset(); + int endOffset = ael.getEndOffset(); + int len = endOffset - startOffset; + if (startOffset != offset && endOffset != offset) + { + Element paragraph = ael.getParentElement(); + if (paragraph instanceof BranchElement) + { + BranchElement par = (BranchElement) paragraph; + Element child1 = createLeafElement(par, ael, startOffset, + offset); + Element child2 = createLeafElement(par, ael, offset, + endOffset); + int index = par.getElementIndex(startOffset); + Element[] add = new Element[]{ child1, child2 }; + par.replace(index, 1, add); + documentEvent.addEdit(new ElementEdit(par, index, + new Element[]{ el }, + add)); + } else throw new AssertionError("paragraph elements are expected to " + "be instances of " + "javax.swing.text.AbstractDocument.BranchElement"); - } - } + } + } else - throw new AssertionError("content elements are expected to be " - + "instances of " + throw new AssertionError("content elements are expected to be " + + "instances of " + "javax.swing.text.AbstractDocument.AbstractElement"); } + + /** + * Inserts new <code>Element</code> in the document at the specified + * position. + * + * Most of the work is done by {@link #insertUpdate}, after some fields + * have been prepared for it. + * + * @param offset the location in the document at which the content is + * inserted + * @param length the length of the inserted content + * @param data the element specifications for the content to be inserted + * @param ev the document event that is updated to reflect the structural + * changes + */ + public void insert(int offset, int length, ElementSpec[] data, + DefaultDocumentEvent ev) + { + this.offset = offset; + this.length = length; + documentEvent = ev; + insertUpdate(data); + } + + /** + * Performs the actual structural change for {@link #insert}. This + * creates a bunch of {@link Element}s as specified by <code>data</code> + * and inserts it into the document as specified in the arguments to + * {@link #insert}. + * + * @param data the element specifications for the elements to be inserte + */ + protected void insertUpdate(ElementSpec[] data) + { + for (int i = 0; i < data.length; i++) + { + switch (data[i].getType()) + { + case ElementSpec.StartTagType: + insertStartTag(data[i]); + break; + case ElementSpec.EndTagType: + insertEndTag(data[i]); + break; + default: + insertContentTag(data[i]); + break; + } + } + } + + /** + * Insert a new paragraph after the paragraph at the current position. + * + * @param tag the element spec that describes the element to be inserted + */ + void insertStartTag(ElementSpec tag) + { + BranchElement root = (BranchElement) getDefaultRootElement(); + int index = root.getElementIndex(offset); + if (index == -1) + index = 0; + + BranchElement newParagraph = + (BranchElement) createBranchElement(root, tag.getAttributes()); + newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); + + // Add new paragraph into document structure. + Element[] added = new Element[]{newParagraph}; + root.replace(index + 1, 0, added); + ElementEdit edit = new ElementEdit(root, index + 1, new Element[0], + added); + documentEvent.addEdit(edit); + + // Maybe add fractured elements. + if (tag.getDirection() == ElementSpec.JoinFractureDirection) + { + Element[] newFracture = new Element[fracture.length]; + for (int i = 0; i < fracture.length; i++) + { + Element oldLeaf = fracture[i]; + Element newLeaf = createLeafElement(newParagraph, + oldLeaf.getAttributes(), + oldLeaf.getStartOffset(), + oldLeaf.getEndOffset()); + newFracture[i] = newLeaf; + } + newParagraph.replace(0, 0, newFracture); + edit = new ElementEdit(newParagraph, 0, new Element[0], + fracture); + documentEvent.addEdit(edit); + fracture = new Element[0]; + } + } + + /** + * Inserts an end tag into the document structure. This cuts of the + * current paragraph element, possibly fracturing it's child elements. + * The fractured elements are saved so that they can be joined later + * with a new paragraph element. + */ + void insertEndTag(ElementSpec tag) + { + BranchElement root = (BranchElement) getDefaultRootElement(); + int parIndex = root.getElementIndex(offset); + BranchElement paragraph = (BranchElement) root.getElement(parIndex); + + int index = paragraph.getElementIndex(offset); + LeafElement content = (LeafElement) paragraph.getElement(index); + // We might have to split the element at offset. + split(content, offset); + index = paragraph.getElementIndex(offset); + + int count = paragraph.getElementCount(); + // Store fractured elements. + fracture = new Element[count - index]; + for (int i = index; i < count; ++i) + fracture[i - index] = paragraph.getElement(i); + + // Delete fractured elements. + paragraph.replace(index, count - index, new Element[0]); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(paragraph, index, fracture, + new Element[0]); + documentEvent.addEdit(edit); + } + + /** + * Inserts a content element into the document structure. + * + * @param tag the element spec + */ + void insertContentTag(ElementSpec tag) + { + int len = tag.getLength(); + int dir = tag.getDirection(); + if (dir == ElementSpec.JoinPreviousDirection) + { + Element prev = getCharacterElement(offset); + BranchElement prevParent = (BranchElement) prev.getParentElement(); + Element join = createLeafElement(prevParent, tag.getAttributes(), + prev.getStartOffset(), + Math.max(prev.getEndOffset(), + offset + len)); + int ind = prevParent.getElementIndex(offset); + if (ind == -1) + ind = 0; + Element[] add = new Element[]{join}; + prevParent.replace(ind, 1, add); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(prevParent, ind, + new Element[]{prev}, add); + documentEvent.addEdit(edit); + } + else if (dir == ElementSpec.JoinNextDirection) + { + Element next = getCharacterElement(offset + len); + BranchElement nextParent = (BranchElement) next.getParentElement(); + Element join = createLeafElement(nextParent, tag.getAttributes(), + offset, + next.getEndOffset()); + int ind = nextParent.getElementIndex(offset + len); + if (ind == -1) + ind = 0; + Element[] add = new Element[]{join}; + nextParent.replace(ind, 1, add); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(nextParent, ind, + new Element[]{next}, add); + documentEvent.addEdit(edit); + } + else + { + BranchElement par = (BranchElement) getParagraphElement(offset); + + int ind = par.getElementIndex(offset); + + // Make room for the element. + // Cut previous element. + Element prev = par.getElement(ind); + if (prev != null && prev.getStartOffset() < offset) + { + Element cutPrev = createLeafElement(par, prev.getAttributes(), + prev.getStartOffset(), + offset); + Element[] remove = new Element[]{prev}; + Element[] add = new Element[]{cutPrev}; + if (prev.getEndOffset() > offset + len) + { + Element rem = createLeafElement(par, prev.getAttributes(), + offset + len, + prev.getEndOffset()); + add = new Element[]{cutPrev, rem}; + } + + par.replace(ind, 1, add); + documentEvent.addEdit(new ElementEdit(par, ind, remove, add)); + ind++; + } + // ind now points to the next element. + + // Cut next element if necessary. + Element next = par.getElement(ind); + if (next != null && next.getStartOffset() < offset + len) + { + Element cutNext = createLeafElement(par, next.getAttributes(), + offset + len, + next.getEndOffset()); + Element[] remove = new Element[]{next}; + Element[] add = new Element[]{cutNext}; + par.replace(ind, 1, add); + documentEvent.addEdit(new ElementEdit(par, ind, remove, + add)); + } + + // Insert new element. + Element newEl = createLeafElement(par, tag.getAttributes(), + offset, offset + len); + Element[] added = new Element[]{newEl}; + par.replace(ind, 0, added); + // Add this action to the document event. + ElementEdit edit = new ElementEdit(par, ind, new Element[0], + added); + documentEvent.addEdit(edit); + } + offset += len; + } + + /** + * Creates a copy of the element <code>clonee</code> that has the parent + * <code>parent</code>. + * @param parent the parent of the newly created Element + * @param clonee the Element to clone + * @return the cloned Element + */ + public Element clone (Element parent, Element clonee) + { + // If the Element we want to clone is a leaf, then simply copy it + if (clonee.isLeaf()) + return createLeafElement(parent, clonee.getAttributes(), + clonee.getStartOffset(), clonee.getEndOffset()); + + // Otherwise create a new BranchElement with the desired parent and + // the clonee's attributes + BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes()); + + // And clone all the of clonee's children + Element[] children = new Element[clonee.getElementCount()]; + for (int i = 0; i < children.length; i++) + children[i] = clone(result, clonee.getElement(i)); + + // Make the cloned children the children of the BranchElement + result.replace(0, 0, children); + return result; + } } /** + * An element type for sections. This is a simple BranchElement with + * a unique name. + */ + protected class SectionElement extends BranchElement + { + /** + * Creates a new SectionElement. + */ + public SectionElement() + { + super(null, null); + } + + /** + * Returns the name of the element. This method always returns + * "section". + * + * @return the name of the element + */ + public String getName() + { + return "section"; + } + } + + /** + * Receives notification when any of the document's style changes and calls + * {@link DefaultStyledDocument#styleChanged(Style)}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class StyleChangeListener + implements ChangeListener + { + + /** + * Receives notification when any of the document's style changes and calls + * {@link DefaultStyledDocument#styleChanged(Style)}. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + Style style = (Style) event.getSource(); + styleChanged(style); + } + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 940485415728614849L; + + /** * The default size to use for new content buffers. */ public static final int BUFFER_SIZE_DEFAULT = 4096; @@ -185,6 +866,11 @@ public class DefaultStyledDocument extends AbstractDocument protected DefaultStyledDocument.ElementBuffer buffer; /** + * Listens for changes on this document's styles and notifies styleChanged(). + */ + private StyleChangeListener styleChangeListener; + + /** * Creates a new <code>DefaultStyledDocument</code>. */ public DefaultStyledDocument() @@ -237,7 +923,14 @@ public class DefaultStyledDocument extends AbstractDocument public Style addStyle(String nm, Style parent) { StyleContext context = (StyleContext) getAttributeContext(); - return context.addStyle(nm, parent); + Style newStyle = context.addStyle(nm, parent); + + // Register change listener. + if (styleChangeListener == null) + styleChangeListener = new StyleChangeListener(); + newStyle.addChangeListener(styleChangeListener); + + return newStyle; } /** @@ -250,9 +943,11 @@ public class DefaultStyledDocument extends AbstractDocument Element[] tmp; // FIXME: Create a SecionElement here instead of a BranchElement. // Use createBranchElement() and createLeafElement instead. - BranchElement section = new BranchElement(null, null); - - BranchElement paragraph = new BranchElement(section, null); + SectionElement section = new SectionElement(); + + BranchElement paragraph = + (BranchElement) createBranchElement(section, null); + paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); tmp = new Element[1]; tmp[0] = paragraph; section.replace(0, 0, tmp); @@ -261,7 +956,7 @@ public class DefaultStyledDocument extends AbstractDocument tmp = new Element[1]; tmp[0] = leaf; paragraph.replace(0, 0, tmp); - + return section; } @@ -279,10 +974,10 @@ public class DefaultStyledDocument extends AbstractDocument { Element element = getDefaultRootElement(); - while (! element.isLeaf()) + while (!element.isLeaf()) { - int index = element.getElementIndex(position); - element = element.getElement(index); + int index = element.getElementIndex(position); + element = element.getElement(index); } return element; @@ -353,6 +1048,10 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the paragraph element for the specified position. + * If the position is outside the bounds of the document's root element, + * then the closest element is returned. That is the last paragraph if + * <code>position >= endIndex</code> or the first paragraph if + * <code>position < startIndex</code>. * * @param position the position for which to query the paragraph element * @@ -360,8 +1059,18 @@ public class DefaultStyledDocument extends AbstractDocument */ public Element getParagraphElement(int position) { - Element element = getCharacterElement(position); - return element.getParentElement(); + BranchElement root = (BranchElement) getDefaultRootElement(); + int start = root.getStartOffset(); + int end = root.getEndOffset(); + if (position >= end) + position = end - 1; + else if (position < start) + position = start; + + Element par = root.positionToElement(position); + + assert par != null : "The paragraph element must not be null"; + return par; } /** @@ -416,35 +1125,37 @@ public class DefaultStyledDocument extends AbstractDocument int paragraphCount = root.getElementCount(); for (int pindex = 0; pindex < paragraphCount; pindex++) { - Element paragraph = root.getElement(pindex); - // Skip paragraphs that lie outside the interval. - if ((paragraph.getStartOffset() > offset + length) - || (paragraph.getEndOffset() < offset)) - continue; - - // Visit content elements within this paragraph - int contentCount = paragraph.getElementCount(); - for (int cindex = 0; cindex < contentCount; cindex++) - { - Element content = paragraph.getElement(cindex); - // Skip content that lies outside the interval. - if ((content.getStartOffset() > offset + length) - || (content.getEndOffset() < offset)) - continue; - - if (content instanceof AbstractElement) - { - AbstractElement el = (AbstractElement) content; - if (replace) - el.removeAttributes(el); - el.addAttributes(attributes); - } - else - throw new AssertionError("content elements are expected to be" - + "instances of " + Element paragraph = root.getElement(pindex); + // Skip paragraphs that lie outside the interval. + if ((paragraph.getStartOffset() > offset + length) + || (paragraph.getEndOffset() < offset)) + continue; + + // Visit content elements within this paragraph + int contentCount = paragraph.getElementCount(); + for (int cindex = 0; cindex < contentCount; cindex++) + { + Element content = paragraph.getElement(cindex); + // Skip content that lies outside the interval. + if ((content.getStartOffset() > offset + length) + || (content.getEndOffset() < offset)) + continue; + + if (content instanceof AbstractElement) + { + AbstractElement el = (AbstractElement) content; + if (replace) + el.removeAttributes(el); + el.addAttributes(attributes); + } + else + throw new AssertionError("content elements are expected to be" + + "instances of " + "javax.swing.text.AbstractDocument.AbstractElement"); - } + } } + + fireChangedUpdate(ev); } /** @@ -476,10 +1187,199 @@ public class DefaultStyledDocument extends AbstractDocument * selection are overridden, otherwise they are merged */ public void setParagraphAttributes(int offset, int length, - AttributeSet attributes, - boolean replace) + AttributeSet attributes, + boolean replace) + { + int index = offset; + while (index < offset + length) + { + AbstractElement par = (AbstractElement) getParagraphElement(index); + AttributeContext ctx = getAttributeContext(); + if (replace) + par.removeAttributes(par); + par.addAttributes(attributes); + index = par.getElementCount(); + } + } + + /** + * Called in response to content insert actions. This is used to + * update the element structure. + * + * @param ev the <code>DocumentEvent</code> describing the change + * @param attr the attributes for the change + */ + protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr) + { + super.insertUpdate(ev, attr); + int offset = ev.getOffset(); + int length = ev.getLength(); + int endOffset = offset + length; + Segment txt = new Segment(); + try + { + getText(offset, length, txt); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + int len = 0; + Vector specs = new Vector(); + + Element prev = getCharacterElement(offset); + Element next = getCharacterElement(endOffset); + + for (int i = offset; i < endOffset; ++i) + { + len++; + if (txt.array[i] == '\n') + { + ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, + len); + + // If we are at the last index, then check if we could probably be + // joined with the next element. + if (i == endOffset - 1) + { + if (next.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinNextDirection); + } + // If we are at the first new element, then check if it could be + // joined with the previous element. + else if (specs.size() == 0) + { + if (prev.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinPreviousDirection); + } + + specs.add(spec); + + // Add ElementSpecs for the newline. + ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType); + specs.add(endTag); + ElementSpec startTag = new ElementSpec(null, + ElementSpec.StartTagType); + startTag.setDirection(ElementSpec.JoinFractureDirection); + specs.add(startTag); + + len = 0; + offset += len; + } + } + + // Create last element if last character hasn't been a newline. + if (len > 0) + { + ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len); + // If we are at the first new element, then check if it could be + // joined with the previous element. + if (specs.size() == 0) + { + if (prev.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinPreviousDirection); + } + // Check if we could probably be joined with the next element. + else if (next.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinNextDirection); + + specs.add(spec); + } + + ElementSpec[] elSpecs = + (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); + + buffer.insert(offset, length, elSpecs, ev); + } + + /** + * Returns an enumeration of all style names. + * + * @return an enumeration of all style names + */ + public Enumeration getStyleNames() + { + StyleContext context = (StyleContext) getAttributeContext(); + return context.getStyleNames(); + } + + /** + * Called when any of this document's styles changes. + * + * @param style the style that changed + */ + protected void styleChanged(Style style) { - // FIXME: Implement me. - throw new Error("not implemented"); + // Nothing to do here. This is intended to be overridden by subclasses. + } + + /** + * Inserts a bulk of structured content at once. + * + * @param offset the offset at which the content should be inserted + * @param data the actual content spec to be inserted + */ + protected void insert(int offset, ElementSpec[] data) + throws BadLocationException + { + writeLock(); + // First we insert the content. + int index = offset; + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + if (spec.getArray() != null && spec.getLength() > 0) + { + String insertString = new String(spec.getArray(), spec.getOffset(), + spec.getLength()); + content.insertString(index, insertString); + } + index += spec.getLength(); + } + // Update the view structure. + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset, + DocumentEvent.EventType.INSERT); + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + AttributeSet atts = spec.getAttributes(); + if (atts != null) + insertUpdate(ev, atts); + } + + // Finally we must update the document structure and fire the insert update + // event. + buffer.insert(offset, index - offset, data, ev); + fireInsertUpdate(ev); + writeUnlock(); + } + + /** + * Initializes the <code>DefaultStyledDocument</code> with the specified + * data. + * + * @param data the specification of the content with which the document is + * initialized + */ + protected void create(ElementSpec[] data) + { + try + { + // Clear content. + content.remove(0, content.length()); + // Clear buffer and root element. + buffer = new ElementBuffer(createDefaultRoot()); + // Insert the data. + insert(0, data); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("Unexpected bad location"); + err.initCause(ex); + throw err; + } } } diff --git a/libjava/classpath/javax/swing/text/DefaultTextUI.java b/libjava/classpath/javax/swing/text/DefaultTextUI.java new file mode 100644 index 00000000000..e7ff01845e6 --- /dev/null +++ b/libjava/classpath/javax/swing/text/DefaultTextUI.java @@ -0,0 +1,61 @@ +/* DefaultTextUI.java -- Deprecated base UI for text components + Copyright (C) 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 javax.swing.text; + +import javax.swing.plaf.basic.BasicTextUI; + +/** + * This class is deprecated and should not be used anymore. The base UI for + * all text components is now {@link BasicTextUI}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class DefaultTextUI extends BasicTextUI +{ + /** + * This class is deprecated and should not be used anymore. The base UI for + * all text components is now {@link BasicTextUI}. + * + * @deprecated use {@link BasicTextUI} instead + */ + public DefaultTextUI() + { + // Nothing to do here. + } +} diff --git a/libjava/classpath/javax/swing/text/EditorKit.java b/libjava/classpath/javax/swing/text/EditorKit.java index bd51a866f68..8719aee595f 100644 --- a/libjava/classpath/javax/swing/text/EditorKit.java +++ b/libjava/classpath/javax/swing/text/EditorKit.java @@ -48,24 +48,24 @@ import java.io.Writer; import javax.swing.Action; import javax.swing.JEditorPane; -public abstract class EditorKit - implements Cloneable, Serializable +public abstract class EditorKit implements Cloneable, Serializable { private static final long serialVersionUID = -5044124649345887822L; public EditorKit() { + // Nothing to do here. } public Object clone() { try { - return super.clone(); + return super.clone(); } catch (CloneNotSupportedException e) { - return null; + return null; } } @@ -74,10 +74,12 @@ public abstract class EditorKit */ public void deinstall(JEditorPane c) { + // This default implementation does nothing. } public void install(JEditorPane c) { + // This default implementation does nothing. } public abstract Caret createCaret(); diff --git a/libjava/classpath/javax/swing/text/FieldView.java b/libjava/classpath/javax/swing/text/FieldView.java index e2e04d7c495..2496418444e 100644 --- a/libjava/classpath/javax/swing/text/FieldView.java +++ b/libjava/classpath/javax/swing/text/FieldView.java @@ -118,22 +118,24 @@ public class FieldView extends PlainView FontMetrics fm = getFontMetrics(); if (axis == Y_AXIS) - return fm.getHeight(); + return super.getPreferredSpan(axis); String text; Element elem = getElement(); try { - text = elem.getDocument().getText(elem.getStartOffset(), - elem.getEndOffset()); + text = elem.getDocument().getText(elem.getStartOffset(), + elem.getEndOffset()); } catch (BadLocationException e) { - // This should never happen. - text = ""; + // Should never happen + AssertionError ae = new AssertionError(); + ae.initCause(e); + throw ae; } - + return fm.stringWidth(text); } @@ -159,18 +161,21 @@ public class FieldView extends PlainView { Shape newAlloc = adjustAllocation(shape); super.insertUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { Shape newAlloc = adjustAllocation(shape); super.removeUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { Shape newAlloc = adjustAllocation(shape); super.removeUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index a6ef89efb78..fd6785b6f18 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -42,8 +42,10 @@ import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +import java.util.Iterator; import java.util.Vector; +import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; /** @@ -71,6 +73,7 @@ public abstract class FlowView extends BoxView */ public FlowStrategy() { + // Nothing to do here. } /** @@ -137,7 +140,7 @@ public abstract class FlowView extends BoxView * Performs the layout for the whole view. By default this rebuilds * all the physical views from the logical views of the managed FlowView. * - * This is called by {@link FlowLayout#layout} to update the layout of + * This is called by {@link FlowView#layout} to update the layout of * the view. * * @param fv the flow view for which we perform the layout @@ -183,11 +186,17 @@ public abstract class FlowView extends BoxView { View child = createView(fv, offset, spanLeft, rowIndex); if (child == null) - break; + { + offset = -1; + break; + } int span = (int) child.getPreferredSpan(flowAxis); if (span > spanLeft) - break; + { + offset = -1; + break; + } row.append(child); spanLeft -= span; @@ -204,7 +213,7 @@ public abstract class FlowView extends BoxView * not fit in the available span and also cannot be broken down). * * @param fv the flow view - * @param startOffset the start offset for the view to be created + * @param offset the start offset for the view to be created * @param spanLeft the available span * @param rowIndex the index of the row * @@ -218,13 +227,15 @@ public abstract class FlowView extends BoxView View logicalView = getLogicalView(fv); int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward); + if (viewIndex == -1) + return null; + View child = logicalView.getView(viewIndex); int flowAxis = fv.getFlowAxis(); int span = (int) child.getPreferredSpan(flowAxis); if (span <= spanLeft) return child; - else if (child.getBreakWeight(flowAxis, offset, spanLeft) > BadBreakWeight) // FIXME: What to do with the pos parameter here? @@ -326,7 +337,19 @@ public abstract class FlowView extends BoxView */ public int getViewIndex(int pos, Position.Bias b) { - return getElement().getElementIndex(pos); + int index = -1; + int i = 0; + for (Iterator it = children.iterator(); it.hasNext(); i++) + { + View child = (View) it.next(); + if (child.getStartOffset() >= pos + && child.getEndOffset() < pos) + { + index = i; + break; + } + } + return index; } /** @@ -373,6 +396,37 @@ public abstract class FlowView extends BoxView throw new AssertionError("This method must not be called in " + "LogicalView."); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + assert false : "getNextVisualPositionFrom() must not be called in " + + "LogicalView"; + return 0; + } } /** @@ -478,7 +532,7 @@ public abstract class FlowView extends BoxView * The real children are created at layout time and each represent one * row. * - * This method is called by {@link #setParent} in order to initialize + * This method is called by {@link View#setParent} in order to initialize * the view. * * @param vf the view factory to use for creating the child views @@ -502,7 +556,7 @@ public abstract class FlowView extends BoxView /** * Performs the layout of this view. If the span along the flow axis changed, - * this first calls {@link FlowStrategy.layout} in order to rebuild the + * this first calls {@link FlowStrategy#layout} in order to rebuild the * rows of this view. Then the superclass's behaviour is called to arrange * the rows within the box. * diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 1dd46c4b0f4..4c65de0f5f4 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -39,10 +39,15 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; +import java.util.Iterator; import java.util.ListIterator; +import java.util.Vector; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoableEdit; /** @@ -59,7 +64,6 @@ import javax.swing.undo.UndoableEdit; public class GapContent implements AbstractDocument.Content, Serializable { - /** * A {@link Position} implementation for <code>GapContent</code>. */ @@ -114,6 +118,11 @@ public class GapContent */ public int getOffset() { + // Check precondition. + assert mark <= gapStart || mark >= gapEnd : "mark: " + mark + + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + if (mark <= gapStart) return mark; else @@ -121,13 +130,91 @@ public class GapContent } } - private static final long serialVersionUID = 8374645204155842629L; + class UndoInsertString extends AbstractUndoableEdit + { + public int where, length; + String text; + public UndoInsertString(int start, int len) + { + where = start; + length = len; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + text = getString(where, length); + remove(where, length); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + class UndoRemove extends AbstractUndoableEdit + { + public int where; + String text; + public UndoRemove(int start, String removedText) + { + where = start; + text = removedText; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + remove(where, text.length()); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6226052713477823730L; /** * This is the default buffer size and the amount of bytes that a buffer is * extended if it is full. */ - static final int DEFAULT_BUFSIZE = 64; + static final int DEFAULT_BUFSIZE = 10; /** * The text buffer. @@ -148,7 +235,7 @@ public class GapContent * The positions generated by this GapContent. They are kept in an ordered * fashion, so they can be looked up easily. */ - LinkedList positions; + ArrayList positions; /** * Creates a new GapContent object. @@ -166,10 +253,10 @@ public class GapContent public GapContent(int size) { buffer = (char[]) allocateArray(size); - gapStart = 0; - gapEnd = size - 1; - buffer[size - 1] = '\n'; - positions = new LinkedList(); + gapStart = 1; + gapEnd = size; + buffer[0] = '\n'; + positions = new ArrayList(); } /** @@ -211,8 +298,7 @@ public class GapContent * @param where the position where the string is inserted * @param str the string that is to be inserted * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -228,9 +314,9 @@ public class GapContent throw new BadLocationException("the where argument cannot be greater" + " than the content length", where); - replace(where, 0, str.toCharArray(), str.length()); + replace(where, 0, str.toCharArray(), strLen); - return null; + return new UndoInsertString(where, strLen); } /** @@ -239,8 +325,7 @@ public class GapContent * @param where the position where the content is to be removed * @param nitems number of characters to be removed * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -257,9 +342,10 @@ public class GapContent throw new BadLocationException("where + nitems cannot be greater" + " than the content length", where + nitems); + String removedText = getString(where, nitems); replace(where, nitems, null, 0); - return null; + return new UndoRemove(where, removedText); } /** @@ -372,7 +458,6 @@ public class GapContent if (index < 0) index = -(index + 1); positions.add(index, pos); - return pos; } @@ -386,7 +471,14 @@ public class GapContent */ protected void shiftEnd(int newSize) { - int delta = (gapEnd - gapStart) - newSize; + assert newSize > (gapEnd - gapStart) : "The new gap size must be greater " + + "than the old gap size"; + + int delta = newSize - gapEnd + gapStart; + // Update the marks after the gapEnd. + adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta); + + // Copy the data around. char[] newBuf = (char[]) allocateArray(length() + newSize); System.arraycopy(buffer, 0, newBuf, 0, gapStart); System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length @@ -394,18 +486,6 @@ public class GapContent gapEnd = gapStart + newSize; buffer = newBuf; - // Update the marks after the gapEnd. - int index = Collections.binarySearch(positions, new GapContentPosition( - gapEnd)); - if (index < 0) - { - index = -(index + 1); - } - for (ListIterator i = positions.listIterator(index); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += delta; - } } /** @@ -415,32 +495,15 @@ public class GapContent */ protected void shiftGap(int newGapStart) { - int newGapEnd = newGapStart + (gapEnd - gapStart); - - // Update the positions between newGapEnd and (old) gapEnd. The marks - // must be shifted by (gapEnd - newGapEnd). - int index1 = Collections.binarySearch(positions, - new GapContentPosition(gapEnd)); - int index2 = Collections.binarySearch(positions, - new GapContentPosition(newGapEnd)); - if (index1 > 0 && index2 > 0) - { - int i1 = Math.min(index1, index2); - int i2 = Math.max(index1, index2); - for (ListIterator i = positions.listIterator(i1); i.hasNext();) - { - if (i.nextIndex() > i2) - break; - - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += gapEnd - newGapEnd; - } - } - if (newGapStart == gapStart) return; - else if (newGapStart < gapStart) + + int newGapEnd = newGapStart + gapEnd - gapStart; + if (newGapStart < gapStart) { + // Update the positions between newGapStart and (old) gapStart. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart); System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -448,11 +511,54 @@ public class GapContent } else { + // Update the positions between newGapEnd and (old) gapEnd. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart)); System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; gapEnd = newGapEnd; } + if (gapStart == 0) + resetMarksAtZero(); + } + + /** + * Shifts the gap start downwards. This does not affect the content of the + * buffer. This only updates the gap start and all the marks that are between + * the old gap start and the new gap start. They all are squeezed to the start + * of the gap, because their location has been removed. + * + * @param newGapStart the new gap start + */ + protected void shiftGapStartDown(int newGapStart) + { + if (newGapStart == gapStart) + return; + + assert newGapStart < gapStart : "The new gap start must be less than the " + + "old gap start."; + setPositionsInRange(newGapStart, gapStart - newGapStart, gapStart); + gapStart = newGapStart; + } + + /** + * Shifts the gap end upwards. This does not affect the content of the + * buffer. This only updates the gap end and all the marks that are between + * the old gap end and the new end start. They all are squeezed to the end + * of the gap, because their location has been removed. + * + * @param newGapEnd the new gap start + */ + protected void shiftGapEndUp(int newGapEnd) + { + if (newGapEnd == gapEnd) + return; + + assert newGapEnd > gapEnd : "The new gap end must be greater than the " + + "old gap end."; + setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1); + gapEnd = newGapEnd; } /** @@ -476,13 +582,15 @@ public class GapContent protected void replace(int position, int rmSize, Object addItems, int addSize) { + if (gapStart != position) + shiftGap(position); // Remove content - shiftGap(position); - gapEnd += rmSize; + if (rmSize > 0) + shiftGapEndUp(gapEnd + rmSize); // If gap is too small, enlarge the gap. - if ((gapEnd - gapStart) < addSize) - shiftEnd(addSize); + if ((gapEnd - gapStart) <= addSize) + shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE); // Add new items to the buffer. if (addItems != null) @@ -491,4 +599,164 @@ public class GapContent gapStart += addSize; } } + + /** + * Returns the start index of the gap within the buffer array. + * + * @return the start index of the gap within the buffer array + */ + protected final int getGapStart() + { + return gapStart; + } + + /** + * Returns the end index of the gap within the buffer array. + * + * @return the end index of the gap within the buffer array + */ + protected final int getGapEnd() + { + return gapEnd; + } + + /** + * Returns all <code>Position</code>s that are in the range specified by + * <code>offset</code> and </code>length</code> within the buffer array. + * + * @param v the vector to use; if <code>null</code>, a new Vector is allocated + * @param offset the start offset of the range to search + * @param length the length of the range to search + * + * @return the positions within the specified range + */ + protected Vector getPositionsInRange(Vector v, int offset, int length) + { + Vector res = v; + if (res == null) + res = new Vector(); + else + res.clear(); + + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + if (p.mark >= offset && p.mark <= endOffset) + res.add(p); + } + return res; + } + + /** + * Sets the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array to <code>value</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param value the new value for each mark + */ + void setPositionsInRange(int offset, int length, int value) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark = value; + } + } + + /** + * Adjusts the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array by <code>increment</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param incr the increment + */ + void adjustPositionsInRange(int offset, int length, int incr) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark += incr; + } + } + + /** + * Resets all <code>Position</code> that have an offset of <code>0</code>, + * to also have an array index of <code>0</code>. This might be necessary + * after a call to <code>shiftGap(0)</code>, since then the marks at offset + * <code>0</code> get shifted to <code>gapEnd</code>. + */ + protected void resetMarksAtZero() + { + if (gapStart != 0) + return; + + setPositionsInRange(gapEnd, 0, 0); + } + + /** + * Outputs debugging info to System.err. It prints out the buffer array, + * the gapStart is marked by a < sign, the gapEnd is marked by a > + * sign and each position is marked by a # sign. + */ + private void dump() + { + System.err.println("GapContent debug information"); + System.err.println("buffer length: " + buffer.length); + System.err.println("gap start: " + gapStart); + System.err.println("gap end: " + gapEnd); + for (int i = 0; i < buffer.length; i++) + { + if (i == gapStart) + System.err.print('<'); + if (i == gapEnd) + System.err.print('>'); + + if (!Character.isISOControl(buffer[i])) + System.err.print(buffer[i]); + else + System.err.print('.'); + } + System.err.println(); + } + + private void dumpPositions() + { + for (Iterator i = positions.iterator(); i.hasNext();) + { + GapContentPosition pos = (GapContentPosition) i.next(); + System.err.println("position at: " + pos.mark); + } + } } diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java index f9e60972d84..eb1fadd4f2d 100644 --- a/libjava/classpath/javax/swing/text/GlyphView.java +++ b/libjava/classpath/javax/swing/text/GlyphView.java @@ -44,6 +44,12 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; +import java.text.BreakIterator; + +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; /** * Renders a run of styled text. This {@link View} subclass paints the @@ -52,9 +58,7 @@ import java.awt.Shape; * * @author Roman Kennke (roman@kennke.org) */ -public class GlyphView - extends View - implements TabableView, Cloneable +public class GlyphView extends View implements TabableView, Cloneable { /** @@ -68,15 +72,47 @@ public class GlyphView */ public GlyphPainter() { + // Nothing to do here. } /** + * Returns the ascent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the ascent of the font that is used by this glyph painter + */ + public abstract float getAscent(GlyphView v); + + /** + * Returns the descent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the descent of the font that is used by this glyph painter + */ + public abstract float getDescent(GlyphView v); + + /** * Returns the full height of the rendered text. * * @return the full height of the rendered text */ public abstract float getHeight(GlyphView view); - + + /** + * Determines the model offset, so that the text between <code>p0</code> + * and this offset fits within the span starting at <code>x</code> with + * the length of <code>len</code>. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public abstract int getBoundedPosition(GlyphView v, int p0, float x, + float len); + /** * Paints the glyphs. * @@ -97,8 +133,8 @@ public class GlyphView * @param view the glyph view * @param pos the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -114,6 +150,20 @@ public class GlyphView throws BadLocationException; /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public abstract int viewToModel(GlyphView v, float x, float y, Shape a, + Position.Bias[] biasRet); + + /** * Determine the span of the glyphs from location <code>p0</code> to * location <code>p1</code>. If <code>te</code> is not <code>null</code>, * then TABs are expanded using this <code>TabExpander</code>. @@ -122,7 +172,7 @@ public class GlyphView * * @param view the glyph view * @param p0 the starting location in the document model - * @param p0 the end location in the document model + * @param p1 the end location in the document model * @param te the tab expander to use * @param x the location at which the view is located * @@ -132,6 +182,69 @@ public class GlyphView public abstract float getSpan(GlyphView view, int p0, int p1, TabExpander te, float x); + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param v the glyph view + * @param pos the current model location + * @param b the bias for <code>p</code> + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if <code>pos</code> is an invalid model + * location + * @throws IllegalArgumentException if <code>d</code> is invalid + */ + public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, + Shape a, int direction, + Position.Bias[] biasRet) + throws BadLocationException + + { + int result = pos; + switch (direction) + { + case SwingConstants.EAST: + result = pos + 1; + break; + case SwingConstants.WEST: + result = pos - 1; + break; + case SwingConstants.NORTH: + case SwingConstants.SOUTH: + default: + // This should be handled in enclosing view, since the glyph view + // does not layout vertically. + break; + } + return result; + } + + /** + * Returns a painter that can be used to render the specified glyph view. + * If this glyph painter is stateful, then it should return a new instance. + * However, if this painter is stateless it should return itself. The + * default behaviour is to return itself. + * + * @param v the glyph view for which to create a painter + * @param p0 the start offset of the rendered area + * @param p1 the end offset of the rendered area + * + * @return a painter that can be used to render the specified glyph view + */ + public GlyphPainter getPainter(GlyphView v, int p0, int p1) + { + return this; + } } /** @@ -147,7 +260,7 @@ public class GlyphView public float getHeight(GlyphView view) { Font font = view.getFont(); - FontMetrics metrics = view.getContainer().getFontMetrics(font); + FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font); float height = metrics.getHeight(); return height; } @@ -173,11 +286,40 @@ public class GlyphView if (parent instanceof TabExpander) tabEx = (TabExpander) parent; - // FIXME: Set character attributes like font-family, font-size, colors. - Color foreground = view.getForeground(); - g.setColor(foreground); - Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, - txt.offset); + // Fill the background of the text run. + Color background = view.getBackground(); + g.setColor(background); + int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), + bounds.x, tabEx, txt.offset); + g.fillRect(bounds.x, bounds.y, width, height); + + // Draw the actual text. + g.setColor(view.getForeground()); + g.setFont(view.getFont()); + if (view.isSuperscript()) + // TODO: Adjust font for superscripting. + Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx, + txt.offset); + else if (view.isSubscript()) + // TODO: Adjust font for subscripting. + Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx, + txt.offset); + else + Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, + txt.offset); + + if (view.isStikeThrough()) + { + int strikeHeight = (int) (getAscent(view) / 2); + g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, + bounds.y + strikeHeight); + } + if (view.isUnderline()) + { + int lineHeight = (int) getAscent(view); + g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, + bounds.y + lineHeight); + } } /** @@ -188,8 +330,8 @@ public class GlyphView * @param view the glyph view * @param pos the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -225,7 +367,7 @@ public class GlyphView * * @param view the glyph view * @param p0 the starting location in the document model - * @param p0 the end location in the document model + * @param p1 the end location in the document model * @param te the tab expander to use * @param x the location at which the view is located * @@ -237,11 +379,90 @@ public class GlyphView { Element el = view.getElement(); Font font = view.getFont(); - FontMetrics fm = view.getContainer().getFontMetrics(font); + FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font); Segment txt = view.getText(p0, p1); int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); return span; } + + /** + * Returns the ascent of the text run that is rendered by this + * <code>GlyphPainter</code>. + * + * @param v the glyph view + * + * @return the ascent of the text run that is rendered by this + * <code>GlyphPainter</code> + * + * @see FontMetrics#getAscent() + */ + public float getAscent(GlyphView v) + { + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + return fm.getAscent(); + } + + /** + * Returns the descent of the text run that is rendered by this + * <code>GlyphPainter</code>. + * + * @param v the glyph view + * + * @return the descent of the text run that is rendered by this + * <code>GlyphPainter</code> + * + * @see FontMetrics#getDescent() + */ + public float getDescent(GlyphView v) + { + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + return fm.getDescent(); + } + + /** + * Determines the model offset, so that the text between <code>p0</code> + * and this offset fits within the span starting at <code>x</code> with + * the length of <code>len</code>. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public int getBoundedPosition(GlyphView v, int p0, float x, float len) + { + TabExpander te = v.getTabExpander(); + Segment txt = v.getText(p0, v.getEndOffset()); + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, + (int) (x + len), te, p0, false); + return pos; + } + + /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public int viewToModel(GlyphView v, float x, float y, Shape a, + Bias[] biasRet) + { + Rectangle b = a.getBounds(); + assert b.contains(x, y) : "The coordinates are expected to be within the " + + "view's bounds: x=" + x + ", y=" + y + + "a=" + a; + int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); + return pos; + } } /** @@ -250,6 +471,16 @@ public class GlyphView GlyphPainter glyphPainter; /** + * The start offset within the document for this view. + */ + int startOffset; + + /** + * The end offset within the document for this view. + */ + int endOffset; + + /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. * * @param element the element that is rendered by this GlyphView @@ -257,6 +488,8 @@ public class GlyphView public GlyphView(Element element) { super(element); + startOffset = element.getStartOffset(); + endOffset = element.getEndOffset(); } /** @@ -319,16 +552,21 @@ public class GlyphView */ public float getPreferredSpan(int axis) { - Element el = getElement(); + float span = 0; checkPainter(); GlyphPainter painter = getGlyphPainter(); - TabExpander tabEx = null; - View parent = getParent(); - if (parent instanceof TabExpander) - tabEx = (TabExpander) parent; - // FIXME: Figure out how to determine the x parameter. - float span = painter.getSpan(this, el.getStartOffset(), el.getEndOffset(), - tabEx, 0.F); + if (axis == X_AXIS) + { + Element el = getElement(); + TabExpander tabEx = null; + View parent = getParent(); + if (parent instanceof TabExpander) + tabEx = (TabExpander) parent; + span = painter.getSpan(this, getStartOffset(), getEndOffset(), + tabEx, 0.F); + } + else + span = painter.getHeight(this); return span; } @@ -372,8 +610,9 @@ public class GlyphView */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // FIXME: not implemented - return 0; + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.viewToModel(this, x, y, a, b); } /** @@ -383,12 +622,11 @@ public class GlyphView */ public TabExpander getTabExpander() { - // TODO: Figure out if this is correct. TabExpander te = null; View parent = getParent(); - if (parent instanceof ParagraphView) - te = (ParagraphView) parent; + if (parent instanceof TabExpander) + te = (TabExpander) parent; return te; } @@ -428,23 +666,26 @@ public class GlyphView } catch (BadLocationException ex) { - throw new AssertionError("BadLocationException must not be thrown " - + "here"); + AssertionError ae; + ae = new AssertionError("BadLocationException must not be thrown " + + "here"); + ae.initCause(ex); + throw ae; } FontMetrics fm = null; // Fetch font metrics somewhere. return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0); } /** - * Returns the starting offset in the document model of the portion + * Returns the start offset in the document model of the portion * of text that this view is responsible for. * - * @return the starting offset in the document model of the portion + * @return the start offset in the document model of the portion * of text that this view is responsible for */ - public int getBeginIndex() + public int getStartOffset() { - return getElement().getStartOffset(); + return startOffset; } /** @@ -454,9 +695,9 @@ public class GlyphView * @return the end offset in the document model of the portion * of text that this view is responsible for */ - public int getEndIndex() + public int getEndOffset() { - return getElement().getEndOffset(); + return endOffset; } /** @@ -476,8 +717,11 @@ public class GlyphView } catch (BadLocationException ex) { - throw new AssertionError("BadLocationException should not be " - + "thrown here. p0 = " + p0 + ", p1 = " + p1); + AssertionError ae; + ae = new AssertionError("BadLocationException should not be " + + "thrown here. p0 = " + p0 + ", p1 = " + p1); + ae.initCause(ex); + throw ae; } return txt; @@ -518,4 +762,332 @@ public class GlyphView AttributeSet atts = el.getAttributes(); return StyleConstants.getForeground(atts); } + + /** + * Returns the background color which should be used to paint the text. + * This is fetched from the associated element's text attributes using + * {@link StyleConstants#getBackground}. + * + * @return the background color which should be used to paint the text + */ + public Color getBackground() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.getBackground(atts); + } + + /** + * Determines whether the text should be rendered strike-through or not. This + * is determined using the method + * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered strike-through or not + */ + public boolean isStikeThrough() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isStrikeThrough(atts); + } + + /** + * Determines whether the text should be rendered as subscript or not. This + * is determined using the method + * {@link StyleConstants#isSubscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as subscript or not + */ + public boolean isSubscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSubscript(atts); + } + + /** + * Determines whether the text should be rendered as superscript or not. This + * is determined using the method + * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as superscript or not + */ + public boolean isSuperscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSuperscript(atts); + } + + /** + * Determines whether the text should be rendered as underlined or not. This + * is determined using the method + * {@link StyleConstants#isUnderline(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as underlined or not + */ + public boolean isUnderline() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isUnderline(atts); + } + + /** + * Creates and returns a shallow clone of this GlyphView. This is used by + * the {@link #createFragment} and {@link #breakView} methods. + * + * @return a shallow clone of this GlyphView + */ + protected final Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + AssertionError err = new AssertionError("CloneNotSupportedException " + + "must not be thrown here"); + err.initCause(ex); + throw err; + } + } + + /** + * Tries to break the view near the specified view span <code>len</code>. + * The glyph view can only be broken in the X direction. For Y direction it + * returns itself. + * + * @param axis the axis for breaking, may be {@link View#X_AXIS} or + * {@link View#Y_AXIS} + * @param p0 the model location where the fragment should start + * @param pos the view position along the axis where the fragment starts + * @param len the desired length of the fragment view + * + * @return the fragment view, or <code>this</code> if breaking was not + * possible + */ + public View breakView(int axis, int p0, float pos, float len) + { + if (axis == Y_AXIS) + return this; + + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + int breakLocation = painter.getBoundedPosition(this, p0, pos, len); + // Try to find a suitable line break. + BreakIterator lineBreaker = BreakIterator.getLineInstance(); + Segment txt = new Segment(); + try + { + getDocument().getText(getStartOffset(), getEndOffset(), txt); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("BadLocationException must not " + + "be thrown here."); + err.initCause(ex); + throw err; + } + lineBreaker.setText(txt); + int goodBreakLocation = lineBreaker.previous(); + if (goodBreakLocation != BreakIterator.DONE) + breakLocation = goodBreakLocation; + + View brokenView = createFragment(p0, breakLocation); + return brokenView; + } + + /** + * Determines how well the specified view location is suitable for inserting + * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then + * this method forwards to the superclass, if <code>axis</code> is + * <code>View.X_AXIS</code> then this method returns + * {@link View#ExcellentBreakWeight} if there is a suitable break location + * (usually whitespace) within the specified view span, or + * {@link View#GoodBreakWeight} if not. + * + * @param axis the axis along which the break weight is requested + * @param pos the starting view location + * @param len the length of the span at which the view should be broken + * + * @return the break weight + */ + public int getBreakWeight(int axis, float pos, float len) + { + int weight; + if (axis == Y_AXIS) + weight = super.getBreakWeight(axis, pos, len); + else + { + // Determine the model locations at pos and pos + len. + int spanX = (int) getPreferredSpan(X_AXIS); + int spanY = (int) getPreferredSpan(Y_AXIS); + Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY); + Position.Bias[] biasRet = new Position.Bias[1]; + int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); + int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); + Segment txt = getText(offset1, offset2); + BreakIterator lineBreaker = BreakIterator.getLineInstance(); + lineBreaker.setText(txt); + int breakLoc = lineBreaker.previous(); + if (breakLoc == offset1) + weight = View.BadBreakWeight; + else if(breakLoc == BreakIterator.DONE) + weight = View.GoodBreakWeight; + else + weight = View.ExcellentBreakWeight; + } + return weight; + } + + /** + * Receives notification that some text attributes have changed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * both width and height. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, true); + } + + /** + * Receives notification that some text has been inserted within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * width. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, false); + } + + /** + * Receives notification that some text has been removed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * width. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, false); + } + + /** + * Creates a fragment view of this view that starts at <code>p0</code> and + * ends at <code>p1</code>. + * + * @param p0 the start location for the fragment view + * @param p1 the end location for the fragment view + * + * @return the fragment view + */ + public View createFragment(int p0, int p1) + { + GlyphView fragment = (GlyphView) clone(); + fragment.startOffset = p0; + fragment.endOffset = p1; + return fragment; + } + + /** + * Returns the alignment of this view along the specified axis. For the Y + * axis this is <code>(height - descent) / height</code> for the used font, + * so that it is aligned along the baseline. + * For the X axis the superclass is called. + */ + public float getAlignment(int axis) + { + float align; + if (axis == Y_AXIS) + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + float height = painter.getHeight(this); + float descent = painter.getDescent(this); + align = (height - descent) / height; + } + else + align = super.getAlignment(axis); + + return align; + } + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param pos the current model location + * @param bias the bias for <code>p</code> + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if <code>pos</code> is an invalid model + * location + * @throws IllegalArgumentException if <code>d</code> is invalid + */ + public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, + int direction, Position.Bias[] biasRet) + throws BadLocationException + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, + biasRet); + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index c7e22b6c3eb..6dd0f7ad34a 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -41,6 +41,8 @@ package javax.swing.text; import java.awt.Graphics; import java.awt.Shape; +import javax.swing.SwingConstants; + // TODO: Implement this class. public class IconView extends View @@ -125,4 +127,34 @@ public class IconView // FIXME: not implemented return 0; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java index cedaf59feeb..86300a70d8e 100644 --- a/libjava/classpath/javax/swing/text/InternationalFormatter.java +++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java @@ -57,9 +57,8 @@ import javax.swing.JFormattedTextField; public class InternationalFormatter extends DefaultFormatter { - - /** The serialVersoinUID. */ - private static final long serialVersionUID = 6941977820906408656L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 2436068675711756856L; /** The format that handles value to string conversion. */ Format format; diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index b3fad79124c..83966bbdf47 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/javax/swing/text/JTextComponent.java @@ -50,9 +50,9 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.InputMethodListener; import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.io.IOException; import java.io.Reader; import java.io.Writer; @@ -72,7 +72,6 @@ import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.Scrollable; import javax.swing.SwingConstants; -import javax.swing.Timer; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.CaretEvent; @@ -89,6 +88,7 @@ public abstract class JTextComponent extends JComponent /** * AccessibleJTextComponent */ + // FIXME: This inner class is a complete stub and needs to be implemented. public class AccessibleJTextComponent extends AccessibleJComponent implements AccessibleText, CaretListener, DocumentListener { @@ -99,6 +99,7 @@ public abstract class JTextComponent extends JComponent */ public AccessibleJTextComponent() { + // Nothing to do here. } /** @@ -301,50 +302,6 @@ public abstract class JTextComponent extends JComponent } /** - * The timer that lets the caret blink. - */ - private class CaretBlinkTimer - extends Timer - implements ActionListener - { - /** - * Creates a new CaretBlinkTimer object with a default delay of 1 second. - */ - public CaretBlinkTimer() - { - super(1000, null); - addActionListener(this); - } - - /** - * Lets the caret blink. - */ - public void actionPerformed(ActionEvent ev) - { - Caret c = caret; - if (c != null) - c.setVisible(!c.isVisible()); - } - - /** - * Updates the blink delay according to the current caret. - */ - public void update() - { - stop(); - Caret c = caret; - if (c != null) - { - setDelay(c.getBlinkRate()); - if (editable) - start(); - else - c.setVisible(false); - } - } - } - - /** * According to <a * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this * report</a>, a pair of private classes wraps a {@link @@ -604,8 +561,7 @@ public abstract class JTextComponent extends JComponent } } - class DefaultTransferHandler - extends TransferHandler + class DefaultTransferHandler extends TransferHandler { public boolean canImport(JComponent component, DataFlavor[] flavors) { @@ -631,23 +587,23 @@ public abstract class JTextComponent extends JComponent int end = textComponent.getSelectionEnd(); if (start == end) - return; + return; try - { - // Copy text to clipboard. - String data = textComponent.getDocument().getText(start, end); - StringSelection selection = new StringSelection(data); - clipboard.setContents(selection, null); - - // Delete selected text on cut action. - if (action == MOVE) - doc.remove(start, end - start); - } + { + // Copy text to clipboard. + String data = textComponent.getDocument().getText(start, end); + StringSelection selection = new StringSelection(data); + clipboard.setContents(selection, null); + + // Delete selected text on cut action. + if (action == MOVE) + doc.remove(start, end - start); + } catch (BadLocationException e) - { - // Ignore this and do nothing. - } + { + // Ignore this and do nothing. + } } public int getSourceActions() @@ -661,30 +617,30 @@ public abstract class JTextComponent extends JComponent DataFlavor[] flavors = transferable.getTransferDataFlavors(); if (flavors == null) - return false; + return false; for (int i = 0; i < flavors.length; ++i) - if (flavors[i].equals(DataFlavor.stringFlavor)) - flavor = flavors[i]; + if (flavors[i].equals(DataFlavor.stringFlavor)) + flavor = flavors[i]; if (flavor == null) - return false; + return false; try - { - JTextComponent textComponent = (JTextComponent) component; - String data = (String) transferable.getTransferData(flavor); - textComponent.replaceSelection(data); - return true; - } + { + JTextComponent textComponent = (JTextComponent) component; + String data = (String) transferable.getTransferData(flavor); + textComponent.replaceSelection(data); + return true; + } catch (IOException e) - { - // Ignored. - } + { + // Ignored. + } catch (UnsupportedFlavorException e) - { - // Ignored. - } + { + // Ignored. + } return false; } @@ -701,8 +657,6 @@ public abstract class JTextComponent extends JComponent private char focusAccelerator = '\0'; private NavigationFilter navigationFilter; - private CaretBlinkTimer caretBlinkTimer; - /** * Get a Keymap from the global keymap table, by name. * @@ -960,8 +914,6 @@ public abstract class JTextComponent extends JComponent creatingKeymap = true; } - caretBlinkTimer = new CaretBlinkTimer(); - setFocusable(true); setEditable(true); enableEvents(AWTEvent.KEY_EVENT_MASK); @@ -1021,12 +973,17 @@ public abstract class JTextComponent extends JComponent { try { - doc.remove(0, doc.getLength()); - doc.insertString(0, text, null); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).replace(0, doc.getLength(), text, null); + else + { + doc.remove(0, doc.getLength()); + doc.insertString(0, text, null); + } } catch (BadLocationException e) { - // This can never happen. + // This can never happen. } } @@ -1044,12 +1001,12 @@ public abstract class JTextComponent extends JComponent try { - return doc.getText(0, doc.getLength()); + return doc.getText(0, doc.getLength()); } catch (BadLocationException e) { - // This should never happen. - return ""; + // This should never happen. + return ""; } } @@ -1080,12 +1037,12 @@ public abstract class JTextComponent extends JComponent { try { - return doc.getText(getSelectionStart(), getSelectionEnd()); + return doc.getText(getSelectionStart(), getSelectionEnd()); } catch (BadLocationException e) { - // This should never happen. - return null; + // This should never happen. + return null; } } @@ -1105,7 +1062,8 @@ public abstract class JTextComponent extends JComponent */ protected String paramString() { - return "JTextComponent"; + // TODO: Do something useful here. + return super.paramString(); } /** @@ -1194,14 +1152,6 @@ public abstract class JTextComponent extends JComponent if (editable == newValue) return; - if (newValue == true) - caretBlinkTimer.start(); - else - { - caretBlinkTimer.stop(); - caret.setVisible(false); - } - boolean oldValue = editable; editable = newValue; firePropertyChange("editable", oldValue, newValue); @@ -1230,8 +1180,6 @@ public abstract class JTextComponent extends JComponent Caret oldCaret = caret; caret = newCaret; - caretBlinkTimer.update(); - if (caret != null) caret.install(this); @@ -1399,7 +1347,7 @@ public abstract class JTextComponent extends JComponent start = Math.max(start, 0); start = Math.min(start, length); - end = Math.max(end, 0); + end = Math.max(end, start); end = Math.min(end, length); setCaretPosition(start); @@ -1422,28 +1370,28 @@ public abstract class JTextComponent extends JComponent // If content is empty delete selection. if (content == null) { - caret.setDot(dot); - return; + caret.setDot(dot); + return; } try { - int start = getSelectionStart(); - int end = getSelectionEnd(); - - // Remove selected text. - if (dot != mark) - doc.remove(start, end - start); - - // Insert new text. - doc.insertString(start, content, null); - - // Set dot to new position. - setCaretPosition(start + content.length()); + int start = getSelectionStart(); + int end = getSelectionEnd(); + + // Remove selected text. + if (dot != mark) + doc.remove(start, end - start); + + // Insert new text. + doc.insertString(start, content, null); + + // Set dot to new position. + setCaretPosition(start + content.length()); } catch (BadLocationException e) { - // This should never happen. + // This should never happen. } } @@ -1577,15 +1525,15 @@ public abstract class JTextComponent extends JComponent // Install default TransferHandler if none set. if (getTransferHandler() == null) { - if (defaultTransferHandler == null) - defaultTransferHandler = new DefaultTransferHandler(); - - setTransferHandler(defaultTransferHandler); + if (defaultTransferHandler == null) + defaultTransferHandler = new DefaultTransferHandler(); + + setTransferHandler(defaultTransferHandler); } // Perform action. ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, - action.getValue(Action.NAME).toString()); + action.getValue(Action.NAME).toString()); action.actionPerformed(event); } @@ -1669,5 +1617,20 @@ public abstract class JTextComponent extends JComponent throws IOException { output.write(getText()); - } + } + + /** + * Returns the tooltip text for this text component for the given mouse + * event. This forwards the call to + * {@link TextUI#getToolTipText(JTextComponent, Point)}. + * + * @param ev the mouse event + * + * @return the tooltip text for this text component for the given mouse + * event + */ + public String getToolTipText(MouseEvent ev) + { + return getUI().getToolTipText(this, ev.getPoint()); + } } diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java index a10391613cd..4890735b925 100644 --- a/libjava/classpath/javax/swing/text/LabelView.java +++ b/libjava/classpath/javax/swing/text/LabelView.java @@ -38,10 +38,57 @@ exception statement from your version. */ package javax.swing.text; -// TODO: Implement this class. -public class LabelView - extends GlyphView +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Shape; + +import javax.swing.event.DocumentEvent; + +/** + * A {@link GlyphView} that caches the textattributes for most effective + * rendering. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class LabelView extends GlyphView { + + /** + * The background color. + */ + Color background; + + /** + * The foreground color. + */ + Color foreground; + + /** + * The background color. + */ + Font font; + + /** + * The strikethrough flag. + */ + boolean strikeThrough; + + /** + * The underline flag. + */ + boolean underline; + + /** + * The subscript flag. + */ + boolean subscript; + + /** + * The superscript flag. + */ + boolean superscript; + /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. * @@ -50,5 +97,194 @@ public class LabelView public LabelView(Element element) { super(element); + setPropertiesFromAttributes(); + } + + /** + * Loads the properties of this label view from the element's text + * attributes. This method is called from the constructor and the + * {@link #changedUpdate} method + */ + protected void setPropertiesFromAttributes() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + background = StyleConstants.getBackground(atts); + foreground = StyleConstants.getForeground(atts); + strikeThrough = StyleConstants.isStrikeThrough(atts); + subscript = StyleConstants.isSubscript(atts); + superscript = StyleConstants.isSuperscript(atts); + underline = StyleConstants.isUnderline(atts); + + // Determine the font. + String family = StyleConstants.getFontFamily(atts); + int size = StyleConstants.getFontSize(atts); + int style = Font.PLAIN; + if (StyleConstants.isBold(atts)) + style |= Font.BOLD; + if (StyleConstants.isItalic(atts)) + style |= Font.ITALIC; + font = new Font(family, style, size); + } + + /** + * Receives notification when text attributes change in the chunk of + * text that this view is responsible for. This simply calls + * {@link #setPropertiesFromAttributes()}. + * + * @param e the document event + * @param a the allocation of this view + * @param vf the view factory to use for creating new views + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + setPropertiesFromAttributes(); + } + + /** + * Returns the background color for the glyphs. + * + * @return the background color for the glyphs + */ + public Color getBackground() + { + return background; + } + + /** + * Sets the background color for the glyphs. A value of <code>null</code> + * means the background of the parent view should shine through. + * + * @param bg the background to set or <code>null</code> + * + * @since 1.5 + */ + protected void setBackground(Color bg) + { + background = bg; + } + + /** + * Returns the foreground color for the glyphs. + * + * @return the foreground color for the glyphs + */ + public Color getForeground() + { + return foreground; + } + + /** + * Returns the font for the glyphs. + * + * @return the font for the glyphs + */ + public Font getFont() + { + return font; + } + + /** + * Returns the font metrics of the current font. + * + * @return the font metrics of the current font + * + * @deprecated this is not used anymore + */ + protected FontMetrics getFontMetrics() + { + return getContainer().getGraphics().getFontMetrics(font); + } + + /** + * Returns <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise + */ + public boolean isUnderline() + { + return underline; + } + + /** + * Sets the underline flag. + * + * @param flag <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise + */ + protected void setUnderline(boolean flag) + { + underline = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise + */ + public boolean isSubscript() + { + return subscript; + } + + /** + * Sets the subscript flag. + * + * @param flag <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise + */ + protected void setSubscript(boolean flag) + { + subscript = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise + */ + public boolean isSuperscript() + { + return superscript; + } + + /** + * Sets the superscript flag. + * + * @param flag <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise + */ + protected void setSuperscript(boolean flag) + { + superscript = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise + */ + public boolean isStrikeThrough() + { + return strikeThrough; + } + + /** + * Sets the strike-through flag. + * + * @param flag <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise + */ + protected void setStrikeThrough(boolean flag) + { + strikeThrough = flag; } } diff --git a/libjava/classpath/javax/swing/text/LayoutQueue.java b/libjava/classpath/javax/swing/text/LayoutQueue.java index 83433b6eef5..b0c84b972b2 100644 --- a/libjava/classpath/javax/swing/text/LayoutQueue.java +++ b/libjava/classpath/javax/swing/text/LayoutQueue.java @@ -57,6 +57,7 @@ public class LayoutQueue */ public LayoutQueue() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java index 6c6006a2a0f..6fb121f949e 100644 --- a/libjava/classpath/javax/swing/text/ParagraphView.java +++ b/libjava/classpath/javax/swing/text/ParagraphView.java @@ -59,6 +59,11 @@ public class ParagraphView extends FlowView implements TabExpander { super(el, X_AXIS); } + public float getAlignment(int axis) + { + // FIXME: This is very likely not 100% correct. Work this out. + return 0.0F; + } } /** @@ -86,4 +91,29 @@ public class ParagraphView extends FlowView implements TabExpander { return new Row(getElement()); } + + /** + * Returns the alignment for this paragraph view for the specified axis. + * For the X_AXIS the paragraph view will be aligned at it's left edge + * (0.0F). For the Y_AXIS the paragraph view will be aligned at the + * center of it's first row. + * + * @param axis the axis which is examined + * + * @return the alignment for this paragraph view for the specified axis + */ + public float getAlignment(int axis) + { + if (axis == X_AXIS) + return 0.0F; + else if (getViewCount() > 0) + { + + float prefHeight = getPreferredSpan(Y_AXIS); + float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS); + return (firstRowHeight / 2.F) / prefHeight; + } + else + return 0.0F; + } } diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index 71070e92da7..9e600c4c908 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -132,8 +132,8 @@ public class PlainDocument extends AbstractDocument // collapse elements if the removal spans more than 1 line Element newEl = createLeafElement(rootElement, SimpleAttributeSet.EMPTY, - start, end - len); - rootElement.replace(i1, i2 - i1, new Element[]{ newEl }); + start, end); + rootElement.replace(i1, i2 - i1 + 1, new Element[]{ newEl }); } } @@ -147,4 +147,28 @@ public class PlainDocument extends AbstractDocument Element root = getDefaultRootElement(); return root.getElement(root.getElementIndex(pos)); } + + /** + * Inserts a string into the document. If the document property + * '<code>filterNewLines</code>' is set to <code>Boolean.TRUE</code>, then + * all newlines in the inserted string are replaced by space characters, + * otherwise the superclasses behaviour is executed. + * + * Inserting content causes a write lock to be acquired during this method + * call. + * + * @param offs the offset at which to insert the string + * @param str the string to be inserted + * @param atts the text attributes of the string to be inserted + * + * @throws BadLocationException + */ + public void insertString(int offs, String str, AttributeSet atts) + throws BadLocationException + { + String string = str; + if (Boolean.TRUE.equals(getProperty("filterNewlines"))) + string = str.replaceAll("\n", " "); + super.insertString(offs, string, atts); + } } diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java index 91d7547e77c..9f5ee8ad3c8 100644 --- a/libjava/classpath/javax/swing/text/PlainView.java +++ b/libjava/classpath/javax/swing/text/PlainView.java @@ -46,15 +46,35 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -public class PlainView extends View - implements TabExpander +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentEvent.ElementChange; + +public class PlainView extends View implements TabExpander { Color selectedColor; Color unselectedColor; + + /** + * The color that is used to draw disabled text fields. + */ + Color disabledColor; + Font font; + /** The length of the longest line in the Document **/ + float maxLineLength = -1; + + /** The longest line in the Document **/ + Element longestLine = null; + protected FontMetrics metrics; + /** + * The instance returned by {@link #getLineBuffer()}. + */ + private transient Segment lineBuffer; + public PlainView(Element elem) { super(elem); @@ -104,7 +124,7 @@ public class PlainView extends View // Get the rectangle for position. Element line = getElement().getElement(lineIndex); int lineStart = line.getStartOffset(); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); document.getText(lineStart, position - lineStart, segment); int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x, this, lineStart); @@ -129,7 +149,9 @@ public class PlainView extends View } catch (BadLocationException e) { - // This should never happen. + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(e); + throw ae; } } @@ -137,7 +159,7 @@ public class PlainView extends View throws BadLocationException { g.setColor(selectedColor); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, 0); } @@ -145,8 +167,13 @@ public class PlainView extends View protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - g.setColor(unselectedColor); - Segment segment = new Segment(); + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset); } @@ -161,7 +188,8 @@ public class PlainView extends View g.setFont(textComponent.getFont()); selectedColor = textComponent.getSelectedTextColor(); unselectedColor = textComponent.getForeground(); - + disabledColor = textComponent.getDisabledTextColor(); + Rectangle rect = s.getBounds(); // FIXME: Text may be scrolled. @@ -176,9 +204,19 @@ public class PlainView extends View } } + /** + * Returns the tab size of a tab. Checks the Document's + * properties for PlainDocument.tabSizeAttribute and returns it if it is + * defined, otherwise returns 8. + * + * @return the tab size. + */ protected int getTabSize() { - return 8; + Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute); + if (tabSize == null) + return 8; + return ((Integer)tabSize).intValue(); } /** @@ -191,10 +229,54 @@ public class PlainView extends View */ public float nextTabStop(float x, int tabStop) { - float tabSizePixels = getTabSize() + metrics.charWidth('m'); + float tabSizePixels = getTabSize() * metrics.charWidth('m'); return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; } + /** + * Returns the length of the longest line, used for getting the span + * @return the length of the longest line + */ + float determineMaxLineLength() + { + // if the longest line is cached, return the cached value + if (maxLineLength != -1) + return maxLineLength; + + // otherwise we have to go through all the lines and find it + Element el = getElement(); + Segment seg = getLineBuffer(); + float span = 0; + for (int i = 0; i < el.getElementCount(); i++) + { + Element child = el.getElement(i); + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > span) + { + longestLine = child; + span = width; + } + } + maxLineLength = span; + return maxLineLength; + } + public float getPreferredSpan(int axis) { if (axis != X_AXIS && axis != Y_AXIS) @@ -205,36 +287,16 @@ public class PlainView extends View float span = 0; Element el = getElement(); - Document doc = el.getDocument(); - Segment seg = new Segment(); switch (axis) { case X_AXIS: - // calculate the maximum of the line's widths - for (int i = 0; i < el.getElementCount(); i++) - { - Element child = el.getElement(i); - int start = child.getStartOffset(); - int end = child.getEndOffset(); - try { - doc.getText(start, start + end, seg); - } - catch (BadLocationException ex) - { - // throw new ClasspathAssertionError - // ("no BadLocationException should be thrown here"); - } - int width = metrics.charsWidth(seg.array, seg.offset, seg.count); - span = Math.max(span, width); - } - break; + span = determineMaxLineLength(); case Y_AXIS: default: span = metrics.getHeight() * el.getElementCount(); break; } - return span; } @@ -252,8 +314,251 @@ public class PlainView extends View */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // FIXME: not implemented - return 0; + Rectangle rec = a.getBounds(); + Document doc = getDocument(); + Element root = doc.getDefaultRootElement(); + + // PlainView doesn't support line-wrapping so we can find out which + // Element was clicked on just by the y-position + int lineClicked = (int) (y - rec.y) / metrics.getHeight(); + if (lineClicked >= root.getElementCount()) + return getEndOffset() - 1; + + Element line = root.getElement(lineClicked); + Segment s = getLineBuffer(); + int start = line.getStartOffset(); + // We don't want the \n at the end of the line. + int end = line.getEndOffset() - 1; + try + { + doc.getText(start, end - start, s); + } + catch (BadLocationException ble) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ble); + throw ae; + } + + int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start); + return Math.max (0, pos); + } + + /** + * Since insertUpdate and removeUpdate each deal with children + * Elements being both added and removed, they both have to perform + * the same checks. So they both simply call this method. + * @param changes the DocumentEvent for the changes to the Document. + * @param a the allocation of the View. + * @param f the ViewFactory to use for rebuilding. + */ + protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) + { + Element el = getElement(); + ElementChange ec = changes.getChange(el); + + // If ec is null then no lines were added or removed, just + // repaint the changed line + if (ec == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + Element[] removed = ec.getChildrenRemoved(); + Element[] newElements = ec.getChildrenAdded(); + + // If no Elements were added or removed, we just want to repaint + // the area containing the line that was modified + if (removed == null && newElements == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + // Check to see if we removed the longest line, if so we have to + // search through all lines and find the longest one again + if (removed != null) + { + for (int i = 0; i < removed.length; i++) + if (removed[i].equals(longestLine)) + { + // reset maxLineLength and search through all lines for longest one + maxLineLength = -1; + determineMaxLineLength(); + ((JTextComponent)getContainer()).repaint(); + return; + } + } + + // If we've reached here, that means we haven't removed the longest line + if (newElements == null) + { + // No lines were added, just repaint the container and exit + ((JTextComponent)getContainer()).repaint(); + return; + } + + // Make sure we have the metrics + updateMetrics(); + + // If we've reached here, that means we haven't removed the longest line + // and we have added at least one line, so we have to check if added lines + // are longer than the previous longest line + Segment seg = getLineBuffer(); + float longestNewLength = 0; + Element longestNewLine = null; + + // Loop through the added lines to check their length + for (int i = 0; i < newElements.length; i++) + { + Element child = newElements[i]; + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > longestNewLength) + { + longestNewLine = child; + longestNewLength = width; + } + } + + // Check if the longest of the new lines is longer than our previous + // longest line, and if so update our values + if (longestNewLength > maxLineLength) + { + maxLineLength = longestNewLength; + longestLine = longestNewLine; + } + // Repaint the container + ((JTextComponent)getContainer()).repaint(); + } + + /** + * This method is called when something is inserted into the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when something is removed from the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when attributes were changed in the + * Document in a location that this view is responsible for. + */ + public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * Repaint the given line range. This is called from insertUpdate, + * changedUpdate, and removeUpdate when no new lines were added + * and no lines were removed, to repaint the line that was + * modified. + * + * @param line0 the start of the range + * @param line1 the end of the range + * @param a the rendering region of the host + * @param host the Component that uses this View (used to call repaint + * on that Component) + * + * @since 1.4 + */ + protected void damageLineRange (int line0, int line1, Shape a, Component host) + { + if (a == null) + return; + + Rectangle rec0 = lineToRect(a, line0); + Rectangle rec1 = lineToRect(a, line1); + + if (rec0 == null || rec1 == null) + // something went wrong, repaint the entire host to be safe + host.repaint(); + else + { + Rectangle repaintRec = rec0.union(rec1); + host.repaint(); + } + } + + /** + * Provides a {@link Segment} object, that can be used to fetch text from + * the document. + * + * @returna {@link Segment} object, that can be used to fetch text from + * the document + */ + protected Segment getLineBuffer() + { + if (lineBuffer == null) + lineBuffer = new Segment(); + return lineBuffer; + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); } } diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java index 92d850016d9..84e0e700f2e 100644 --- a/libjava/classpath/javax/swing/text/Segment.java +++ b/libjava/classpath/javax/swing/text/Segment.java @@ -39,8 +39,7 @@ package javax.swing.text; import java.text.CharacterIterator; -public class Segment - implements Cloneable, CharacterIterator +public class Segment implements Cloneable, CharacterIterator { private boolean partialReturn; private int current; @@ -51,6 +50,7 @@ public class Segment public Segment() { + // Nothing to do here. } public Segment(char[] array, int offset, int count) diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java index 3ef5db61d43..0c9f607b196 100644 --- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java +++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java @@ -45,6 +45,9 @@ import java.util.Hashtable; public class SimpleAttributeSet implements MutableAttributeSet, Serializable, Cloneable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 8267656273837665219L; + public static final AttributeSet EMPTY = new SimpleAttributeSet(); Hashtable tab; @@ -84,12 +87,34 @@ public class SimpleAttributeSet return s; } + /** + * Returns true if the given name and value represent an attribute + * found either in this AttributeSet or in its resolve parent hierarchy. + * @param name the key for the attribute + * @param value the value for the attribute + * @return true if the attribute is found here or in this set's resolve + * parent hierarchy + */ public boolean containsAttribute(Object name, Object value) { + return (tab.containsKey(name) && tab.get(name).equals(value)) || + (getResolveParent() != null && getResolveParent(). + containsAttribute(name, value)); + } + + /** + * Returns true if the given name and value are found in this AttributeSet. + * Does not check the resolve parent. + * @param name the key for the attribute + * @param value the value for the attribute + * @return true if the attribute is found in this AttributeSet + */ + boolean containsAttributeLocally(Object name, Object value) + { return tab.containsKey(name) && tab.get(name).equals(value); } - + public boolean containsAttributes(AttributeSet attributes) { Enumeration e = attributes.getAttributeNames(); @@ -110,9 +135,9 @@ public class SimpleAttributeSet public boolean equals(Object obj) { - return (obj != null) - && (obj instanceof SimpleAttributeSet) - && ((SimpleAttributeSet)obj).tab.equals(this.tab); + return + (obj instanceof AttributeSet) + && this.isEqual((AttributeSet) obj); } public Object getAttribute(Object name) @@ -157,10 +182,16 @@ public class SimpleAttributeSet { return tab.isEmpty(); } - + + /** + * Returns true if the given set has the same number of attributes + * as this set and <code>containsAttributes(attr)</code> returns + * true. + */ public boolean isEqual(AttributeSet attr) { - return this.equals(attr); + return getAttributeCount() == attr.getAttributeCount() + && this.containsAttributes(attr); } public void removeAttribute(Object name) @@ -168,9 +199,21 @@ public class SimpleAttributeSet tab.remove(name); } + /** + * Removes attributes from this set if they are found in the + * given set. Only attributes whose key AND value are removed. + * Removes attributes only from this set, not from the resolving parent. + */ public void removeAttributes(AttributeSet attributes) { - removeAttributes(attributes.getAttributeNames()); + Enumeration e = attributes.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + Object val = attributes.getAttribute(name); + if (containsAttributeLocally(name, val)) + removeAttribute(name); + } } public void removeAttributes(Enumeration names) diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java index bedf480d4ec..7db377a1c9b 100644 --- a/libjava/classpath/javax/swing/text/StringContent.java +++ b/libjava/classpath/javax/swing/text/StringContent.java @@ -56,6 +56,9 @@ import javax.swing.undo.UndoableEdit; */ public final class StringContent implements AbstractDocument.Content, Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 4755994433709540381L; + // This is package-private to avoid an accessor method. char[] content; diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java index 3f973f22631..598eaf621bc 100644 --- a/libjava/classpath/javax/swing/text/StyleConstants.java +++ b/libjava/classpath/javax/swing/text/StyleConstants.java @@ -54,11 +54,13 @@ public class StyleConstants public static final Object BidiLevel = CharacterConstants.BidiLevel; public static final Object Bold = CharacterConstants.Bold; public static final Object ComponentAttribute = CharacterConstants.ComponentAttribute; - public static final Object FontFamily = CharacterConstants.Family; + public static final Object Family = CharacterConstants.Family; + public static final Object FontFamily = CharacterConstants.Family; public static final Object FontSize = CharacterConstants.Size; public static final Object Foreground = CharacterConstants.Foreground; public static final Object IconAttribute = CharacterConstants.IconAttribute; public static final Object Italic = CharacterConstants.Italic; + public static final Object Size = CharacterConstants.Size; public static final Object StrikeThrough = CharacterConstants.StrikeThrough; public static final Object Subscript = CharacterConstants.Subscript; public static final Object Superscript = CharacterConstants.Superscript; @@ -109,7 +111,7 @@ public class StyleConstants if (a.isDefined(Background)) return (Color) a.getAttribute(Background); else - return Color.BLACK; + return Color.WHITE; } public static int getBidiLevel(AttributeSet a) diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java index ae11622ffc6..6c4e299455f 100644 --- a/libjava/classpath/javax/swing/text/StyleContext.java +++ b/libjava/classpath/javax/swing/text/StyleContext.java @@ -57,9 +57,15 @@ import javax.swing.event.EventListenerList; public class StyleContext implements Serializable, AbstractDocument.AttributeContext { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 8042858831190784241L; + public class NamedStyle implements Serializable, Style { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6690628971806226374L; + protected ChangeEvent changeEvent; protected EventListenerList listenerList; @@ -288,7 +294,7 @@ public class StyleContext public boolean equals(Object obj) { return - (obj instanceof SmallAttributeSet) + (obj instanceof AttributeSet) && this.isEqual((AttributeSet)obj); } @@ -300,9 +306,14 @@ public class StyleContext return attrs[i+1]; } - Object p = getResolveParent(); - if (p != null && p instanceof AttributeSet) - return (((AttributeSet)p).getAttribute(key)); + // Check the resolve parent, unless we're looking for the + // ResolveAttribute, which would cause an infinite loop + if (!(key.equals(ResolveAttribute))) + { + Object p = getResolveParent(); + if (p != null && p instanceof AttributeSet) + return (((AttributeSet)p).getAttribute(key)); + } return null; } diff --git a/libjava/classpath/javax/swing/text/StyledDocument.java b/libjava/classpath/javax/swing/text/StyledDocument.java index ea277540f23..168e1b116f3 100644 --- a/libjava/classpath/javax/swing/text/StyledDocument.java +++ b/libjava/classpath/javax/swing/text/StyledDocument.java @@ -45,101 +45,96 @@ import java.awt.Font; * @author Andrew Selkirk * @version 1.0 */ -public interface StyledDocument extends Document { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * addStyle - * @param nm TODO - * @param rent TODO - * @returns Style - */ - Style addStyle(String nm, Style parent); - - /** - * removeStyle - * @param nm TODO - */ - void removeStyle(String nm); - - /** - * getStyle - * @param nm TODO - * @returns Style - */ - Style getStyle(String nm); - - /** - * setCharacterAttributes - * @param offset TODO - * @param length TODO - * @param set TODO - * @param replace TODO - */ - void setCharacterAttributes(int offset, int length, - AttributeSet set, boolean replace); - - /** - * setParagraphAttributes - * @param offset TODO - * @param length TODO - * @param set TODO - * @param replace TODO - */ - void setParagraphAttributes(int offset, int length, - AttributeSet set, boolean replace); - - /** - * getLogicalStyle - * @param position TODO - * @returns Style - */ - Style getLogicalStyle(int position); - - /** - * setLogicalStyle - * @param position TODO - * @param style TODO - */ - void setLogicalStyle(int position, Style style); - - /** - * getParagraphElement - * @param position TODO - * @returns Element - */ - Element getParagraphElement(int position); - - /** - * getCharacterElement - * @param position TODO - * @returns Element - */ - Element getCharacterElement(int position); - - /** - * getForeground - * @param set TODO - * @returns Color - */ - Color getForeground(AttributeSet set); - - /** - * getBackground - * @param set TODO - * @returns Color - */ - Color getBackground(AttributeSet set); - - /** - * getFont - * @param set TODO - * @returns Font - */ - Font getFont(AttributeSet set); - - -} // StyledDocument +public interface StyledDocument extends Document +{ + /** + * addStyle + * @param nm TODO + * @param parent TODO + * @returns Style + */ + Style addStyle(String nm, Style parent); + + /** + * removeStyle + * @param nm TODO + */ + void removeStyle(String nm); + + /** + * getStyle + * @param nm TODO + * @returns Style + */ + Style getStyle(String nm); + + /** + * setCharacterAttributes + * @param offset TODO + * @param length TODO + * @param set TODO + * @param replace TODO + */ + void setCharacterAttributes(int offset, int length, AttributeSet set, + boolean replace); + + /** + * setParagraphAttributes + * @param offset TODO + * @param length TODO + * @param set TODO + * @param replace TODO + */ + void setParagraphAttributes(int offset, int length, AttributeSet set, + boolean replace); + + /** + * getLogicalStyle + * @param position TODO + * @returns Style + */ + Style getLogicalStyle(int position); + + /** + * setLogicalStyle + * @param position TODO + * @param style TODO + */ + void setLogicalStyle(int position, Style style); + + /** + * getParagraphElement + * @param position TODO + * @returns Element + */ + Element getParagraphElement(int position); + + /** + * getCharacterElement + * @param position TODO + * @returns Element + */ + Element getCharacterElement(int position); + + /** + * getForeground + * @param set TODO + * @returns Color + */ + Color getForeground(AttributeSet set); + + /** + * getBackground + * @param set TODO + * @returns Color + */ + Color getBackground(AttributeSet set); + + /** + * getFont + * @param set TODO + * @returns Font + */ + Font getFont(AttributeSet set); + +} diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java index 89c4cf18ee4..e71f992b534 100644 --- a/libjava/classpath/javax/swing/text/StyledEditorKit.java +++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java @@ -40,13 +40,9 @@ package javax.swing.text; import java.awt.Color; import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Serializable; import javax.swing.Action; import javax.swing.JEditorPane; -import javax.swing.JTextPane; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -460,11 +456,11 @@ public class StyledEditorKit extends DefaultEditorKit * <code>StyledEditorKit</code>, namely the following types of Elements: * * <ul> - * <li>{@link AbstractDocument.ContentElementName}</li> - * <li>{@link AbstractDocument.ParagraphElementName}</li> - * <li>{@link AbstractDocument.SectionElementName}</li> - * <li>{@link StyleContext.ComponentElementName}</li> - * <li>{@link StyleContext.IconElementName}</li> + * <li>{@link AbstractDocument#ContentElementName}</li> + * <li>{@link AbstractDocument#ParagraphElementName}</li> + * <li>{@link AbstractDocument#SectionElementName}</li> + * <li>{@link StyleConstants#ComponentElementName}</li> + * <li>{@link StyleConstants#IconElementName}</li> * </ul> */ static class StyledViewFactory @@ -667,11 +663,11 @@ public class StyledEditorKit extends DefaultEditorKit * namely the following types of <code>Element</code>s: * * <ul> - * <li>{@link AbstractDocument.ContentElementName}</li> - * <li>{@link AbstractDocument.ParagraphElementName}</li> - * <li>{@link AbstractDocument.SectionElementName}</li> - * <li>{@link StyleContext.ComponentElementName}</li> - * <li>{@link StyleContext.IconElementName}</li> + * <li>{@link AbstractDocument#ContentElementName}</li> + * <li>{@link AbstractDocument#ParagraphElementName}</li> + * <li>{@link AbstractDocument#SectionElementName}</li> + * <li>{@link StyleConstants#ComponentElementName}</li> + * <li>{@link StyleConstants#IconElementName}</li> * </ul> * * @return a {@link ViewFactory} that is able to create {@link View}s diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java index 146f545aac7..ecad9444ea5 100644 --- a/libjava/classpath/javax/swing/text/TabSet.java +++ b/libjava/classpath/javax/swing/text/TabSet.java @@ -41,6 +41,9 @@ import java.io.Serializable; public class TabSet implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 2367703481999080593L; + TabStop[] tabs; public TabSet(TabStop[] t) diff --git a/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java index 032da8bca46..56f862fdae4 100644 --- a/libjava/classpath/javax/swing/text/TabStop.java +++ b/libjava/classpath/javax/swing/text/TabStop.java @@ -41,6 +41,9 @@ import java.io.Serializable; public class TabStop implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -5381995917363605058L; + public static final int ALIGN_LEFT = 0; public static final int ALIGN_RIGHT = 1; public static final int ALIGN_CENTER = 2; diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java index d40408ddc3f..7830b2fca04 100644 --- a/libjava/classpath/javax/swing/text/Utilities.java +++ b/libjava/classpath/javax/swing/text/Utilities.java @@ -40,6 +40,11 @@ package javax.swing.text; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.text.BreakIterator; + +import javax.swing.SwingConstants; /** * A set of utilities to deal with text. This is used by several other classes @@ -195,4 +200,409 @@ public class Utilities return maxWidth; } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * The parameter <code>round</code> controls which model location is returned + * if the view coordinates are on a character: If <code>round</code> is + * <code>true</code>, then the result is rounded up to the next character, so + * that the resulting fragment is the smallest fragment that is larger than + * the specified span. If <code>round</code> is <code>false</code>, then the + * resulting fragment is the largest fragment that is smaller than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * @param round if <code>true</code> round up to the next location, otherwise + * round down to the current location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0, + boolean round) + { + // At the end of the for loop, this holds the requested model location + int pos; + int currentX = x0; + + for (pos = p0; pos < s.count; pos++) + { + char nextChar = s.array[s.offset+pos]; + if (nextChar == 0) + { + if (! round) + pos--; + break; + } + if (nextChar != '\t') + currentX += fm.charWidth(nextChar); + else + { + if (te == null) + currentX += fm.charWidth(' '); + else + currentX = (int) te.nextTabStop(currentX, pos); + } + if (currentX > x) + { + if (! round) + pos--; + break; + } + } + return pos; + } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * This method rounds up to the next location, so that the resulting fragment + * will be the smallest fragment of the text, that is greater than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0) + { + return getTabbedTextOffset(s, fm, x0, x, te, p0, true); + } + + /** + * Finds the start of the next word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the next word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getNextWord(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs > (c.getText().length() - 1)) + throw new BadLocationException("invalid offset specified", offs); + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + int last = wb.following(offs); + int current = wb.next(); + while (current != BreakIterator.DONE) + { + for (int i = last; i < current; i++) + { + // FIXME: Should use isLetter(int) and text.codePointAt(int) + // instead, but isLetter(int) isn't implemented yet + if (Character.isLetter(text.charAt(i))) + return last; + } + last = current; + current = wb.next(); + } + return BreakIterator.DONE; + } + + /** + * Finds the start of the previous word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the previous word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getPreviousWord(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs > (c.getText().length() - 1)) + throw new BadLocationException("invalid offset specified", offs); + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + int last = wb.preceding(offs); + int current = wb.previous(); + + while (current != BreakIterator.DONE) + { + for (int i = last; i < offs; i++) + { + // FIXME: Should use isLetter(int) and text.codePointAt(int) + // instead, but isLetter(int) isn't implemented yet + if (Character.isLetter(text.charAt(i))) + return last; + } + last = current; + current = wb.previous(); + } + return 0; + } + + /** + * Finds the start of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word beginning + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordStart(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs >= c.getText().length()) + throw new BadLocationException("invalid offset specified", offs); + + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + if (wb.isBoundary(offs)) + return offs; + return wb.preceding(offs); + } + + /** + * Finds the end of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word end + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordEnd(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs >= c.getText().length()) + throw new BadLocationException("invalid offset specified", offs); + + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + return wb.following(offs); + } + + /** + * Get the model position of the end of the row that contains the + * specified model position. Return null if the given JTextComponent + * does not have a size. + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the end of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowEnd(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the smallest position X > offs + // such that that character at positino X is not on the same + // line as the character at position offs + int high = offs + ((text.length() - 1 - offs) / 2); + int low = offs; + int oldHigh = text.length() + 1; + while (true) + { + if (c.modelToView(high).y != c.modelToView(offs).y) + { + oldHigh = high; + high = low + ((high + 1 - low) / 2); + if (oldHigh == high) + return high - 1; + } + else + { + low = high; + high += ((oldHigh - high) / 2); + if (low == high) + return low; + } + } + } + + /** + * Get the model position of the start of the row that contains the specified + * model position. Return null if the given JTextComponent does not have a + * size. + * + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the start of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowStart(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the greatest position X < offs + // such that the character at position X is not on the same + // row as the character at position offs + int high = offs; + int low = 0; + int oldLow = 0; + while (true) + { + if (c.modelToView(low).y != c.modelToView(offs).y) + { + oldLow = low; + low = high - ((high + 1 - low) / 2); + if (oldLow == low) + return low + 1; + } + else + { + high = low; + low -= ((low - oldLow) / 2); + if (low == high) + return low; + } + } + } + + /** + * Determine where to break the text in the given Segment, attempting to find + * a word boundary. + * @param s the Segment that holds the text + * @param metrics the font metrics used for calculating the break point + * @param x0 starting view location representing the start of the text + * @param x the target view location + * @param e the TabExpander used for expanding tabs (if this is null tabs + * are expanded to 1 space) + * @param startOffset the offset in the Document of the start of the text + * @return the offset at which we should break the text + */ + public static final int getBreakLocation(Segment s, FontMetrics metrics, + int x0, int x, TabExpander e, + int startOffset) + { + int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset); + BreakIterator breaker = BreakIterator.getWordInstance(); + breaker.setText(s.toString()); + + // If mark is equal to the end of the string, just use that position + if (mark == s.count) + return mark; + + // Try to find a word boundary previous to the mark at which we + // can break the text + int preceding = breaker.preceding(mark + 1); + + if (preceding != 0) + return preceding; + else + // If preceding is 0 we couldn't find a suitable word-boundary so + // just break it on the character boundary + return mark; + } + + /** + * Returns the paragraph element in the text component <code>c</code> at + * the specified location <code>offset</code>. + * + * @param c the text component + * @param offset the offset of the paragraph element to return + * + * @return the paragraph element at <code>offset</code> + */ + public static final Element getParagraphElement(JTextComponent c, int offset) + { + Document doc = c.getDocument(); + Element par = null; + if (doc instanceof StyledDocument) + { + StyledDocument styledDoc = (StyledDocument) doc; + par = styledDoc.getParagraphElement(offset); + } + else + { + Element root = c.getDocument().getDefaultRootElement(); + int parIndex = root.getElementIndex(offset); + par = root.getElement(parIndex); + } + return par; + } + + /** + * Returns the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionAbove(JTextComponent c, int offset, int x) + throws BadLocationException + { + View rootView = c.getUI().getRootView(c); + Rectangle r = c.modelToView(offset); + int offs = c.viewToModel(new Point(x, r.y)); + int pos = rootView.getNextVisualPositionFrom(c, offs, + Position.Bias.Forward, + SwingConstants.NORTH, + new Position.Bias[1]); + return pos; + } + + /** + * Returns the document position that is closest below to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionBelow(JTextComponent c, int offset, int x) + throws BadLocationException + { + View rootView = c.getUI().getRootView(c); + Rectangle r = c.modelToView(offset); + int offs = c.viewToModel(new Point(x, r.y)); + int pos = rootView.getNextVisualPositionFrom(c, offs, + Position.Bias.Forward, + SwingConstants.SOUTH, + new Position.Bias[1]); + return pos; + } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index 24efba9a1bc..daab347d731 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/javax/swing/text/View.java @@ -43,7 +43,6 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; @@ -87,9 +86,9 @@ public abstract class View implements SwingConstants { View parent = getParent(); if (parent == null) - throw new AssertionError("The parent of a View must not be null."); - - return parent.getContainer(); + return null; + else + return parent.getContainer(); } public Document getDocument() @@ -508,6 +507,30 @@ public abstract class View implements SwingConstants } /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * This method is deprecated and calls + * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with + * a bias of {@link Position.Bias#Forward}. + * + * @param pos the position of the character in the model + * @param a the area that is occupied by the view + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if <code>pos</code> is invalid + * + * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead. + */ + public Shape modelToView(int pos, Shape a) throws BadLocationException + { + return modelToView(pos, a, Position.Bias.Forward); + } + + /** * Maps coordinates from the <code>View</code>'s space into a position * in the document model. * @@ -521,6 +544,25 @@ public abstract class View implements SwingConstants */ public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b); + /** + * Maps coordinates from the <code>View</code>'s space into a position + * in the document model. This method is deprecated and only there for + * compatibility. + * + * @param x the x coordinate in the view space + * @param y the y coordinate in the view space + * @param a the allocation of this <code>View</code> + * + * @return the position in the document that corresponds to the screen + * coordinates <code>x, y</code> + * + * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])} + * instead. + */ + public int viewToModel(float x, float y, Shape a) + { + return viewToModel(x, y, a, new Position.Bias[0]); + } /** * Dumps the complete View hierarchy. This method can be used for debugging @@ -552,4 +594,30 @@ public abstract class View implements SwingConstants for (int i = 0; i < count; ++i) getView(i).dump(indent + 1); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public abstract int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException; } diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java new file mode 100644 index 00000000000..b90519046ae --- /dev/null +++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java @@ -0,0 +1,700 @@ +/* WrappedPlainView.java -- + Copyright (C) 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 javax.swing.text; + +import java.awt.Color; +import java.awt.Container; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; + +/** + * @author abalkiss + * + */ +public class WrappedPlainView extends BoxView implements TabExpander +{ + /** The color for selected text **/ + Color selectedColor; + + /** The color for unselected text **/ + Color unselectedColor; + + /** The color for disabled components **/ + Color disabledColor; + + /** Stores the font metrics **/ + protected FontMetrics metrics; + + /** Whether or not to wrap on word boundaries **/ + boolean wordWrap; + + /** A ViewFactory that creates WrappedLines **/ + ViewFactory viewFactory = new WrappedLineCreator(); + + /** The start of the selected text **/ + int selectionStart; + + /** The end of the selected text **/ + int selectionEnd; + + /** + * The instance returned by {@link #getLineBuffer()}. + */ + private transient Segment lineBuffer; + + public WrappedPlainView (Element elem) + { + this (elem, false); + } + + public WrappedPlainView (Element elem, boolean wordWrap) + { + super (elem, Y_AXIS); + this.wordWrap = wordWrap; + } + + /** + * Provides access to the Segment used for retrievals from the Document. + * @return the Segment. + */ + protected final Segment getLineBuffer() + { + if (lineBuffer == null) + lineBuffer = new Segment(); + return lineBuffer; + } + + /** + * Returns the next tab stop position after a given reference position. + * + * This implementation ignores the <code>tabStop</code> argument. + * + * @param x the current x position in pixels + * @param tabStop the position within the text stream that the tab occured at + */ + public float nextTabStop(float x, int tabStop) + { + JTextComponent host = (JTextComponent)getContainer(); + float tabSizePixels = getTabSize() + * host.getFontMetrics(host.getFont()).charWidth('m'); + return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; + } + + /** + * Returns the tab size for the Document based on + * PlainDocument.tabSizeAttribute, defaulting to 8 if this property is + * not defined + * + * @return the tab size. + */ + protected int getTabSize() + { + Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute); + if (tabSize == null) + return 8; + return ((Integer)tabSize).intValue(); + } + + /** + * Draws a line of text, suppressing white space at the end and expanding + * tabs. Calls drawSelectedText and drawUnselectedText. + * @param p0 starting document position to use + * @param p1 ending document position to use + * @param g graphics context + * @param x starting x position + * @param y starting y position + */ + protected void drawLine(int p0, int p1, Graphics g, int x, int y) + { + try + { + // We have to draw both selected and unselected text. There are + // several cases: + // - entire range is unselected + // - entire range is selected + // - start of range is selected, end of range is unselected + // - start of range is unselected, end of range is selected + // - middle of range is selected, start and end of range is unselected + + // entire range unselected: + if ((selectionStart == selectionEnd) || + (p0 > selectionEnd || p1 < selectionStart)) + drawUnselectedText(g, x, y, p0, p1); + + // entire range selected + else if (p0 >= selectionStart && p1 <= selectionEnd) + drawSelectedText(g, x, y, p0, p1); + + // start of range selected, end of range unselected + else if (p0 >= selectionStart) + { + x = drawSelectedText(g, x, y, p0, selectionEnd); + drawUnselectedText(g, x, y, selectionEnd, p1); + } + + // start of range unselected, end of range selected + else if (selectionStart > p0 && selectionEnd > p1) + { + x = drawUnselectedText(g, x, y, p0, selectionStart); + drawSelectedText(g, x, y, selectionStart, p1); + } + + // middle of range selected + else if (selectionStart > p0) + { + x = drawUnselectedText(g, x, y, p0, selectionStart); + x = drawSelectedText(g, x, y, selectionStart, selectionEnd); + drawUnselectedText(g, x, y, selectionEnd, p1); + } + } + catch (BadLocationException ble) + { + // shouldn't happen + } + } + + /** + * Renders the range of text as selected text. Just paints the text + * in the color specified by the host component. Assumes the highlighter + * will render the selected background. + * @param g the graphics context + * @param x the starting X coordinate + * @param y the starting Y coordinate + * @param p0 the starting model location + * @param p1 the ending model location + * @return the X coordinate of the end of the text + * @throws BadLocationException if the given range is invalid + */ + protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) + throws BadLocationException + { + g.setColor(selectedColor); + Segment segment = getLineBuffer(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, p0); + } + + /** + * Renders the range of text as normal unhighlighted text. + * @param g the graphics context + * @param x the starting X coordinate + * @param y the starting Y coordinate + * @param p0 the starting model location + * @param p1 the end model location + * @return the X location of the end off the range + * @throws BadLocationException if the range given is invalid + */ + protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) + throws BadLocationException + { + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + + Segment segment = getLineBuffer(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, p0); + } + + /** + * Loads the children to initiate the view. Called by setParent. + * Creates a WrappedLine for each child Element. + */ + protected void loadChildren (ViewFactory f) + { + Element root = getElement(); + int numChildren = root.getElementCount(); + if (numChildren == 0) + return; + + View[] children = new View[numChildren]; + for (int i = 0; i < numChildren; i++) + children[i] = new WrappedLine(root.getElement(i)); + replace(0, 0, children); + } + + /** + * Calculates the break position for the text between model positions + * p0 and p1. Will break on word boundaries or character boundaries + * depending on the break argument given in construction of this + * WrappedPlainView. Used by the nested WrappedLine class to determine + * when to start the next logical line. + * @param p0 the start model position + * @param p1 the end model position + * @return the model position at which to break the text + */ + protected int calculateBreakPosition(int p0, int p1) + { + Container c = getContainer(); + Rectangle alloc = c.isValid() ? c.getBounds() + : new Rectangle(c.getPreferredSize()); + updateMetrics(); + try + { + getDocument().getText(p0, p1 - p0, getLineBuffer()); + } + catch (BadLocationException ble) + { + // this shouldn't happen + } + // FIXME: Should we account for the insets of the container? + if (wordWrap) + return p0 + + Utilities.getBreakLocation(lineBuffer, metrics, alloc.x, + alloc.x + alloc.width, this, 0); + else + { + return p0 + + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x, + alloc.x + alloc.width, this, 0); + } + } + + void updateMetrics() + { + Container component = getContainer(); + metrics = component.getFontMetrics(component.getFont()); + } + + /** + * Determines the preferred span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getPreferredSpan (int axis) + { + updateMetrics(); + return super.getPreferredSpan(axis); + } + + /** + * Determines the minimum span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getMinimumSpan (int axis) + { + updateMetrics(); + return super.getMinimumSpan(axis); + } + + /** + * Determines the maximum span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getMaximumSpan (int axis) + { + updateMetrics(); + return super.getMaximumSpan(axis); + } + + /** + * Called when something was inserted. Overridden so that + * the view factory creates WrappedLine views. + */ + public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.insertUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + /** + * Called when something is removed. Overridden so that + * the view factory creates WrappedLine views. + */ + public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.removeUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + /** + * Called when the portion of the Document that this View is responsible + * for changes. Overridden so that the view factory creates + * WrappedLine views. + */ + public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.changedUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + class WrappedLineCreator implements ViewFactory + { + // Creates a new WrappedLine + public View create(Element elem) + { + return new WrappedLine(elem); + } + } + + /** + * Renders the <code>Element</code> that is associated with this + * <code>View</code>. Caches the metrics and then calls + * super.paint to paint all the child views. + * + * @param g the <code>Graphics</code> context to render to + * @param a the allocated region for the <code>Element</code> + */ + public void paint(Graphics g, Shape a) + { + JTextComponent comp = (JTextComponent)getContainer(); + selectionStart = comp.getSelectionStart(); + selectionEnd = comp.getSelectionEnd(); + updateMetrics(); + super.paint(g, a); + } + + /** + * Sets the size of the View. Implemented to update the metrics + * and then call super method. + */ + public void setSize (float width, float height) + { + updateMetrics(); + if (width != getWidth()) + preferenceChanged(null, true, true); + super.setSize(width, height); + } + + class WrappedLine extends View + { + /** Used to cache the number of lines for this View **/ + int numLines; + + public WrappedLine(Element elem) + { + super(elem); + determineNumLines(); + } + + /** + * Renders this (possibly wrapped) line using the given Graphics object + * and on the given rendering surface. + */ + public void paint(Graphics g, Shape s) + { + // Ensure metrics are up-to-date. + updateMetrics(); + JTextComponent textComponent = (JTextComponent) getContainer(); + + g.setFont(textComponent.getFont()); + selectedColor = textComponent.getSelectedTextColor(); + unselectedColor = textComponent.getForeground(); + disabledColor = textComponent.getDisabledTextColor(); + + // FIXME: this is a hack, for some reason textComponent.getSelectedColor + // was returning black, which is not visible against a black background + selectedColor = Color.WHITE; + + Rectangle rect = s.getBounds(); + int lineHeight = metrics.getHeight(); + + int end = getEndOffset(); + int currStart = getStartOffset(); + int currEnd; + while (currStart < end) + { + currEnd = calculateBreakPosition(currStart, end); + drawLine(currStart, currEnd, g, rect.x, rect.y); + rect.y += lineHeight; + if (currEnd == currStart) + currStart ++; + else + currStart = currEnd; + } + } + + /** + * Determines the number of logical lines that the Element + * needs to be displayed + * @return the number of lines needed to display the Element + */ + int determineNumLines() + { + numLines = 0; + int end = getEndOffset(); + if (end == 0) + return 0; + + int breakPoint; + for (int i = getStartOffset(); i < end;) + { + numLines ++; + // careful: check that there's no off-by-one problem here + // depending on which position calculateBreakPosition returns + breakPoint = calculateBreakPosition(i, end); + if (breakPoint == i) + i ++; + else + i = breakPoint; + } + return numLines; + } + + /** + * Determines the preferred span for this view along the given axis. + * + * @param axis the axis (either X_AXIS or Y_AXIS) + * + * @return the preferred span along the given axis. + * @throws IllegalArgumentException if axis is not X_AXIS or Y_AXIS + */ + public float getPreferredSpan(int axis) + { + if (axis == X_AXIS) + return getWidth(); + else if (axis == Y_AXIS) + return numLines * metrics.getHeight(); + + throw new IllegalArgumentException("Invalid axis for getPreferredSpan: " + + axis); + } + + /** + * Provides a mapping from model space to view space. + * + * @param pos the position in the model + * @param a the region into which the view is rendered + * @param b the position bias (forward or backward) + * + * @return a box in view space that represents the given position + * in model space + * @throws BadLocationException if the given model position is invalid + */ + public Shape modelToView(int pos, Shape a, Bias b) + throws BadLocationException + { + Segment s = getLineBuffer(); + int lineHeight = metrics.getHeight(); + Rectangle rect = a.getBounds(); + + // Return a rectangle with width 1 and height equal to the height + // of the text + rect.height = lineHeight; + rect.width = 1; + + int currLineStart = getStartOffset(); + int end = getEndOffset(); + + if (pos < currLineStart || pos >= end) + throw new BadLocationException("invalid offset", pos); + + while (true) + { + int currLineEnd = calculateBreakPosition(currLineStart, end); + // If pos is between currLineStart and currLineEnd then just find + // the width of the text from currLineStart to pos and add that + // to rect.x + if (pos >= currLineStart && pos < currLineEnd || pos == end - 1) + { + try + { + getDocument().getText(currLineStart, pos - currLineStart, s); + } + catch (BadLocationException ble) + { + // Shouldn't happen + } + rect.x += Utilities.getTabbedTextWidth(s, metrics, rect.x, + WrappedPlainView.this, + currLineStart); + return rect; + } + // Increment rect.y so we're checking the next logical line + rect.y += lineHeight; + + // Increment currLineStart to the model position of the start + // of the next logical line + if (currLineEnd == currLineStart) + currLineStart = end; + else + currLineStart = currLineEnd; + } + + } + + /** + * Provides a mapping from view space to model space. + * + * @param x the x coordinate in view space + * @param y the y coordinate in view space + * @param a the region into which the view is rendered + * @param b the position bias (forward or backward) + * + * @return the location in the model that best represents the + * given point in view space + */ + public int viewToModel(float x, float y, Shape a, Bias[] b) + { + Segment s = getLineBuffer(); + Rectangle rect = a.getBounds(); + int currLineStart = getStartOffset(); + int end = getEndOffset(); + int lineHeight = metrics.getHeight(); + if (y < rect.y) + return currLineStart; + if (y > rect.y + rect.height) + return end - 1; + + while (true) + { + int currLineEnd = calculateBreakPosition(currLineStart, end); + // If we're at the right y-position that means we're on the right + // logical line and we should look for the character + if (y >= rect.y && y < rect.y + lineHeight) + { + // Check if the x position is to the left or right of the text + if (x < rect.x) + return currLineStart; + if (x > rect.x + rect.width) + return currLineEnd - 1; + + try + { + getDocument().getText(currLineStart, end - currLineStart, s); + } + catch (BadLocationException ble) + { + // Shouldn't happen + } + int mark = Utilities.getTabbedTextOffset(s, metrics, rect.x, + (int) x, + WrappedPlainView.this, + currLineStart); + return currLineStart + mark; + } + // Increment rect.y so we're checking the next logical line + rect.y += lineHeight; + + // Increment currLineStart to the model position of the start + // of the next logical line + if (currLineEnd == currLineStart) + currLineStart = end; + else + currLineStart = currLineEnd; + } + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset + * in the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } + + /** + * This method is called from insertUpdate and removeUpdate. + * If the number of lines in the document has changed, just repaint + * the whole thing (note, could improve performance by not repainting + * anything above the changes). If the number of lines hasn't changed, + * just repaint the given Rectangle. + * @param a the Rectangle to repaint if the number of lines hasn't changed + */ + void updateDamage (Rectangle a) + { + int newNumLines = determineNumLines(); + if (numLines != newNumLines) + { + numLines = newNumLines; + getContainer().repaint(); + } + else + getContainer().repaint(a.x, a.y, a.width, a.height); + } + + /** + * This method is called when something is inserted into the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage((Rectangle)a); + } + + /** + * This method is called when something is removed from the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void removeUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage((Rectangle)a); + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java new file mode 100644 index 00000000000..029ad26f8a9 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/CSS.java @@ -0,0 +1,461 @@ +/* CSS.java -- Provides CSS attributes + Copyright (C) 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 javax.swing.text.html; + +import java.util.HashMap; + +/** + * Provides CSS attributes to be used by the HTML view classes. The constants + * defined here are used as keys for text attributes for use in + * {@link javax.swing.text.AttributeSet}s of {@link javax.swing.text.Element}s. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class CSS +{ + /** + * Returns an array of all CSS attributes. + * + * @return All available CSS.Attribute objects. + */ + public static CSS.Attribute[] getAllAttributeKeys() + { + Object[] src = Attribute.attributeMap.values().toArray(); + CSS.Attribute[] dst = new CSS.Attribute[ src.length ]; + System.arraycopy(src, 0, dst, 0, src.length); + return dst; + } + + /** + * Returns an a given CSS attribute. + * + * @param name - The name of the attribute. + * @return The CSS attribute with the given name, or <code>null</code> if + * no attribute with that name exists. + */ + public static CSS.Attribute getAttribute(String name) + { + return (CSS.Attribute)Attribute.attributeMap.get( name ); + } + + public static final class Attribute + { + /** + * The CSS attribute 'background'. + */ + public static final Attribute BACKGROUND = + new Attribute("background", false, null); + + /** + * The CSS attribute 'background-attachment'. + */ + public static final Attribute BACKGROUND_ATTACHMENT = + new Attribute("background-attachment", false, "scroll"); + + /** + * The CSS attribute 'background-color'. + */ + public static final Attribute BACKGROUND_COLOR = + new Attribute("background-color", false, "transparent"); + + /** + * The CSS attribute 'background-image'. + */ + public static final Attribute BACKGROUND_IMAGE = + new Attribute("background-image", false, "none"); + + /** + * The CSS attribute 'background-position'. + */ + public static final Attribute BACKGROUND_POSITION = + new Attribute("background-position", false, null); + + /** + * The CSS attribute 'background-repeat'. + */ + public static final Attribute BACKGROUND_REPEAT = + new Attribute("background-repeat", false, "repeat"); + + /** + * The CSS attribute 'border'. + */ + public static final Attribute BORDER = new Attribute("border", false, null); + + /** + * The CSS attribute 'border-bottom'. + */ + public static final Attribute BORDER_BOTTOM = + new Attribute("border-bottom", false, null); + + /** + * The CSS attribute 'border-bottom-width'. + */ + public static final Attribute BORDER_BOTTOM_WIDTH = + new Attribute("border-bottom-width", false, "medium"); + + /** + * The CSS attribute 'border-color'. + */ + public static final Attribute BORDER_COLOR = + new Attribute("border-color", false, "black"); + + /** + * The CSS attribute 'border-left'. + */ + public static final Attribute BORDER_LEFT = + new Attribute("border-left", false, null); + + /** + * The CSS attribute 'border-left-width'. + */ + public static final Attribute BORDER_LEFT_WIDTH = + new Attribute("border-left-width", false, "medium"); + + /** + * The CSS attribute 'border-right'. + */ + public static final Attribute BORDER_RIGHT = + new Attribute("border-right", false, null); + + /** + * The CSS attribute 'border-right-width'. + */ + public static final Attribute BORDER_RIGHT_WIDTH = + new Attribute("border-right-width", false, "medium"); + + /** + * The CSS attribute 'border-style'. + */ + public static final Attribute BORDER_STYLE = + new Attribute("border-style", false, "none"); + + /** + * The CSS attribute 'border-top'. + */ + public static final Attribute BORDER_TOP = + new Attribute("border-top", false, null); + + /** + * The CSS attribute 'border-top-width'. + */ + public static final Attribute BORDER_TOP_WIDTH = + new Attribute("border-top-width", false, "medium"); + + /** + * The CSS attribute 'border-width'. + */ + public static final Attribute BORDER_WIDTH = + new Attribute("border-width", false, "medium"); + + /** + * The CSS attribute 'clear'. + */ + public static final Attribute CLEAR = new Attribute("clear", false, "none"); + + /** + * The CSS attribute 'color'. + */ + public static final Attribute COLOR = new Attribute("color", true, "black"); + + /** + * The CSS attribute 'display'. + */ + public static final Attribute DISPLAY = + new Attribute("display", false, "block"); + + /** + * The CSS attribute 'float'. + */ + public static final Attribute FLOAT = new Attribute("float", false, "none"); + + /** + * The CSS attribute 'font'. + */ + public static final Attribute FONT = new Attribute("font", true, null); + + /** + * The CSS attribute 'font-family'. + */ + public static final Attribute FONT_FAMILY = + new Attribute("font-family", true, null); + + /** + * The CSS attribute 'font-size'. + */ + public static final Attribute FONT_SIZE = + new Attribute("font-size", true, "medium"); + + /** + * The CSS attribute 'font-style'. + */ + public static final Attribute FONT_STYLE = + new Attribute("font-style", true, "normal"); + + /** + * The CSS attribute 'font-variant'. + */ + public static final Attribute FONT_VARIANT = + new Attribute("font-variant", true, "normal"); + + /** + * The CSS attribute 'font-weight'. + */ + public static final Attribute FONT_WEIGHT = + new Attribute("font-weight", true, "normal"); + + /** + * The CSS attribute 'height'. + */ + public static final Attribute HEIGHT = + new Attribute("height", false, "auto"); + + /** + * The CSS attribute 'letter-spacing'. + */ + public static final Attribute LETTER_SPACING = + new Attribute("letter-spacing", true, "normal"); + + /** + * The CSS attribute 'line-height'. + */ + public static final Attribute LINE_HEIGHT = + new Attribute("line-height", true, "normal"); + + /** + * The CSS attribute 'list-style'. + */ + public static final Attribute LIST_STYLE = + new Attribute("list-style", true, null); + + /** + * The CSS attribute 'list-style-image'. + */ + public static final Attribute LIST_STYLE_IMAGE = + new Attribute("list-style-image", true, "none"); + + /** + * The CSS attribute 'list-style-position'. + */ + public static final Attribute LIST_STYLE_POSITION = + new Attribute("list-style-position", true, "outside"); + + /** + * The CSS attribute 'list-style-type'. + */ + public static final Attribute LIST_STYLE_TYPE = + new Attribute("list-style-type", true, "disc"); + + /** + * The CSS attribute 'margin'. + */ + public static final Attribute MARGIN = new Attribute("margin", false, null); + + /** + * The CSS attribute 'margin-bottom'. + */ + public static final Attribute MARGIN_BOTTOM = + new Attribute("margin-bottom", false, "0"); + + /** + * The CSS attribute 'margin-left'. + */ + public static final Attribute MARGIN_LEFT = + new Attribute("margin-left", false, "0"); + + /** + * The CSS attribute 'margin-right'. + */ + public static final Attribute MARGIN_RIGHT = + new Attribute("margin-right", false, "0"); + + /** + * The CSS attribute 'margin-top'. + */ + public static final Attribute MARGIN_TOP = + new Attribute("margin-top", false, "0"); + + /** + * The CSS attribute 'padding'. + */ + public static final Attribute PADDING = + new Attribute("padding", false, null); + + /** + * The CSS attribute 'padding-bottom'. + */ + public static final Attribute PADDING_BOTTOM = + new Attribute("padding-bottom", false, "0"); + + /** + * The CSS attribute 'padding-left'. + */ + public static final Attribute PADDING_LEFT = + new Attribute("padding-left", false, "0"); + + /** + * The CSS attribute 'padding-right'. + */ + public static final Attribute PADDING_RIGHT = + new Attribute("padding-right", false, "0"); + + /** + * The CSS attribute 'padding-top'. + */ + public static final Attribute PADDING_TOP = + new Attribute("padding-top", false, "0"); + + /** + * The CSS attribute 'text-align'. + */ + public static final Attribute TEXT_ALIGN = + new Attribute("text-align", true, null); + + /** + * The CSS attribute 'text-decoration'. + */ + public static final Attribute TEXT_DECORATION = + new Attribute("text-decoration", true, "none"); + + /** + * The CSS attribute 'text-indent'. + */ + public static final Attribute TEXT_INDENT = + new Attribute("text-indent", true, "0"); + + /** + * The CSS attribute 'text-transform'. + */ + public static final Attribute TEXT_TRANSFORM = + new Attribute("text-transform", true, "none"); + + /** + * The CSS attribute 'vertical-align'. + */ + public static final Attribute VERTICAL_ALIGN = + new Attribute("vertical-align", false, "baseline"); + + /** + * The CSS attribute 'white-space'. + */ + public static final Attribute WHITE_SPACE = + new Attribute("white-space", true, "normal"); + + /** + * The CSS attribute 'width'. + */ + public static final Attribute WIDTH = + new Attribute("width", false, "auto"); + + /** + * The CSS attribute 'word-spacing'. + */ + public static final Attribute WORD_SPACING = + new Attribute("word-spacing", true, "normal"); + + /** + * The attribute string. + */ + String attStr; + + /** + * Indicates if this attribute should be inherited from it's parent or + * not. + */ + boolean isInherited; + + /** + * A default value for this attribute if one exists, otherwise null. + */ + String defaultValue; + + /** + * A HashMap of all attributes. + */ + static HashMap attributeMap; + + /** + * Creates a new Attribute instance with the specified values. + * + * @param attr the attribute string + * @param inherited if the attribute should be inherited or not + * @param def a default value; may be <code>null</code> + */ + Attribute(String attr, boolean inherited, String def) + { + attStr = attr; + isInherited = inherited; + defaultValue = def; + if( attributeMap == null) + attributeMap = new HashMap(); + attributeMap.put( attr, this ); + } + + /** + * Returns the string representation of this attribute as specified + * in the CSS specification. + */ + public String toString() + { + return attStr; + } + + /** + * Returns <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise. + * + * @return <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise + */ + public boolean isInherited() + { + return isInherited; + } + + /** + * Returns the default value of this attribute if one exists, + * <code>null</code> otherwise. + * + * @return the default value of this attribute if one exists, + * <code>null</code> otherwise + */ + public String getDefaultValue() + { + return defaultValue; + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java index 3c03a63a471..0b758d2b873 100644 --- a/libjava/classpath/javax/swing/text/html/HTML.java +++ b/libjava/classpath/javax/swing/text/html/HTML.java @@ -945,22 +945,22 @@ public class HTML * This tag is not included into the array, returned by getAllTags(). * toString() returns 'comment'. HTML reader synthesizes this tag. */ - public static final Tag COMMENT = new Tag("comment", SYNTETIC); + public static final Tag COMMENT = new Tag("comment", SYNTHETIC); /** * All text content is labeled with this tag. * This tag is not included into the array, returned by getAllTags(). * toString() returns 'content'. HTML reader synthesizes this tag. */ - public static final Tag CONTENT = new Tag("content", SYNTETIC); + public static final Tag CONTENT = new Tag("content", SYNTHETIC); /** * All text content must be in a paragraph element. * If a paragraph didn't exist when content was encountered, * a paragraph is manufactured. - * toString() returns 'implied'. HTML reader synthesizes this tag. + * toString() returns 'p-implied'. HTML reader synthesizes this tag. */ - public static final Tag IMPLIED = new Tag("implied", SYNTETIC); + public static final Tag IMPLIED = new Tag("p-implied", SYNTHETIC); final String name; final int flags; @@ -1144,7 +1144,7 @@ public class HTML */ boolean isSyntetic() { - return (flags & SYNTETIC) != 0; + return (flags & SYNTHETIC) != 0; } private static void unexpected(Exception ex) @@ -1185,7 +1185,7 @@ public class HTML static final int BREAKS = 1; static final int BLOCK = 2; static final int PREFORMATTED = 4; - static final int SYNTETIC = 8; + static final int SYNTHETIC = 8; private static Map tagMap; private static Map attrMap; @@ -1196,6 +1196,7 @@ public class HTML */ public HTML() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index a95e496ec4d..d048a04e614 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -38,7 +38,14 @@ exception statement from your version. */ package javax.swing.text.html; +import java.net.URL; + +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Element; +import javax.swing.text.ElementIterator; +import javax.swing.text.html.HTML.Tag; /** * TODO: This class is not yet completetely implemented. @@ -47,7 +54,215 @@ import javax.swing.text.DefaultStyledDocument; */ public class HTMLDocument extends DefaultStyledDocument { + /** A key for document properies. The value for the key is + * a Vector of Strings of comments not found in the body. + */ + public static final String AdditionalComments = "AdditionalComments"; + URL baseURL = null; + boolean preservesUnknownTags = true; + + /** + * Returns the location against which to resolve relative URLs. + * This is the document's URL if the document was loaded from a URL. + * If a <code>base</code> tag is found, it will be used. + * @return the base URL + */ + public URL getBase() + { + return baseURL; + } + + /** + * Sets the location against which to resolve relative URLs. + * @param u the new base URL + */ + public void setBase(URL u) + { + baseURL = u; + //TODO: also set the base of the StyleSheet + } + + /** + * Returns whether or not the parser preserves unknown HTML tags. + * @return true if the parser preserves unknown tags + */ + public boolean getPreservesUnknownTags() + { + return preservesUnknownTags; + } + + /** + * Sets the behaviour of the parser when it encounters unknown HTML tags. + * @param preservesTags true if the parser should preserve unknown tags. + */ + public void setPreservesUnknownTags(boolean preservesTags) + { + preservesUnknownTags = preservesTags; + } + + /** + * An iterator to iterate through LeafElements in the document. + */ + class LeafIterator extends Iterator + { + HTML.Tag tag; + HTMLDocument doc; + ElementIterator it; + + public LeafIterator (HTML.Tag t, HTMLDocument d) + { + doc = d; + tag = t; + it = new ElementIterator(doc); + } + + /** + * Return the attributes for the tag associated with this iteartor + * @return the AttributeSet + */ + public AttributeSet getAttributes() + { + if (it.current() != null) + return it.current().getAttributes(); + return null; + } + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public int getEndOffset() + { + if (it.current() != null) + return it.current().getEndOffset(); + return -1; + } + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + + public int getStartOffset() + { + if (it.current() != null) + return it.current().getStartOffset(); + return -1; + } + + /** + * Advance the iterator to the next LeafElement . + */ + public void next() + { + it.next(); + while (it.current()!= null && !it.current().isLeaf()) + it.next(); + } + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public boolean isValid() + { + return it.current() != null; + } + + /** + * Type of tag for this iterator. + */ + public Tag getTag() + { + return tag; + } + + } + public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) { + // TODO: Implement this properly. + } + + /** + * Gets an iterator for the given HTML.Tag. + * @param t the requested HTML.Tag + * @return the Iterator + */ + public HTMLDocument.Iterator getIterator (HTML.Tag t) + { + return new HTMLDocument.LeafIterator(t, this); + } + + /** + * An iterator over a particular type of tag. + */ + public abstract static class Iterator + { + /** + * Return the attribute set for this tag. + * @return the <code>AttributeSet</code> (null if none found). + */ + public abstract AttributeSet getAttributes(); + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public abstract int getEndOffset(); + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + public abstract int getStartOffset(); + + /** + * Move the iterator forward. + */ + public abstract void next(); + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public abstract boolean isValid(); + + /** + * Type of tag this iterator represents. + * @return the tag. + */ + public abstract HTML.Tag getTag(); + } + + public class BlockElement extends AbstractDocument.BranchElement + { + public BlockElement (Element parent, AttributeSet a) + { + super (parent, a); + } + + /** + * Gets the resolving parent. Since HTML attributes are not + * inherited at the model level, this returns null. + */ + public AttributeSet getResolveParent() + { + return null; + } + + public String getName() + { + //FIXME: this is supposed to do something different from the super class + return super.getName(); + } } } diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index c0182fe6ac9..5189c777539 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -43,8 +43,10 @@ import java.io.Reader; import java.io.Serializable; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; import javax.swing.text.MutableAttributeSet; import javax.swing.text.StyledEditorKit; +import javax.swing.text.html.parser.ParserDelegator; /** * This class is NOT implemented. This file currently holds only @@ -96,9 +98,9 @@ public class HTMLEditorKit /** * The parser calls this method after it finishes parsing the document. */ - public void flush() - throws BadLocationException + public void flush() throws BadLocationException { + // TODO: What to do here, if anything? } /** @@ -108,6 +110,7 @@ public class HTMLEditorKit */ public void handleComment(char[] comment, int position) { + // TODO: What to do here, if anything? } /** @@ -118,6 +121,7 @@ public class HTMLEditorKit */ public void handleEndOfLineString(String end_of_line) { + // TODO: What to do here, if anything? } /** @@ -129,6 +133,7 @@ public class HTMLEditorKit */ public void handleEndTag(HTML.Tag tag, int position) { + // TODO: What to do here, if anything? } /** @@ -139,6 +144,7 @@ public class HTMLEditorKit */ public void handleError(String message, int position) { + // TODO: What to do here, if anything? } /** @@ -149,9 +155,9 @@ public class HTMLEditorKit * @param position The tag position in the text being parsed. */ public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, - int position - ) + int position) { + // TODO: What to do here, if anything? } /** @@ -165,6 +171,7 @@ public class HTMLEditorKit int position ) { + // TODO: What to do here, if anything? } /** @@ -174,6 +181,7 @@ public class HTMLEditorKit */ public void handleText(char[] text, int position) { + // TODO: What to do here, if anything? } } @@ -247,4 +255,26 @@ public class HTMLEditorKit * The "ident paragraph right" action. */ public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; + + /** + * Create a text storage model for this type of editor. + * + * @return the model + */ + public Document createDefaultDocument() + { + HTMLDocument document = new HTMLDocument(); + return document; + } + + /** + * Get the parser that this editor kit uses for reading HTML streams. This + * method can be overridden to use the alternative parser. + * + * @return the HTML parser (by default, {@link ParserDelegator}). + */ + protected Parser getParser() + { + return new ParserDelegator(); + } }
\ No newline at end of file diff --git a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java index dc0ab10a8f7..e146965d778 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java +++ b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java @@ -41,7 +41,6 @@ package javax.swing.text.html; import java.net.URL; import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.text.Element; /** @@ -50,8 +49,7 @@ import javax.swing.text.Element; * * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) */ -public class HTMLFrameHyperlinkEvent - extends HyperlinkEvent +public class HTMLFrameHyperlinkEvent extends HyperlinkEvent { private final String target_frame; diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java index deb7b1602bb..70e9c2acbff 100644 --- a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java +++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java @@ -95,9 +95,12 @@ public final class ContentModel */ public int type; - /** Create a content model initializing all fields to default values. */ + /** + * Create a content model initializing all fields to default values. + */ public ContentModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java index f17ca011ea0..16bc5b0d6af 100644 --- a/libjava/classpath/javax/swing/text/html/parser/DTD.java +++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java @@ -81,8 +81,9 @@ public class DTD { /** * The version of the persistent data format. + * @specnote This was made <code>final</code> in 1.5. */ - public static int FILE_VERSION = 1; + public static final int FILE_VERSION = 1; /** * The table of existing available DTDs. @@ -590,8 +591,7 @@ public class DTD * @param name the name of the entity * @param type the type of the entity, a bitwise combination * of GENERAL, PARAMETER, SYSTEM and PUBLIC. - * @throws an error if the parameter is both GENERAL and PARAMETER - * of both PUBLIC and SYSTEM. + * * @return the created entity */ private Entity newEntity(String name, int type) diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java index 164297f1882..062606d17ba 100644 --- a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java +++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java @@ -168,6 +168,7 @@ public class DocumentParser * specific packages, write your own DTD or obtain the working instance * of parser in other way, for example, by calling * {@link javax.swing.text.html.HTMLEditorKit#getParser()}. + * * @param a_dtd a DTD to use. */ public DocumentParser(DTD a_dtd) @@ -212,6 +213,7 @@ public class DocumentParser */ protected void handleComment(char[] comment) { + // This default implementation does nothing. } /** @@ -224,6 +226,7 @@ public class DocumentParser protected void handleEmptyTag(TagElement tag) throws javax.swing.text.ChangedCharSetException { + // This default implementation does nothing. } /** @@ -234,11 +237,13 @@ public class DocumentParser */ protected void handleEndTag(TagElement tag) { + // This default implementation does nothing. } /* Handle error that has occured in the given line. */ protected void handleError(int line, String message) { + // This default implementation does nothing. } /** @@ -249,6 +254,7 @@ public class DocumentParser */ protected void handleStartTag(TagElement tag) { + // This default implementation does nothing. } /** @@ -257,5 +263,6 @@ public class DocumentParser */ protected void handleText(char[] text) { + // This default implementation does nothing. } } diff --git a/libjava/classpath/javax/swing/text/html/parser/Element.java b/libjava/classpath/javax/swing/text/html/parser/Element.java index 098983c6923..c07c07f5426 100644 --- a/libjava/classpath/javax/swing/text/html/parser/Element.java +++ b/libjava/classpath/javax/swing/text/html/parser/Element.java @@ -148,10 +148,10 @@ public final class Element /** * The default constructor must have package level access in this * class. Use DTD.defineElement(..) to create an element when required. - * @todo MAKE THIS PACKAGE in the final version. Now the Parser needs it! */ Element() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/parser/Parser.java b/libjava/classpath/javax/swing/text/html/parser/Parser.java index 7ff6853da82..a88e9ce1953 100644 --- a/libjava/classpath/javax/swing/text/html/parser/Parser.java +++ b/libjava/classpath/javax/swing/text/html/parser/Parser.java @@ -256,6 +256,7 @@ public class Parser */ protected void endTag(boolean omitted) { + // This default implementation does nothing. } /** @@ -310,6 +311,7 @@ public class Parser */ protected void handleComment(char[] comment) { + // This default implementation does nothing. } /** @@ -333,6 +335,7 @@ public class Parser protected void handleEmptyTag(TagElement tag) throws ChangedCharSetException { + // This default implementation does nothing. } /** @@ -343,11 +346,13 @@ public class Parser */ protected void handleEndTag(TagElement tag) { + // This default implementation does nothing. } /* Handle error that has occured in the given line. */ protected void handleError(int line, String message) { + // This default implementation does nothing. } /** @@ -358,6 +363,7 @@ public class Parser */ protected void handleStartTag(TagElement tag) { + // This default implementation does nothing. } /** @@ -376,6 +382,7 @@ public class Parser */ protected void handleText(char[] text) { + // This default implementation does nothing. } /** @@ -387,6 +394,7 @@ public class Parser */ protected void handleTitle(char[] title) { + // This default implementation does nothing. } /** @@ -420,6 +428,7 @@ public class Parser */ protected void markFirstTime(Element element) { + // This default implementation does nothing. } /** @@ -432,5 +441,6 @@ public class Parser protected void startTag(TagElement tag) throws ChangedCharSetException { + // This default implementation does nothing. } } diff --git a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java index e709e2a0449..d9747729317 100644 --- a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java +++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java @@ -45,7 +45,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.NoSuchElementException; diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java index 7a44e738338..2891a778ee9 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java @@ -47,7 +47,6 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; -import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; @@ -56,12 +55,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.EventObject; -import javax.swing.CellRendererPane; import javax.swing.DefaultCellEditor; import javax.swing.Icon; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingUtilities; @@ -354,9 +349,9 @@ public class DefaultTreeCellEditor /** * Configures the editing component whenever it is null. * - * @param tree- the tree to configure to component for. - * @param renderer- the renderer used to set up the nodes - * @param editor- the editor used + * @param tree the tree to configure to component for. + * @param renderer the renderer used to set up the nodes + * @param editor the editor used */ private void configureEditingComponent(JTree tree, DefaultTreeCellRenderer renderer, @@ -513,6 +508,8 @@ public class DefaultTreeCellEditor // Cell may not be currently editable, but may need to start timer. if (shouldStartEditingTimer(event)) startEditingTimer(); + else if (timer.isRunning()) + timer.stop(); return false; } @@ -605,7 +602,7 @@ public class DefaultTreeCellEditor /** * Messaged when the timer fires, this will start the editing session. * - * @param @param e - the event that characterizes the action. + * @param e the event that characterizes the action. */ public void actionPerformed(ActionEvent e) { diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java index 4a353b30176..d1cb9c0e8b7 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java @@ -419,8 +419,7 @@ public class DefaultTreeCellRenderer super.setBackground(getBackgroundSelectionColor()); setForeground(getTextSelectionColor()); - if (tree.getLeadSelectionPath() == null || - (tree.getLeadSelectionPath().getLastPathComponent()).equals(val)) + if (hasFocus) setBorderSelectionColor(UIManager.getLookAndFeelDefaults(). getColor("Tree.selectionBorderColor")); else diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java index 5b5e0391478..5cf80986061 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java @@ -299,6 +299,7 @@ public class DefaultTreeModel public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { + newChild.setParent(parent); parent.insert(newChild, index); int[] childIndices = new int[1]; childIndices[0] = index; diff --git a/libjava/classpath/javax/swing/tree/TreeCellRenderer.java b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java index ebbe3fa9133..a1808c9ee91 100644 --- a/libjava/classpath/javax/swing/tree/TreeCellRenderer.java +++ b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java @@ -46,22 +46,24 @@ import javax.swing.JTree; * TreeCellRenderer public interface * @author Andrew Selkirk */ -public interface TreeCellRenderer { +public interface TreeCellRenderer +{ - /** - * getTreeCellRendererComponent - * @param tree TODO - * @param value TODO - * @param selected TODO - * @param expanded TODO - * @param leaf TODO - * @param row TODO - * @param us TODO - * @returns TODO - */ - Component getTreeCellRendererComponent(JTree tree, - Object value, boolean selected, boolean expanded, - boolean leaf, int row, boolean hasFocus); + /** + * getTreeCellRendererComponent + * @param tree TODO + * @param value TODO + * @param selected TODO + * @param expanded TODO + * @param leaf TODO + * @param row TODO + * @param hasFocus TODO + * @returns TODO + */ + Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, + boolean leaf, int row, + boolean hasFocus); -} // TreeCellRenderer +} diff --git a/libjava/classpath/javax/swing/tree/TreeModel.java b/libjava/classpath/javax/swing/tree/TreeModel.java index 759aaac588c..ec1884efdb7 100644 --- a/libjava/classpath/javax/swing/tree/TreeModel.java +++ b/libjava/classpath/javax/swing/tree/TreeModel.java @@ -44,66 +44,62 @@ import javax.swing.event.TreeModelListener; * TreeModel public interface * @author Andrew Selkirk */ -public interface TreeModel { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getRoot - * @returns Object - */ - Object getRoot(); - - /** - * getChild - * @param parent TODO - * @param index TODO - * @returns Object - */ - Object getChild(Object parent, int index); - - /** - * getChildCount - * @param parent TODO - * @returns int - */ - int getChildCount(Object parent); - - /** - * isLeaf - * @param node TODO - * @returns boolean - */ - boolean isLeaf(Object node); - - /** - * valueForPathChanged - * @param path TODO - * @param newvalue TODO - */ - void valueForPathChanged(TreePath path, Object newvalue); - - /** - * getIndexOfChild - * @param parent TODO - * @param ild TODO - * @returns int - */ - int getIndexOfChild(Object parent, Object child); - - /** - * addTreeModelListener - * @param listener TODO - */ - void addTreeModelListener(TreeModelListener listener); - - /** - * removeTreeModelListener - * @param listener TODO - */ - void removeTreeModelListener(TreeModelListener listener); - - -} // TreeModel +public interface TreeModel +{ + /** + * getRoot + * @returns Object + */ + Object getRoot(); + + /** + * getChild + * @param parent TODO + * @param index TODO + * @returns Object + */ + Object getChild(Object parent, int index); + + /** + * getChildCount + * @param parent TODO + * @returns int + */ + int getChildCount(Object parent); + + /** + * isLeaf + * @param node TODO + * @returns boolean + */ + boolean isLeaf(Object node); + + /** + * valueForPathChanged + * @param path TODO + * @param newvalue TODO + */ + void valueForPathChanged(TreePath path, Object newvalue); + + /** + * getIndexOfChild + * @param parent TODO + * @param child TODO + * @returns int + */ + int getIndexOfChild(Object parent, Object child); + + /** + * addTreeModelListener + * @param listener TODO + */ + void addTreeModelListener(TreeModelListener listener); + + /** + * removeTreeModelListener + * @param listener TODO + */ + void removeTreeModelListener(TreeModelListener listener); + + +} diff --git a/libjava/classpath/javax/swing/undo/CannotRedoException.java b/libjava/classpath/javax/swing/undo/CannotRedoException.java index 7d70a38c2c8..5f22648350a 100644 --- a/libjava/classpath/javax/swing/undo/CannotRedoException.java +++ b/libjava/classpath/javax/swing/undo/CannotRedoException.java @@ -44,13 +44,13 @@ package javax.swing.undo; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class CannotRedoException - extends RuntimeException +public class CannotRedoException extends RuntimeException { /** * Constructs a new instance of a <code>CannotRedoException</code>. */ public CannotRedoException() { + super(); } } diff --git a/libjava/classpath/javax/swing/undo/CannotUndoException.java b/libjava/classpath/javax/swing/undo/CannotUndoException.java index 9fc0ec3bd3a..8d08cda6853 100644 --- a/libjava/classpath/javax/swing/undo/CannotUndoException.java +++ b/libjava/classpath/javax/swing/undo/CannotUndoException.java @@ -53,5 +53,6 @@ public class CannotUndoException */ public CannotUndoException() { + super(); } } diff --git a/libjava/classpath/javax/xml/namespace/QName.java b/libjava/classpath/javax/xml/namespace/QName.java index 7b8b194c33c..19700b32e41 100644 --- a/libjava/classpath/javax/xml/namespace/QName.java +++ b/libjava/classpath/javax/xml/namespace/QName.java @@ -1,5 +1,5 @@ /* QName.java - An XML qualified name. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.xml.namespace; +import java.io.Serializable; + import javax.xml.XMLConstants; /** @@ -47,14 +49,15 @@ import javax.xml.XMLConstants; * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> * @since 1.3 */ -public class QName +public class QName implements Serializable { + private static final long serialVersionUID = 4418622981026545151L; private final String namespaceURI; private final String localPart; private final String prefix; - private final String qName; - int hashCode = -1; + private transient String qName; + transient int hashCode = -1; public QName(String namespaceURI, String localPart) { @@ -78,21 +81,6 @@ public class QName this.namespaceURI = namespaceURI; this.localPart = localPart; this.prefix = prefix; - - StringBuffer buf = new StringBuffer(); - if (namespaceURI.length() > 0) - { - buf.append('{'); - buf.append(namespaceURI); - buf.append('}'); - } - if (prefix.length() > 0) - { - buf.append(prefix); - buf.append(':'); - } - buf.append(localPart); - qName = buf.toString(); } public QName(String localPart) @@ -115,7 +103,7 @@ public class QName return prefix; } - public boolean equals(Object obj) + public final boolean equals(Object obj) { if (obj instanceof QName) { @@ -129,19 +117,29 @@ public class QName public final int hashCode() { if (hashCode == -1) - { - StringBuffer buf = new StringBuffer(); - buf.append('{'); - buf.append(namespaceURI); - buf.append('}'); - buf.append(localPart); - hashCode = buf.toString().hashCode(); - } + hashCode = localPart.hashCode() ^ namespaceURI.hashCode(); return hashCode; } - public String toString() + public synchronized String toString() { + if (qName == null) + { + StringBuffer buf = new StringBuffer(); + if (namespaceURI.length() > 0) + { + buf.append('{'); + buf.append(namespaceURI); + buf.append('}'); + } + if (prefix.length() > 0) + { + buf.append(prefix); + buf.append(':'); + } + buf.append(localPart); + qName = buf.toString(); + } return qName; } diff --git a/libjava/classpath/javax/xml/transform/TransformerConfigurationException.java b/libjava/classpath/javax/xml/transform/TransformerConfigurationException.java index b2153c2cbf9..81db3bed913 100644 --- a/libjava/classpath/javax/xml/transform/TransformerConfigurationException.java +++ b/libjava/classpath/javax/xml/transform/TransformerConfigurationException.java @@ -45,9 +45,8 @@ package javax.xml.transform; public class TransformerConfigurationException extends TransformerException { + private static final long serialVersionUID = 1285547467942875745L; - private SourceLocator locator; - /** * Constructor with no detail message. */ @@ -96,8 +95,7 @@ public class TransformerConfigurationException SourceLocator locator, Throwable e) { - super(message, e); - this.locator = locator; + super(message, locator, e); } } diff --git a/libjava/classpath/javax/xml/transform/TransformerException.java b/libjava/classpath/javax/xml/transform/TransformerException.java index a72ee1c2f56..3d97eda1bbb 100644 --- a/libjava/classpath/javax/xml/transform/TransformerException.java +++ b/libjava/classpath/javax/xml/transform/TransformerException.java @@ -47,9 +47,11 @@ import java.io.PrintWriter; public class TransformerException extends Exception { + private static final long serialVersionUID = 975798773772956428L; + // Field names fixed by serialization spec. private SourceLocator locator; - private Throwable cause; + private Throwable containedException; /** * Constructor with a detail message. @@ -94,7 +96,7 @@ public class TransformerException if (cause != null) { initCause(cause); - this.cause = cause; + this.containedException = cause; } } @@ -119,7 +121,7 @@ public class TransformerException */ public Throwable getException() { - return cause; + return containedException; } /** @@ -127,7 +129,7 @@ public class TransformerException */ public Throwable getCause() { - return cause; + return containedException; } /** @@ -143,7 +145,7 @@ public class TransformerException */ public Throwable initCause(Throwable cause) { - if (this.cause != null) + if (this.containedException != null) { throw new IllegalStateException(); } @@ -151,7 +153,7 @@ public class TransformerException { throw new IllegalArgumentException(); } - this.cause = cause; + this.containedException = cause; return this; } @@ -221,20 +223,20 @@ public class TransformerException public void printStackTrace(PrintStream s) { super.printStackTrace(s); - if (cause != null) + if (containedException != null) { s.print("caused by "); - cause.printStackTrace(s); + containedException.printStackTrace(s); } } public void printStackTrace(PrintWriter s) { super.printStackTrace(s); - if (cause != null) + if (containedException != null) { s.print("caused by "); - cause.printStackTrace(s); + containedException.printStackTrace(s); } } diff --git a/libjava/classpath/javax/xml/transform/TransformerFactoryConfigurationError.java b/libjava/classpath/javax/xml/transform/TransformerFactoryConfigurationError.java index 9b16b2b2e4c..82afeeac750 100644 --- a/libjava/classpath/javax/xml/transform/TransformerFactoryConfigurationError.java +++ b/libjava/classpath/javax/xml/transform/TransformerFactoryConfigurationError.java @@ -44,7 +44,9 @@ package javax.xml.transform; public class TransformerFactoryConfigurationError extends Error { + private static final long serialVersionUID = -6527718720676281516L; + // Name is fixed by the serialization spec. private final Exception exception; /** diff --git a/libjava/classpath/javax/xml/xpath/XPathException.java b/libjava/classpath/javax/xml/xpath/XPathException.java index 030d0a9c1d7..cf004c1791e 100644 --- a/libjava/classpath/javax/xml/xpath/XPathException.java +++ b/libjava/classpath/javax/xml/xpath/XPathException.java @@ -49,7 +49,9 @@ import java.io.PrintWriter; public class XPathException extends Exception { + private static final long serialVersionUID = -1837080260374986980L; + // Name is fixed by serialization spec. Throwable cause; public XPathException(String message) diff --git a/libjava/classpath/javax/xml/xpath/XPathExpressionException.java b/libjava/classpath/javax/xml/xpath/XPathExpressionException.java index 91716f1502f..6257adb474b 100644 --- a/libjava/classpath/javax/xml/xpath/XPathExpressionException.java +++ b/libjava/classpath/javax/xml/xpath/XPathExpressionException.java @@ -46,6 +46,7 @@ package javax.xml.xpath; public class XPathExpressionException extends XPathException { + private static final long serialVersionUID = -1837080260374986980L; public XPathExpressionException(String message) { diff --git a/libjava/classpath/javax/xml/xpath/XPathFactoryConfigurationException.java b/libjava/classpath/javax/xml/xpath/XPathFactoryConfigurationException.java index a89646336d8..0fc68a7634b 100644 --- a/libjava/classpath/javax/xml/xpath/XPathFactoryConfigurationException.java +++ b/libjava/classpath/javax/xml/xpath/XPathFactoryConfigurationException.java @@ -46,6 +46,7 @@ package javax.xml.xpath; public class XPathFactoryConfigurationException extends XPathException { + private static final long serialVersionUID = -1837080260374986980L; public XPathFactoryConfigurationException(String message) { diff --git a/libjava/classpath/javax/xml/xpath/XPathFunctionException.java b/libjava/classpath/javax/xml/xpath/XPathFunctionException.java index ebc8ce7d341..db680ae65da 100644 --- a/libjava/classpath/javax/xml/xpath/XPathFunctionException.java +++ b/libjava/classpath/javax/xml/xpath/XPathFunctionException.java @@ -46,6 +46,7 @@ package javax.xml.xpath; public class XPathFunctionException extends XPathExpressionException { + private static final long serialVersionUID = -1837080260374986980L; public XPathFunctionException(String message) { |