From 9dd02f103042cb8a196f8a3ed2278da443e345bf Mon Sep 17 00:00:00 2001 From: neothemachine Date: Wed, 5 Dec 2012 17:03:16 +0100 Subject: move all files from trunk to root folder --- .../com/ardor3d/image/util/AWTImageLoader.java | 267 +++++++++++++++++ .../java/com/ardor3d/image/util/AWTImageUtil.java | 132 +++++++++ .../com/ardor3d/image/util/AWTTextureUtil.java | 43 +++ .../java/com/ardor3d/image/util/AwtColorUtil.java | 38 +++ .../image/util/ScreenShotImageExporter.java | 156 ++++++++++ .../com/ardor3d/input/awt/AwtFocusWrapper.java | 50 ++++ .../main/java/com/ardor3d/input/awt/AwtKey.java | 154 ++++++++++ .../com/ardor3d/input/awt/AwtKeyboardWrapper.java | 126 ++++++++ .../com/ardor3d/input/awt/AwtMouseManager.java | 167 +++++++++++ .../com/ardor3d/input/awt/AwtMouseWrapper.java | 326 +++++++++++++++++++++ 10 files changed, 1459 insertions(+) create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java create mode 100644 ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java (limited to 'ardor3d-awt/src/main') diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java new file mode 100644 index 0000000..86a93d3 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.image.util; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; + +import com.ardor3d.image.Image; +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.image.PixelDataType; +import com.ardor3d.renderer.state.TextureState; +import com.ardor3d.util.geom.BufferUtils; +import com.google.common.collect.Lists; + +/** + * Image loader that makes use of AWT's ImageIO to load image file data. + */ +public class AWTImageLoader implements ImageLoader { + private static final Logger logger = Logger.getLogger(AWTImageLoader.class.getName()); + + private static boolean createOnHeap = false; + + private static String[] supportedFormats; + + public static String[] getSupportedFormats() { + return supportedFormats; + } + + public static void registerLoader() { + if (supportedFormats == null) { + final List formats = Lists.newArrayList(); + for (String format : ImageIO.getReaderFormatNames()) { + format = "." + format.toUpperCase(); + if (!formats.contains(format)) { + formats.add(format); + } + } + supportedFormats = formats.toArray(new String[formats.size()]); + } + ImageLoaderUtil.registerHandler(new AWTImageLoader(), supportedFormats); + } + + public Image load(final InputStream is, final boolean flipImage) throws IOException { + final BufferedImage image = ImageIO.read(is); + if (image == null) { + return null; + } + + return makeArdor3dImage(image, flipImage); + } + + public static Image makeArdor3dImage(final BufferedImage image, final boolean flipImage) { + if (image == null) { + return null; + } + + final boolean hasAlpha = image.getColorModel().hasAlpha(); + final boolean grayscale = image.getColorModel().getNumComponents() == 1; + BufferedImage tex; + + if (flipImage + || ((image).getType() != BufferedImage.TYPE_BYTE_GRAY && (hasAlpha ? (image).getType() != BufferedImage.TYPE_4BYTE_ABGR + : (image).getType() != BufferedImage.TYPE_3BYTE_BGR))) { + // Obtain the image data. + try { + tex = new BufferedImage(image.getWidth(null), image.getHeight(null), + grayscale ? BufferedImage.TYPE_BYTE_GRAY : hasAlpha ? BufferedImage.TYPE_4BYTE_ABGR + : BufferedImage.TYPE_3BYTE_BGR); + } catch (final IllegalArgumentException e) { + logger.warning("Problem creating buffered Image: " + e.getMessage()); + return TextureState.getDefaultTextureImage(); + } + + final int imageWidth = image.getWidth(null); + final int imageHeight = image.getHeight(null); + final int[] tmpData = new int[imageWidth]; + int row = 0; + for (int y = imageHeight - 1; y >= 0; y--) { + image.getRGB(0, (flipImage ? row++ : y), imageWidth, 1, tmpData, 0, imageWidth); + tex.setRGB(0, y, imageWidth, 1, tmpData, 0, imageWidth); + } + + } else { + tex = image; + } + + // Get a pointer to the image memory + final byte data[] = asByteArray(tex); + final ByteBuffer scratch = createOnHeap ? BufferUtils.createByteBufferOnHeap(data.length) : BufferUtils + .createByteBuffer(data.length); + scratch.clear(); + scratch.put(data); + scratch.flip(); + final Image ardorImage = new Image(); + ardorImage.setDataFormat(grayscale ? ImageDataFormat.Luminance : hasAlpha ? ImageDataFormat.RGBA + : ImageDataFormat.RGB); + ardorImage.setDataType(PixelDataType.UnsignedByte); + ardorImage.setWidth(tex.getWidth()); + ardorImage.setHeight(tex.getHeight()); + ardorImage.setData(scratch); + return ardorImage; + } + + public static Image makeArdor3dImage(final RenderableImage image, final boolean flipImage) { + return makeArdor3dImage(image.createDefaultRendering(), flipImage); + } + + public static Image makeArdor3dImage(final RenderedImage image, final boolean flipImage) { + if (image == null) { + return null; + } + + final ColorModel colorModel = image.getColorModel(); + final boolean hasAlpha = colorModel.hasAlpha(); + final boolean grayscale = colorModel.getNumComponents() == 1; + + // Get a pointer to the image memory + final byte data[] = asByteArray(image, grayscale, hasAlpha); + final ByteBuffer scratch = createOnHeap ? BufferUtils.createByteBufferOnHeap(data.length) : BufferUtils + .createByteBuffer(data.length); + scratch.clear(); + scratch.put(data); + scratch.flip(); + final Image ardorImage = new Image(); + ardorImage.setDataFormat(grayscale ? ImageDataFormat.Luminance : hasAlpha ? ImageDataFormat.RGBA + : ImageDataFormat.RGB); + ardorImage.setDataType(PixelDataType.UnsignedByte); + ardorImage.setWidth(image.getWidth()); + ardorImage.setHeight(image.getHeight()); + ardorImage.setData(scratch); + return ardorImage; + } + + public static byte[] asByteArray(final BufferedImage image) { + final int imageWidth = image.getWidth(null); + final int imageHeight = image.getHeight(null); + final boolean hasAlpha = image.getColorModel().hasAlpha(); + final boolean grayscale = image.getColorModel().getNumComponents() == 1; + + if (image.getRaster().getTransferType() == DataBuffer.TYPE_BYTE) { + return (byte[]) image.getRaster().getDataElements(0, 0, imageWidth, imageHeight, null); + } + + final byte[] rVal = new byte[imageWidth * imageHeight * (grayscale ? 1 : (hasAlpha ? 4 : 3))]; + final int[] tmpData = new int[imageWidth]; + int index = 0; + for (int y = 0; y < imageHeight; y++) { + image.getRGB(0, y, imageWidth, 1, tmpData, 0, imageWidth); + for (int i = 0; i < imageWidth; i++) { + final int argb = tmpData[i]; + if (grayscale) { + rVal[index++] = (byte) (argb & 0xFF); + } else { + rVal[index++] = (byte) ((argb >> 16) & 0xFF); + rVal[index++] = (byte) ((argb >> 8) & 0xFF); + rVal[index++] = (byte) (argb & 0xFF); + if (hasAlpha) { + rVal[index++] = (byte) ((argb >> 24) & 0xFF); + } + } + } + } + return rVal; + } + + public static byte[] asByteArray(final RenderedImage image, final boolean isGreyscale, final boolean hasAlpha) { + final int imageWidth = image.getWidth(); + final int imageHeight = image.getHeight(); + final Raster raster = image.getData(); + + if (raster.getTransferType() == DataBuffer.TYPE_BYTE) { + return (byte[]) image.getData().getDataElements(0, 0, imageWidth, imageHeight, null); + } + + final byte[] rVal = new byte[imageWidth * imageHeight * (isGreyscale ? 1 : (hasAlpha ? 4 : 3))]; + final int[] tmpData = new int[imageWidth]; + int index = 0; + for (int y = 0; y < imageHeight; y++) { + getRGB(raster, image.getColorModel(), 0, y, imageWidth, 1, tmpData, 0, imageWidth); + for (int i = 0; i < imageWidth; i++) { + final int argb = tmpData[i]; + if (isGreyscale) { + rVal[index++] = (byte) (argb & 0xFF); + } else { + rVal[index++] = (byte) ((argb >> 16) & 0xFF); + rVal[index++] = (byte) ((argb >> 8) & 0xFF); + rVal[index++] = (byte) (argb & 0xFF); + if (hasAlpha) { + rVal[index++] = (byte) ((argb >> 24) & 0xFF); + } + } + } + } + return rVal; + } + + /** + * Extract rgb values from raster using the colormodel. + */ + private static int[] getRGB(final Raster raster, final ColorModel colorModel, final int startX, final int startY, + final int w, final int h, int[] rgbArray, final int offset, final int scansize) { + Object data; + final int nbands = raster.getNumBands(); + final int dataType = raster.getDataBuffer().getDataType(); + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new byte[nbands]; + break; + case DataBuffer.TYPE_USHORT: + data = new short[nbands]; + break; + case DataBuffer.TYPE_INT: + data = new int[nbands]; + break; + case DataBuffer.TYPE_FLOAT: + data = new float[nbands]; + break; + case DataBuffer.TYPE_DOUBLE: + data = new double[nbands]; + break; + default: + throw new IllegalArgumentException("Unknown data buffer type: " + dataType); + } + + if (rgbArray == null) { + rgbArray = new int[offset + h * scansize]; + } + + int yoff = offset; + int off; + for (int y = startY; y < startY + h; y++, yoff += scansize) { + off = yoff; + for (int x = startX; x < startX + w; x++) { + rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x, y, data)); + } + } + + return rgbArray; + } + + public static void setCreateOnHeap(final boolean createOnHeap) { + AWTImageLoader.createOnHeap = createOnHeap; + } + + public static boolean isCreateOnHeap() { + return createOnHeap; + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java new file mode 100644 index 0000000..3a8fa19 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.image.util; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; +import java.util.List; + +import com.ardor3d.image.Image; +import com.ardor3d.image.PixelDataType; +import com.google.common.collect.Lists; + +/** + * Utility methods for converting Ardor3D Images to AWT BufferedImages. + */ +public abstract class AWTImageUtil { + + /** + * Convert the given Ardor3D Image to a List of BufferedImages. It is a List because Ardor3D Images may contain + * multiple layers (for example, in the case of cube maps or 3D textures). + * + * @param input + * the Ardor3D Image to convert + * @return the BufferedImage(s) created in the conversion + */ + public static List convertToAWT(final Image input) { + // convert, using a full white tint (i.e. no applied color change from original data.) + return convertToAWT(input, Color.WHITE); + } + + /** + * Convert the given Ardor3D Image to a List of BufferedImages. It is a List because Ardor3D Images may contain + * multiple layers (for example, in the case of cube maps or 3D textures). The given AWT Color is used to modulate + * or "tint" the returned image. + * + * TODO: Add support for more formats.
+ * XXX: Note that only images of data type ImageDataType.UnsignedByte and ImageDataFormat of RGB or RGBA are + * currently supported. + * + * @param input + * the Ardor3D Image to convert + * @param tint + * the Color to apply to the generated image + * @return the BufferedImage(s) created in the conversion + */ + public static List convertToAWT(final Image input, final Color tint) { + if (input.getDataType() != PixelDataType.UnsignedByte) { + throw new Error("Unhandled Ardor3D image data type: " + input.getDataType()); + } + // count the number of layers we will be converting. + final int size = input.getData().size(); + + // grab our image width and height + final int width = input.getWidth(), height = input.getHeight(); + + // create our return list + final List rVal = Lists.newArrayList(); + + // Calculate our modulation or "tint" values per channel + final double tRed = tint != null ? tint.getRed() / 255. : 1.0; + final double tGreen = tint != null ? tint.getGreen() / 255. : 1.0; + final double tBlue = tint != null ? tint.getBlue() / 255. : 1.0; + final double tAlpha = tint != null ? tint.getAlpha() / 255. : 1.0; + + // go through each layer + for (int i = 0; i < size; i++) { + BufferedImage image; + final ByteBuffer data = input.getData(i); + data.rewind(); + boolean alpha = false; + switch (input.getDataFormat()) { + case RGBA: + alpha = true; + // Falls through on purpose. + case RGB: + if (alpha) { + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } else { + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + } + + int index, + r, + g, + b, + a, + argb; + + // Go through each pixel + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + index = (alpha ? 4 : 3) * (y * width + x); + r = (int) Math.round(((data.get(index + 0)) & 0xFF) * tRed); + g = (int) Math.round(((data.get(index + 1)) & 0xFF) * tGreen); + b = (int) Math.round(((data.get(index + 2)) & 0xFF) * tBlue); + + // convert to integer expression + argb = (r << 16) | (g << 8) | (b); + + // add alpha, if applicable + if (alpha) { + a = (int) Math.round(((data.get(index + 3)) & 0xFF) * tAlpha); + argb |= (a & 0xFF) << 24; + } + + // apply to image + image.setRGB(x, y, argb); + } + } + break; + default: + throw new Error("Unhandled image data format: " + input.getDataFormat()); + } + + // add to our list + rVal.add(image); + } + + // return list + return rVal; + } + +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java new file mode 100644 index 0000000..827b915 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.image.util; + +import java.awt.image.BufferedImage; + +import com.ardor3d.image.Image; +import com.ardor3d.image.Texture; +import com.ardor3d.image.TextureStoreFormat; +import com.ardor3d.util.TextureKey; +import com.ardor3d.util.TextureManager; + +public abstract class AWTTextureUtil { + + /** + * Convenience Utility for loading a BufferedImage as an Ardor3D Texture. + * + * @param image + * our buffered image + * @param minFilter + * the filter for the near values. Used to determine if we should generate mipmaps. + * @param flipVertically + * If true, the image is flipped vertically during image conversion. + * @param storeFormat + * the specific format to use when storing this texture on the card. + * @return our new Texture. + */ + public static final Texture loadTexture(final BufferedImage image, final Texture.MinificationFilter minFilter, + final TextureStoreFormat storeFormat, final boolean flipVertically) { + final Image imageData = AWTImageLoader.makeArdor3dImage(image, flipVertically); + final String fileType = (image != null) ? "" + image.hashCode() : null; + final TextureKey tkey = TextureKey.getKey(null, flipVertically, storeFormat, fileType, minFilter); + return TextureManager.loadFromKey(tkey, imageData, null); + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java new file mode 100644 index 0000000..be24c26 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.image.util; + +import java.awt.Color; + +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.type.ReadOnlyColorRGBA; + +public class AwtColorUtil { + + public static ColorRGBA makeColorRGBA(final Color color) { + if (color == null) { + return new ColorRGBA(0, 0, 0, 1); + } + return new ColorRGBA(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, + color.getAlpha() / 255f); + } + + public static Color makeColor(final ReadOnlyColorRGBA color) { + return makeColor(color, true); + } + + public static Color makeColor(final ReadOnlyColorRGBA color, final boolean useAlpha) { + if (color == null) { + return new Color(0, 0, 0, 1); + } + return new Color(color.getRed(), color.getGreen(), color.getBlue(), useAlpha ? color.getAlpha() : 1.0f); + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java new file mode 100644 index 0000000..3247d5e --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.image.util; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; + +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.util.screen.ScreenExportable; + +public class ScreenShotImageExporter implements ScreenExportable { + private static final Logger logger = Logger.getLogger(ScreenShotImageExporter.class.getName()); + + protected File _directory; + protected String _prepend; + protected String _fileFormat; + protected boolean _useAlpha; + + protected File _lastFile; + + /** + * Make a new exporter with the default settings: + * + *
+     * directory: local working directory
+     * prepend: "capture_"
+     * format: "png"
+     * useAlpha: false
+     * 
+ */ + public ScreenShotImageExporter() { + this(new File(System.getProperty("user.dir")), "capture_", "png", false); + } + + /** + * Construct a new exporter. + * + * @param directory + * the directory to save the screen shots in. + * @param prepend + * a value to prepend onto the generated file name. This must be at least 3 characters long. + * @param format + * the format to use for saving the image. ImageIO is used for this, so safe values are likely: "png", + * "jpg", "gif" and "bmp" + * @param useAlpha + * true for alpha values to be stored in image (as applicable, depending on the given format) + */ + public ScreenShotImageExporter(final File directory, final String prepend, final String format, + final boolean useAlpha) { + _directory = directory; + _prepend = prepend; + _fileFormat = format; + _useAlpha = useAlpha; + } + + public void export(final ByteBuffer data, final int width, final int height) { + final BufferedImage img = new BufferedImage(width, height, _useAlpha ? BufferedImage.TYPE_INT_ARGB + : BufferedImage.TYPE_INT_RGB); + + int index, r, g, b, a; + int argb; + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + index = (_useAlpha ? 4 : 3) * ((height - y - 1) * width + x); + r = ((data.get(index + 0))); + g = ((data.get(index + 1))); + b = ((data.get(index + 2))); + + argb = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + + if (_useAlpha) { + a = ((data.get(index + 3))); + argb |= (a & 0xFF) << 24; + } + + img.setRGB(x, y, argb); + } + } + + try { + final File out = new File(_directory, _prepend + System.currentTimeMillis() + "." + _fileFormat); + logger.fine("Taking screenshot: " + out.getAbsolutePath()); + + // write out the screen shot image to a file. + ImageIO.write(img, _fileFormat, out); + + // save our successful file to be accessed as desired. + _lastFile = out; + } catch (final IOException e) { + logger + .logp(Level.WARNING, getClass().getName(), "export(ByteBuffer, int, int)", e.getLocalizedMessage(), + e); + } + } + + public ImageDataFormat getFormat() { + if (_useAlpha) { + return ImageDataFormat.RGBA; + } else { + return ImageDataFormat.RGB; + } + } + + /** + * @return the last File written by this exporter, or null if none were written. + */ + public File getLastFile() { + return _lastFile; + } + + public File getDirectory() { + return _directory; + } + + public void setDirectory(final File directory) { + _directory = directory; + } + + public String getPrepend() { + return _prepend; + } + + public void setPrepend(final String prepend) { + _prepend = prepend; + } + + public boolean isUseAlpha() { + return _useAlpha; + } + + public void setUseAlpha(final boolean useAlpha) { + _useAlpha = useAlpha; + } + + public String getFileFormat() { + return _fileFormat; + } + + public void setFileFormat(final String format) { + _fileFormat = format; + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java new file mode 100644 index 0000000..5514856 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.awt; + +import java.awt.Component; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import com.ardor3d.input.FocusWrapper; + +/** + * Focus listener class for use with AWT. + */ +public class AwtFocusWrapper implements FocusWrapper, FocusListener { + protected volatile boolean _focusLost = false; + + protected final Component _component; + + public AwtFocusWrapper(final Component component) { + _component = component; + } + + public void focusGained(final FocusEvent e) { + // do nothing + } + + public void focusLost(final FocusEvent e) { + _focusLost = true; + } + + public boolean getAndClearFocusLost() { + final boolean result = _focusLost; + + _focusLost = false; + + return result; + } + + public void init() { + _component.addFocusListener(this); + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java new file mode 100644 index 0000000..f464154 --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.awt; + +import java.awt.event.KeyEvent; + +import com.ardor3d.input.Key; + +/** + * Enum used for translations between AWT key codes and Ardor3D {@link Key} instances. + */ +public enum AwtKey { + + ZERO(KeyEvent.VK_0, Key.ZERO), // + ONE(KeyEvent.VK_1, Key.ONE), // + TWO(KeyEvent.VK_2, Key.TWO), // + THREE(KeyEvent.VK_3, Key.THREE), // + FOUR(KeyEvent.VK_4, Key.FOUR), // + FIVE(KeyEvent.VK_5, Key.FIVE), // + SIX(KeyEvent.VK_6, Key.SIX), // + SEVEN(KeyEvent.VK_7, Key.SEVEN), // + EIGHT(KeyEvent.VK_8, Key.EIGHT), // + NINE(KeyEvent.VK_9, Key.NINE), // + A(KeyEvent.VK_A, Key.A), // + ADD(KeyEvent.VK_ADD, Key.NUMPADADD), // + AT(KeyEvent.VK_AT, Key.AT), // + B(KeyEvent.VK_B, Key.B), // + BACK_QUOTE(KeyEvent.VK_BACK_QUOTE, Key.GRAVE), // + BACK_SPACE(KeyEvent.VK_BACK_SPACE, Key.BACK), // + BACK_SLASH(KeyEvent.VK_BACK_SLASH, Key.BACKSLASH), // + C(KeyEvent.VK_C, Key.C), // + CAPS_LOCK(KeyEvent.VK_CAPS_LOCK, Key.CAPITAL), // + CIRCUMFLEX(KeyEvent.VK_CIRCUMFLEX, Key.CIRCUMFLEX), // + COLON(KeyEvent.VK_COLON, Key.COLON), // + COMMA(KeyEvent.VK_COMMA, Key.COMMA), // + CONVERT(KeyEvent.VK_CONVERT, Key.CONVERT), // + D(KeyEvent.VK_D, Key.D), // + DECIMAL(KeyEvent.VK_DECIMAL, Key.DECIMAL), // + DELETE(KeyEvent.VK_DELETE, Key.DELETE), // + DIVIDE(KeyEvent.VK_DIVIDE, Key.DIVIDE), // + DOWN(KeyEvent.VK_DOWN, Key.DOWN), // + E(KeyEvent.VK_E, Key.E), // + END(KeyEvent.VK_END, Key.END), // + EQUALS(KeyEvent.VK_EQUALS, Key.EQUALS), // + ESCAPE(KeyEvent.VK_ESCAPE, Key.ESCAPE), // + F(KeyEvent.VK_F, Key.F), // + F1(KeyEvent.VK_F1, Key.F1), // + F2(KeyEvent.VK_F2, Key.F2), // + F3(KeyEvent.VK_F3, Key.F3), // + F4(KeyEvent.VK_F4, Key.F4), // + F5(KeyEvent.VK_F5, Key.F5), // + F6(KeyEvent.VK_F6, Key.F6), // + F7(KeyEvent.VK_F7, Key.F7), // + F8(KeyEvent.VK_F8, Key.F8), // + F9(KeyEvent.VK_F9, Key.F9), // + F10(KeyEvent.VK_F10, Key.F10), // + F11(KeyEvent.VK_F11, Key.F11), // + F12(KeyEvent.VK_F12, Key.F12), // + F13(KeyEvent.VK_F13, Key.F13), // + F14(KeyEvent.VK_F14, Key.F14), // + F15(KeyEvent.VK_F15, Key.F15), // + G(KeyEvent.VK_G, Key.G), // + H(KeyEvent.VK_H, Key.H), // + HOME(KeyEvent.VK_HOME, Key.HOME), // + I(KeyEvent.VK_I, Key.I), // + INSERT(KeyEvent.VK_INSERT, Key.INSERT), // + J(KeyEvent.VK_J, Key.J), // + K(KeyEvent.VK_K, Key.K), // + KANA(KeyEvent.VK_KANA, Key.KANA), // + KANJI(KeyEvent.VK_KANJI, Key.KANJI), // + L(KeyEvent.VK_L, Key.L), // + OPEN_BRACKET(KeyEvent.VK_OPEN_BRACKET, Key.LBRACKET), // + CONTROL(KeyEvent.VK_CONTROL, Key.LCONTROL), // + LEFT(KeyEvent.VK_LEFT, Key.LEFT), // + ALT(KeyEvent.VK_ALT, Key.LMENU), // + META(KeyEvent.VK_META, Key.LMETA), // + SHIFT(KeyEvent.VK_SHIFT, Key.LSHIFT), // + M(KeyEvent.VK_M, Key.M), // + MINUS(KeyEvent.VK_MINUS, Key.MINUS), // + MULTIPLY(KeyEvent.VK_MULTIPLY, Key.MULTIPLY), // + N(KeyEvent.VK_N, Key.N), // + PAGE_DOWN(KeyEvent.VK_PAGE_DOWN, Key.PAGEDOWN_NEXT), // + NONCONVERT(KeyEvent.VK_NONCONVERT, Key.NOCONVERT), // + NUM_LOCK(KeyEvent.VK_NUM_LOCK, Key.NUMLOCK), // + NUMPAD0(KeyEvent.VK_NUMPAD0, Key.NUMPAD0), // + NUMPAD1(KeyEvent.VK_NUMPAD1, Key.NUMPAD1), // + NUMPAD2(KeyEvent.VK_NUMPAD2, Key.NUMPAD2), // + NUMPAD3(KeyEvent.VK_NUMPAD3, Key.NUMPAD3), // + NUMPAD4(KeyEvent.VK_NUMPAD4, Key.NUMPAD4), // + NUMPAD5(KeyEvent.VK_NUMPAD5, Key.NUMPAD5), // + NUMPAD6(KeyEvent.VK_NUMPAD6, Key.NUMPAD6), // + NUMPAD7(KeyEvent.VK_NUMPAD7, Key.NUMPAD7), // + NUMPAD8(KeyEvent.VK_NUMPAD8, Key.NUMPAD8), // + NUMPAD9(KeyEvent.VK_NUMPAD9, Key.NUMPAD9), // + O(KeyEvent.VK_O, Key.O), // + P(KeyEvent.VK_P, Key.P), // + PAUSE(KeyEvent.VK_PAUSE, Key.PAUSE), // + PERIOD(KeyEvent.VK_PERIOD, Key.PERIOD), // + PAGE_UP(KeyEvent.VK_PAGE_UP, Key.PAGEUP_PRIOR), // + Q(KeyEvent.VK_Q, Key.Q), // + QUOTE(KeyEvent.VK_QUOTE, Key.APOSTROPHE), // + R(KeyEvent.VK_R, Key.R), // + CLOSE_BRACKET(KeyEvent.VK_CLOSE_BRACKET, Key.RBRACKET), // + ENTER(KeyEvent.VK_ENTER, Key.RETURN), // + RIGHT(KeyEvent.VK_RIGHT, Key.RIGHT), // + S(KeyEvent.VK_S, Key.S), // + SCROLL_LOCK(KeyEvent.VK_SCROLL_LOCK, Key.SCROLL), // + SEMICOLON(KeyEvent.VK_SEMICOLON, Key.SEMICOLON), // + SLASH(KeyEvent.VK_SLASH, Key.SLASH), // + SPACE(KeyEvent.VK_SPACE, Key.SPACE), // + STOP(KeyEvent.VK_STOP, Key.STOP), // + PRINTSCREEN(KeyEvent.VK_PRINTSCREEN, Key.SYSRQ), // + T(KeyEvent.VK_T, Key.T), // + TAB(KeyEvent.VK_TAB, Key.TAB), // + U(KeyEvent.VK_U, Key.U), // + UNDERSCORE(KeyEvent.VK_UNDERSCORE, Key.UNDERLINE), // + UP(KeyEvent.VK_UP, Key.UP), // + V(KeyEvent.VK_V, Key.V), // + W(KeyEvent.VK_W, Key.W), // + X(KeyEvent.VK_X, Key.X), // + Y(KeyEvent.VK_Y, Key.Y), // + Z(KeyEvent.VK_Z, Key.Z), // + UNDEFINED(KeyEvent.VK_UNDEFINED, Key.UNKNOWN); + + private final int _awtCode; + private final Key _key; + + private AwtKey(final int awtCode, final Key key) { + _awtCode = awtCode; + _key = key; + } + + public static Key findByCode(final int awtCode) { + for (final AwtKey ak : values()) { + if (ak._awtCode == awtCode) { + return ak._key; + } + } + + return Key.UNKNOWN; + } + + public int getAwtCode() { + return _awtCode; + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java new file mode 100644 index 0000000..5aa9a4d --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.awt; + +import java.awt.Component; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyListener; +import java.util.EnumSet; +import java.util.LinkedList; + +import com.ardor3d.annotation.GuardedBy; +import com.ardor3d.input.Key; +import com.ardor3d.input.KeyEvent; +import com.ardor3d.input.KeyState; +import com.ardor3d.input.KeyboardWrapper; +import com.google.common.base.Preconditions; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.PeekingIterator; + +/** + * Keyboard wrapper class for use with AWT. + */ +public class AwtKeyboardWrapper implements KeyboardWrapper, KeyListener { + @GuardedBy("this") + protected final LinkedList _upcomingEvents = new LinkedList(); + + @GuardedBy("this") + protected AwtKeyboardIterator _currentIterator = null; + + protected final Component _component; + + protected boolean _consumeEvents = false; + + protected final EnumSet _pressedList = EnumSet.noneOf(Key.class); + + public AwtKeyboardWrapper(final Component component) { + _component = Preconditions.checkNotNull(component, "component"); + } + + public void init() { + _component.addKeyListener(this); + _component.addFocusListener(new FocusListener() { + public void focusLost(final FocusEvent e) {} + + public void focusGained(final FocusEvent e) { + _pressedList.clear(); + } + }); + } + + public synchronized PeekingIterator getEvents() { + if (_currentIterator == null || !_currentIterator.hasNext()) { + _currentIterator = new AwtKeyboardIterator(); + } + + return _currentIterator; + } + + public synchronized void keyTyped(final java.awt.event.KeyEvent e) { + if (_consumeEvents) { + e.consume(); + // ignore this event + } + } + + public synchronized void keyPressed(final java.awt.event.KeyEvent e) { + final Key pressed = fromKeyEventToKey(e); + if (!_pressedList.contains(pressed)) { + _upcomingEvents.add(new KeyEvent(pressed, KeyState.DOWN, e.getKeyChar())); + _pressedList.add(pressed); + } + if (_consumeEvents) { + e.consume(); + } + } + + public synchronized void keyReleased(final java.awt.event.KeyEvent e) { + final Key released = fromKeyEventToKey(e); + _upcomingEvents.add(new KeyEvent(released, KeyState.UP, e.getKeyChar())); + _pressedList.remove(released); + if (_consumeEvents) { + e.consume(); + } + } + + /** + * Convert from AWT key event to Ardor3D Key. Override to provide additional or custom behavior. + * + * @param e + * the AWT KeyEvent received by the input system. + * @return an Ardor3D Key, to be forwarded to the Predicate/Trigger system. + */ + public synchronized Key fromKeyEventToKey(final java.awt.event.KeyEvent e) { + return AwtKey.findByCode(e.getKeyCode()); + } + + private class AwtKeyboardIterator extends AbstractIterator implements PeekingIterator { + @Override + protected KeyEvent computeNext() { + synchronized (AwtKeyboardWrapper.this) { + if (_upcomingEvents.isEmpty()) { + return endOfData(); + } + + return _upcomingEvents.poll(); + } + } + } + + public boolean isConsumeEvents() { + return _consumeEvents; + } + + public void setConsumeEvents(final boolean consumeEvents) { + _consumeEvents = consumeEvents; + } +} diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java new file mode 100644 index 0000000..8841f3a --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.awt; + +import java.awt.AWTException; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Logger; + +import javax.swing.SwingUtilities; + +import com.ardor3d.image.util.AWTImageUtil; +import com.ardor3d.input.GrabbedState; +import com.ardor3d.input.MouseCursor; +import com.ardor3d.input.MouseManager; + +/** + * Implementation of the {@link com.ardor3d.input.MouseManager} interface for use with AWT windows. This implementation + * supports the optional {@link #setGrabbed(com.ardor3d.input.GrabbedState)} and {@link #setPosition(int, int)} methods + * if an AWT robot can be created on the current system. The constructor takes an AWT {@link java.awt.Component} + * instance, for which the cursor is set. In a multi-canvas application, each canvas can have its own AwtMouseManager + * instance, or it is possible to use a single one for the AWT container that includes the canvases. + */ +public class AwtMouseManager implements MouseManager { + private static final Logger logger = Logger.getLogger(AwtMouseManager.class.getName()); + + private static Cursor _transparentCursor; + + private final Component _component; + private Robot _robot; + + /** our current grabbed state */ + private GrabbedState _grabbedState; + + /** Our cursor prior to a setGrabbed(GRABBED) operation. Stored to be used when cursor is "ungrabbed" */ + private Cursor _pregrabCursor; + + public AwtMouseManager(final Component component) { + _component = component; + + // Attempt to make + try { + _robot = new Robot(); + } catch (final AWTException ex) { + logger.warning("Unable to create java.awt.Robot. setPosition and setGrabbed will not be supported."); + } + } + + public void setCursor(final MouseCursor cursor) { + if (cursor == MouseCursor.SYSTEM_DEFAULT) { + if (_grabbedState == GrabbedState.GRABBED) { + _pregrabCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); + } else { + _component.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + return; + } + + final BufferedImage image = AWTImageUtil.convertToAWT(cursor.getImage()).get(0); + + // the hotSpot values must be less than the Dimension returned by getBestCursorSize + final Dimension bestCursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(cursor.getHotspotX(), + cursor.getHotspotY()); + final Point hotSpot = new Point(Math.min(cursor.getHotspotX(), (int) bestCursorSize.getWidth() - 1), Math.min( + cursor.getHotspotY(), (int) bestCursorSize.getHeight() - 1)); + + final Cursor awtCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, hotSpot, cursor.getName()); + if (_grabbedState == GrabbedState.GRABBED) { + _pregrabCursor = awtCursor; + } else { + _component.setCursor(awtCursor); + } + } + + public void setPosition(final int x, final int y) { + if (!isSetPositionSupported()) { + throw new UnsupportedOperationException(); + } + + try { + // Only queue up if we are not already in the event thread. + if (java.awt.EventQueue.isDispatchThread()) { + setMousePosition(x, y); + } else { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + setMousePosition(x, y); + } + }); + } + } catch (final InterruptedException ex) { + } catch (final InvocationTargetException ex) { + } + } + + private void setMousePosition(final int ardorX, final int ardorY) { + Component c = _component; + if (c instanceof Frame && ((Frame) c).getComponentCount() > 0) { + c = ((Frame) c).getComponent(0); + } + final Point p = new Point(ardorX, c.getHeight() - ardorY); + SwingUtilities.convertPointToScreen(p, c); + _robot.mouseMove(p.x, p.y); + } + + public void setGrabbed(final GrabbedState grabbedState) { + if (!isSetGrabbedSupported()) { + throw new UnsupportedOperationException(); + } + + // check if we should be here. + if (_grabbedState == grabbedState) { + return; + } + + // remember our grabbed state mode. + _grabbedState = grabbedState; + + if (grabbedState == GrabbedState.GRABBED) { + // remember our old cursor + _pregrabCursor = _component.getCursor(); + + // set our cursor to be invisible + _component.setCursor(getTransparentCursor()); + } else { + // restore our old cursor + _component.setCursor(_pregrabCursor); + } + } + + private static final Cursor getTransparentCursor() { + if (_transparentCursor == null) { + final BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + cursorImage.setRGB(0, 0, 0); + _transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0), + "empty cursor"); + } + return _transparentCursor; + } + + public boolean isSetPositionSupported() { + return _robot != null; + } + + public boolean isSetGrabbedSupported() { + return _robot != null; + } + + public GrabbedState getGrabbed() { + return _grabbedState; + } +} \ No newline at end of file diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java new file mode 100644 index 0000000..c64b70b --- /dev/null +++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.awt; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedList; + +import com.ardor3d.annotation.GuardedBy; +import com.ardor3d.input.ButtonState; +import com.ardor3d.input.GrabbedState; +import com.ardor3d.input.MouseButton; +import com.ardor3d.input.MouseManager; +import com.ardor3d.input.MouseState; +import com.ardor3d.input.MouseWrapper; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.EnumMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.PeekingIterator; + +/** + * Mouse wrapper class for use with AWT. + */ +public class AwtMouseWrapper implements MouseWrapper, MouseListener, MouseWheelListener, MouseMotionListener { + @GuardedBy("this") + protected final LinkedList _upcomingEvents = Lists.newLinkedList(); + + @GuardedBy("this") + protected AwtMouseIterator _currentIterator = null; + + @GuardedBy("this") + protected MouseState _lastState = null; + + protected boolean _consumeEvents = false; + + protected final Component _component; + protected final Frame _frame; + protected final MouseManager _manager; + + protected final Multiset _clicks = EnumMultiset.create(MouseButton.class); + protected final EnumMap _lastClickTime = Maps.newEnumMap(MouseButton.class); + protected final EnumSet _clickArmed = EnumSet.noneOf(MouseButton.class); + + protected int _ignoreX = Integer.MAX_VALUE; + protected int _ignoreY = Integer.MAX_VALUE; + + public AwtMouseWrapper(final Component component, final MouseManager manager) { + _manager = manager; + if (component instanceof Frame) { + _frame = (Frame) (_component = component); + } else { + _component = checkNotNull(component, "component"); + _frame = null; + } + for (final MouseButton mb : MouseButton.values()) { + _lastClickTime.put(mb, 0L); + } + } + + public void init() { + _component.addMouseListener(this); + _component.addMouseMotionListener(this); + _component.addMouseWheelListener(this); + } + + public synchronized PeekingIterator getEvents() { + expireClickEvents(); + + if (_currentIterator == null || !_currentIterator.hasNext()) { + _currentIterator = new AwtMouseIterator(); + } + + return _currentIterator; + } + + private void expireClickEvents() { + if (!_clicks.isEmpty()) { + for (final MouseButton mb : MouseButton.values()) { + if (System.currentTimeMillis() - _lastClickTime.get(mb) > MouseState.CLICK_TIME_MS) { + _clicks.setCount(mb, 0); + } + } + } + } + + public synchronized void mousePressed(final MouseEvent e) { + final MouseButton b = getButtonForEvent(e); + if (_clickArmed.contains(b)) { + _clicks.setCount(b, 0); + } + _clickArmed.add(b); + _lastClickTime.put(b, System.currentTimeMillis()); + + initState(e); + if (_consumeEvents) { + e.consume(); + } + + final EnumMap buttons = _lastState.getButtonStates(); + + setStateForButton(e, buttons, ButtonState.DOWN); + + addNewState(e, buttons, null); + } + + public synchronized void mouseReleased(final MouseEvent e) { + initState(e); + if (_consumeEvents) { + e.consume(); + } + + final EnumMap buttons = _lastState.getButtonStates(); + + setStateForButton(e, buttons, ButtonState.UP); + + final MouseButton b = getButtonForEvent(e); + if (_clickArmed.contains(b) && (System.currentTimeMillis() - _lastClickTime.get(b) <= MouseState.CLICK_TIME_MS)) { + _clicks.add(b); // increment count of clicks for button b. + // XXX: Note the double event add... this prevents sticky click counts, but is it the best way? + addNewState(e, buttons, EnumMultiset.create(_clicks)); + } else { + _clicks.setCount(b, 0); // clear click count for button b. + } + _clickArmed.remove(b); + + addNewState(e, buttons, null); + } + + public synchronized void mouseDragged(final MouseEvent e) { + // forward to mouseMoved. + mouseMoved(e); + } + + public synchronized void mouseMoved(final MouseEvent e) { + _clickArmed.clear(); + _clicks.clear(); + + // check that we have a valid _lastState + initState(e); + if (_consumeEvents) { + e.consume(); + } + + // remember our current ardor3d position + final int oldX = _lastState.getX(), oldY = _lastState.getY(); + + // check the state against the "ignore next" values + if (_ignoreX != Integer.MAX_VALUE // shortcut to prevent dx/dy calculations + && (_ignoreX == getDX(e) && _ignoreY == getDY(e))) { + + // we matched, so we'll consider this a "mouse pointer reset move" + // so reset ignore to let the next move event through. + _ignoreX = Integer.MAX_VALUE; + _ignoreY = Integer.MAX_VALUE; + + // exit without adding an event to our queue + return; + } + + // save our old "last state." + final MouseState _savedState = _lastState; + + // Add our latest state info to the queue + addNewState(e, _lastState.getButtonStates(), null); + + // If we have a valid move... should always be the case, but occasionally something slips through. + if (_lastState.getDx() != 0 || _lastState.getDy() != 0) { + + // Ask our manager if we're currently "captured" + if (_manager.getGrabbed() == GrabbedState.GRABBED) { + + // if so, set "ignore next" to the inverse of this move + _ignoreX = -_lastState.getDx(); + _ignoreY = -_lastState.getDy(); + + // Move us back to our last position. + _manager.setPosition(oldX, oldY); + + // And finally, revert our _lastState. + _lastState = _savedState; + } else { + // otherwise, set us to not ignore anything. This may be unnecessary, but prevents any possible + // "ignore" bleeding. + _ignoreX = Integer.MAX_VALUE; + _ignoreY = Integer.MAX_VALUE; + } + } + } + + public void mouseWheelMoved(final MouseWheelEvent e) { + initState(e); + + addNewState(e, _lastState.getButtonStates(), null); + if (_consumeEvents) { + e.consume(); + } + } + + private void initState(final MouseEvent mouseEvent) { + if (_lastState == null) { + _lastState = new MouseState(mouseEvent.getX(), getArdor3DY(mouseEvent), 0, 0, 0, null, null); + } + } + + private void addNewState(final MouseEvent mouseEvent, final EnumMap enumMap, + final Multiset clicks) { + final MouseState newState = new MouseState(mouseEvent.getX(), getArdor3DY(mouseEvent), getDX(mouseEvent), + getDY(mouseEvent), (mouseEvent instanceof MouseWheelEvent ? ((MouseWheelEvent) mouseEvent) + .getWheelRotation() : 0), enumMap, clicks); + + synchronized (AwtMouseWrapper.this) { + _upcomingEvents.add(newState); + } + _lastState = newState; + } + + private int getDX(final MouseEvent e) { + return e.getX() - _lastState.getX(); + } + + private int getDY(final MouseEvent e) { + return getArdor3DY(e) - _lastState.getY(); + } + + /** + * @param e + * our mouseEvent + * @return the Y coordinate of the event, flipped relative to the component since we expect an origin in the lower + * left corner. + */ + private int getArdor3DY(final MouseEvent e) { + final int height = (_frame != null && _frame.getComponentCount() > 0) ? _frame.getComponent(0).getHeight() + : _component.getHeight(); + return height - e.getY(); + } + + private void setStateForButton(final MouseEvent e, final EnumMap buttons, + final ButtonState buttonState) { + final MouseButton button = getButtonForEvent(e); + buttons.put(button, buttonState); + } + + private MouseButton getButtonForEvent(final MouseEvent e) { + MouseButton button; + switch (e.getButton()) { + case MouseEvent.BUTTON1: + button = MouseButton.LEFT; + break; + case MouseEvent.BUTTON2: + button = MouseButton.MIDDLE; + break; + case MouseEvent.BUTTON3: + button = MouseButton.RIGHT; + break; + default: + throw new RuntimeException("unknown button: " + e.getButton()); + } + return button; + } + + private class AwtMouseIterator extends AbstractIterator implements PeekingIterator { + @Override + protected MouseState computeNext() { + synchronized (AwtMouseWrapper.this) { + if (_upcomingEvents.isEmpty()) { + return endOfData(); + } + + return _upcomingEvents.poll(); + } + + } + } + + // -- The following interface methods are not used. -- + + public synchronized void mouseClicked(final MouseEvent e) { + // Yes, we could use the click count here, but in the interests of this working the same way as SWT and Native, + // we + // will do it the same way they do it. + if (_consumeEvents) { + e.consume(); + } + } + + public synchronized void mouseEntered(final MouseEvent e) { + // ignore this + if (_consumeEvents) { + e.consume(); + } + } + + public synchronized void mouseExited(final MouseEvent e) { + // ignore this + if (_consumeEvents) { + e.consume(); + } + } + + public boolean isConsumeEvents() { + return _consumeEvents; + } + + public void setConsumeEvents(final boolean consumeEvents) { + _consumeEvents = consumeEvents; + } +} -- cgit v1.2.3