Michael Ludwig avatar Michael Ludwig committed a297dfc

Finish documenting the renderer package

Comments (0)

Files changed (2)

src/com/ferox/core/renderer/RenderContext.java

 	
 	RenderManager manager;
 	
-	private RenderPassPeer<RenderPass> defaultPass; 
+	private RenderPassPeer<RenderPass> defaultPass;
 	// TODO: add render to texture render passes
 	
 	private StateManager[] stateRecord;
 	private DynamicStateTracker[] dynamicRecord;
 	private RenderAtom currAtom;
 	
+	/**
+	 * Register the given type of atom and return the dynamic type.  All state atoms and state managers
+	 * call this in their constructor.  If an atom type has already been registered, it returns the previously
+	 * assigned value.
+	 */
 	public static int registerStateAtomType(Class<? extends StateAtom> m) {
 		if (atomTypes.containsKey(m))
 			return atomTypes.get(m);
 	 * it is here to force subclasses to take DisplayOptions as arguments.  Subclasses must do there best to 
 	 * satisfy the requested options.  They are also responsible for instantiating the correct implementation of 
 	 * a RenderSurface.
-	 * @param options
 	 */
 	public RenderContext(DisplayOptions options) {
 		this.defaultPass = this.createDefaultRenderPassPeer();
 	public void beginAtom(RenderAtom atom) {
 		if (this.currAtom != null)
 			throw new FeroxException("Can't call beginAtom() after a previous beginAtom() call if endAtom() hasn't been called");
+		if (atom == null)
+			throw new NullPointerException("Can't call beginAtom() with a null atom");
 		this.currAtom = atom;
 		if (this.dynamicRecord != null)
 			for (int i = 0; i < this.dynamicRecord.length; i++)
 	 * The rest of the atom's states should have already been applied by the RenderAtomBin.
 	 */
 	public void endAtom(RenderAtom atom) {
+		if (atom != this.currAtom || this.currAtom == null)
+			throw new FeroxException("Can't call endAtom() with a different RenderAtom or not after beginAtom()");
 		if (this.dynamicRecord != null)
 			for (int i = 0; i < this.dynamicRecord.length; i++)
 				if (this.dynamicRecord[i] != null)
 		this.pushModelTransform(atom.getSpatialLink().getWorldTransform());
 		this.renderGeometry(atom.getGeometry());
 		this.popModelTransform();
+		this.currAtom = null;
 	}
 	
 	/**
 	 */
 	public abstract StateAtomPeer getStateAtomPeer(StateAtom atom);
 
+	/**
+	 * Update the texture data on the graphics card for the given texture and region, using the given slice of 
+	 * the buffer in as source data.  The region must be contained completely within the actual textures dimensions
+	 * at the given mipmap level (level >= 0).  It will throw an exception if the region is too large, if the 
+	 * mipmap level doesn't exist in the texture, or if the slice extends beyond the buffer's capacity, or if 
+	 * the slice isn't sized to fit the entire texture.  The buffer to update must be of the same format and type
+	 * of the texture (which might be compressed data).  Because of some driver issues, it may be necessary to 
+	 * reallocate the texture on the graphics card, which is why the source buffer must be sized for the texture.
+	 * It is then recommended to modify the in-memory nio buffer of the texture (in the given region) and then call
+	 * this method with that buffer, so that in the event of a reallocation instead of an update, you will not
+	 * lose your texture image.
+	 * 
+	 * The depth and z offset values of the region are ignored.
+	 */
 	public void setTextureRegion(Texture2D data, Block region, int level, Buffer in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getWidth(level), data.getHeight(level), 1, 
 		this.setTextureData(data, region, null, level, in, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * Equivalent to setTextureRegion(Texture2D, ... Buffer) but uses the BufferData as a source.  If the source BufferData
+	 * is not a vbo or if pixel buffer objects aren't supported, it is identical to the previous method (using the returned
+	 * nio buffer), otherwise it uses pixel buffers (not pbuffers) to use data transfer directly on the graphics
+	 * card (using the graphics card vbo data as a source).
+	 */
 	public void setTextureRegion(Texture2D data, Block region, int level, BufferData in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getWidth(level), data.getHeight(level), 1, 
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * As before with a Texture2D argument, but the depth and z offset values must be valid.
+	 */
 	public void setTextureRegion(Texture3D data, Block region, int level, Buffer in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getWidth(level), data.getHeight(level), data.getDepth(level), 
 		this.setTextureData(data, region, null, level, in, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * See the Texture3D version with a Buffer and the Texture2D version with a BufferData.
+	 */
 	public void setTextureRegion(Texture3D data, Block region, int level, BufferData in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getWidth(level), data.getHeight(level), data.getDepth(level), 
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * Just like the Texture2D call except that it takes a Face value to choose which face of the cube map to 
+	 * update.  Validation then works on the size of that 2D texture face.
+	 */
 	public void setTextureRegion(TextureCubeMap data, Block region, Face face, int level, Buffer in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getSideLength(level), data.getSideLength(level), 1, 
 		this.setTextureData(data, region, face, level, in, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * See previous setTextureRegion docs.
+	 */
 	public void setTextureRegion(TextureCubeMap data, Block region, Face face, int level, BufferData in, Slice slice) {
 		validateAll(data.getDataType(), data.getDataFormat(), data.getNumMipmaps(),
 					data.getSideLength(level), data.getSideLength(level), 1, 
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * Implementations of a RenderContext must implement this method to set given region of the texture data and mipmap level.  Face is only 
+	 * non-null when a cube map is involved.  If the Buffer in is null, then the transfer should use pixel buffers to transfer the 
+	 * data.  Both the texture and possibly the pixel buffer will already have been applied to the 0th unit.
+	 */
 	protected abstract void setTextureData(TextureData data, Block region, Face face, int level, Buffer in, Slice slice);
 	
+	/**
+	 * Fetch the texture data from the graphics card into the given buffer for the given level.  It fetches
+	 * the entire image data, so the slice of the buffer must be fitted to the texture.  And, of course, the
+	 * slice must be contained within the entire buffer's capacity.  The fetched texture will be in the same
+	 * original texture format and type.
+	 */
 	public void getTexture(Texture2D data, int level, Buffer out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		this.getTextureData(data, null, level, out, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * Same as getTexture() but stores the texture data into the buffer data.  If isVBO is true for BufferData
+	 * it will store it on the graphics card.  If pixel buffers are available, there will be a direct transfer
+	 * and the in-memory data will not be updated.  If isVBO is true, and pixel buffers aren't supported, and
+	 * the client buffer has been set to null, then this will be a no-op.
+	 */
 	public void getTexture(Texture2D data, int level, BufferData out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		BufferData pBD = (BufferData)this.push(out, BufferTarget.PIXEL_READ_BUFFER, BufferData.class);
 		if (out.isVBO() && RenderManager.getSystemCapabilities().arePixelBuffersSupported())
 			this.getTextureData(data, null, level, null, slice);
-		else if (out.getData() != null)
+		else if (out.getData() != null) {
 			this.getTextureData(data, null, level, out.getData(), slice);
+			out.updateNow(this.getRenderManager());
+		}
 		this.pop(out, pBD, BufferTarget.PIXEL_READ_BUFFER);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * As getTexture(Textur2D ...)
+	 */
 	public void getTexture(Texture3D data, int level, Buffer out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		this.getTextureData(data, null, level, out, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * As getTexture(Texture2D ...)
+	 */
 	public void getTexture(Texture3D data, int level, BufferData out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		BufferData pBD = (BufferData)this.push(out, BufferTarget.PIXEL_READ_BUFFER, BufferData.class);
 		if (out.isVBO() && RenderManager.getSystemCapabilities().arePixelBuffersSupported())
 			this.getTextureData(data, null, level, null, slice);
-		else if (out.getData() != null)
+		else if (out.getData() != null) {
 			this.getTextureData(data, null, level, out.getData(), slice);
+			out.updateNow(this.getRenderManager());
+		}
 		this.pop(out, pBD, BufferTarget.PIXEL_READ_BUFFER);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * As getTexture(Textur2D ...) but you pick a cube map face to fetch.
+	 */
 	public void getTexture(TextureCubeMap data, Face face, int level, Buffer out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		this.getTextureData(data, face, level, out, slice);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
+	/**
+	 * As getTexture(Textur2D ...) but you pick a cube map face to fetch.
+	 */
 	public void getTexture(TextureCubeMap data, Face face, int level, BufferData out, Slice slice) {
 		TextureFormat f = getServerCompressedFormat(data.getDataFormat());
 		TextureType t = (f.isServerCompressed() ? TextureType.UNSIGNED_BYTE : data.getDataType());
 		BufferData pBD = (BufferData)this.push(out, BufferTarget.PIXEL_READ_BUFFER, BufferData.class);
 		if (out.isVBO() && RenderManager.getSystemCapabilities().arePixelBuffersSupported())
 			this.getTextureData(data, face, level, null, slice);
-		else if (out.getData() != null)
+		else if (out.getData() != null) {
 			this.getTextureData(data, face, level, out.getData(), slice);
+			out.updateNow(this.getRenderManager());
+		}
 		this.pop(out, pBD, BufferTarget.PIXEL_READ_BUFFER);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * Implementations of RenderContext must fetch the data of the texture from the graphics card (and face
+	 * if the texture is a cube map) at the given mipmap level into the buffer.  If the buffer is null,
+	 * it should do a pixel buffer transfer.  The texture data and buffer are already applied.
+	 */
 	protected abstract void getTextureData(TextureData data, Face face, int level, Buffer out, Slice slice);
 	
+	/**
+	 * Copy the current contents of the active drawing buffer into the texture, given the region.  The region
+	 * must be contained within the whole texture's dimensions.  Fails if the region, offset by the screen
+	 * fetch coordinates (sx, sy) goes off screen.  This is an operation on the graphics card, no memory
+	 * is transfered back to the in-memory buffer of the texture.
+	 */
 	public void copyFramePixels(Texture2D data, Block region, int level, int sx, int sy) {
 		if (data.getDataFormat().isServerCompressed())
 			throw new IllegalArgumentException("copyFramePixels doesn't support compressed textures");
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * As copyFramePixels(Texture2D ...) except that in essence it copies the framebuffer into a slice of
+	 * the 3D texture (as specified by the region).  The depth value is ignored, and is set to 1 for the region.
+	 */
 	public void copyFramePixels(Texture3D data, Block region, int level, int sx, int sy) {
 		if (data.getDataFormat().isServerCompressed())
 			throw new IllegalArgumentException("copyFramePixels doesn't support compressed textures");
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
-	public void copyFramePixels(TextureCubeMap data, Block region, int level, int sx, int sy) {
+	/**
+	 * As copyFramePixels(Texture2D ...) except that the face argument chooses one of the 2d faces of the cube
+	 * map to update
+	 */
+	public void copyFramePixels(TextureCubeMap data, Block region, Face face, int level, int sx, int sy) {
 		if (data.getDataFormat().isServerCompressed())
 			throw new IllegalArgumentException("copyFramePixels doesn't support compressed textures");
 		validateMipmap(level, data.getNumMipmaps());
 		validateRegion(region.getXOffset(), region.getYOffset(), 0, region.getWidth(), region.getHeight(), 1, data.getSideLength(level), data.getSideLength(level), 0);
 		validateRegion(sx, sy, 0, region.getWidth(), region.getHeight(), 1, this.getContextWidth(), this.getContextHeight(), 1);
 		TextureData prev = (TextureData)this.push(data, NumericUnit.get(0), TextureData.class);
-		this.copyTextureData(data, region, null, level, sx, sy);
+		this.copyTextureData(data, region, face, level, sx, sy);
 		this.pop(data, prev, NumericUnit.get(0));
 	}
 	
+	/**
+	 * Implementations of RenderContext should override this to perform an actual copy operation from the active
+	 * drawing buffer to the given texture.  It should update region in the texture at the mipmap level, using the frame pixels
+	 * starting at (sx, sy).  Face is only valid/non-null when data is a cube map.
+	 */
 	protected abstract void copyTextureData(TextureData data, Block region, Face face, int level, int sx, int sy);
 	
+	/**
+	 * Read the current state of the frame buffer into the given buffer.  The pixels are grabbed from the 2D
+	 * region specified by region (depth and zoffset are ignored).  The pixel data is converted to the 
+	 * given type and format.  The in buffer's slice must be contained by the buffer and have the correct size
+	 * and type for an equivalently formatted texture.
+	 */
 	public void readFramePixels(Buffer in, Slice slice, TextureType type, TextureFormat format, Block region) {
 		if (!format.isTypeCompatible(type) || format.isServerCompressed())
 			throw new IllegalArgumentException("Invalid texture type and format");
 		
 		this.readPixels(in, slice, type, format, region);
 	}
+	/**
+	 * As readFramePixels(Buffer ...) except that if the BufferData is a vbo and pixel buffers are supported, then
+	 * it does a transfer completely on the graphics card (much faster).  Otherwise, it reads the pixel data into
+	 * the BufferData's backing buffer (if not null) and then updates the BufferData if necessary.
+	 */
 	public void readFramePixels(BufferData in, Slice slice, TextureType type, TextureFormat format, Block region) {
 		if (!format.isTypeCompatible(type) || format.isServerCompressed())
 			throw new IllegalArgumentException("Invalid texture type and format");
 		BufferData pBD = (BufferData)this.push(in, BufferTarget.PIXEL_READ_BUFFER, BufferData.class);
 		if (in.isVBO() && RenderManager.getSystemCapabilities().arePixelBuffersSupported())
 			this.readPixels(null, slice, type, format, region);
-		else if (in.getData() != null)
+		else if (in.getData() != null) {
 			this.readPixels(in.getData(), slice, type, format, region);
+			in.updateNow(this.getRenderManager());
+		}
 		this.pop(in, pBD, BufferTarget.PIXEL_READ_BUFFER);
 	}
 	
+	/**
+	 * Implementations of RenderContext must implement this method to read pixels (from the region) into the given buffer, and have
+	 * the internal context format it given the type and format.  If buffer is null, then use the already bound
+	 * pixel buffer (but it still must use the slice).
+	 */
 	protected abstract void readPixels(Buffer in, Slice slice, TextureType type, TextureFormat format, Block region);
 	
 	private static TextureFormat getServerCompressedFormat(TextureFormat f) {
 	 * Get the SystemCapabilities of this context, can't return null if isInitialized() returns true.
 	 */
 	public abstract SystemCapabilities getCapabilities();
+	
 	/**
 	 * Destroy the internal context resources for this context.
 	 */
 	public abstract void destroyContext();
+	
 	/**
 	 * Called by RenderManager to tell the context to begin the process of rendering.  When appropriate, the context
 	 * must then call notifyRenderFrame() on this context's manager.
 	 */
 	public abstract void render();
+	
 	/**
 	 * Whether or not this RenderContext is current on the calling thread, i.e. that its appropriate to call internal
 	 * resources or gl functions.
 	 */
 	public abstract boolean isCurrent();
+	
+	/**
+	 * Whether or not this RenderContext has been initialized.  After this returns true, the render context must
+	 * have computed and gathered a valid set of SystemCapabilities and have a valid underlying context.
+	 */
 	public abstract boolean isInitialized();
 	
+	/**
+	 * Sets the active state manager that was used to apply atoms of the given type.  Shouldn't be called
+	 * directly, instead use StateManager's apply().
+	 */
 	public void setActiveStateManager(StateManager man, Class<? extends StateAtom> type) {
 		if (type == null)
 			throw new NullPointerException("Can't have a null type");
 		this.setActiveStateManager(man, index);
 	}
 	
+	/**
+	 * Sets the active state manager that was used to apply atoms with the given dynamic type.  Shouldn't be
+	 * called directly.
+	 */
 	public void setActiveStateManager(StateManager man, int type) {
+		if (man != null && man.getDynamicType() != type)
+			throw new IllegalArgumentException("A non-null state manager's dynamic type doesn't agree with the specified type");
 		if (this.stateRecord == null)
 			this.stateRecord = new StateManager[StateManager.NUM_CORE_STATES];
 		if (this.stateRecord.length <= type) {
 		this.stateRecord[type] = man;
 	}
 	
+	/**
+	 * Get the active state manager for the given type, which can't be null.
+	 */
 	public StateManager getActiveStateManager(Class<? extends StateAtom> type) {
 		if (type == null)
 			throw new NullPointerException("Can't have a null type");
 		return this.getActiveStateManager(index);
 	}
 	
+	/**
+	 * Get the active state manager for the given dynamic type (>= 0).
+	 */
 	public StateManager getActiveStateManager(int dynamicType) {
 		if (this.stateRecord == null || dynamicType >= this.stateRecord.length)
 			return null;
 		return this.stateRecord[dynamicType];
 	}
 	
+	/**
+	 * Set the active state atom for the given class and unit.  This and its variant shouldn't be 
+	 * called directly, instead use the StateAtom's apply() method or restore() method (or RenderPass's
+	 * applyState() method).
+	 */
 	public void setActiveStateAtom(StateAtom atom, Class<? extends StateAtom> type, StateUnit unit) {
 		if (type == null)
 			throw new NullPointerException("Can't have a null type");
 		this.setActiveStateAtom(atom, registerStateAtomType(type), unit.ordinal());
 	}
 	
+	/**
+	 * Set the active state atom for the given type and ordinal value (as returned by a StateUnit, >= 0).
+	 */
 	public void setActiveStateAtom(StateAtom atom, int type, int ordinal) {
+		if (atom != null && atom.getDynamicType() != type)
+			throw new IllegalArgumentException("A non-null state atom must have the same dynamic type");
 		if (this.atomRecord == null)
 			this.atomRecord = new StateAtomTracker[StateManager.NUM_CORE_STATES];
 		if (this.atomRecord.length <= type) {
 		st.stateRecord[ordinal] = atom;
 	}
 	
+	/**
+	 * Get the last applied state atom that is of the given atom type and at the state unit.
+	 * The StateUnit must be valid for the given type of atom.  Null implies that no previous 
+	 * state atom for that type has been applied.
+	 */
 	public StateAtom getActiveStateAtom(Class<? extends StateAtom> type, StateUnit unit) {
 		if (type == null)
 			throw new NullPointerException("Can't have a null type");
 		return this.getActiveStateAtom(registerStateAtomType(type), unit.ordinal());
 	}
 	
+	/**
+	 * Get the last applied state atom that has the given dynamic type and ordinal ->
+	 * number returned by the appropriate StateUnit.  Null implies that no previous 
+	 * state atom for that type has been applied.
+	 */
 	public StateAtom getActiveStateAtom(int dynamicType, int ordinal) {
 		if (this.atomRecord == null || this.atomRecord.length <= dynamicType || this.atomRecord[dynamicType] == null || this.atomRecord[dynamicType].stateRecord.length <= ordinal)
 			return null;
 		return this.atomRecord[dynamicType].stateRecord[ordinal];
 	}
 	
+	/**
+	 * Get the RenderManager that is attached to this RenderContext
+	 */
 	public RenderManager getRenderManager() {
 		return this.manager;
 	}
 
+	/**
+	 * Get the default render pass peer suitable for use in a standard double/single buffered
+	 * setup
+	 */
 	public RenderPassPeer<RenderPass> getDefaultRenderPassPeer() {
 		return this.defaultPass;
 	}
 	
+	/**
+	 * Convenience method that throws an exception if the RenderContext isn't current
+	 */
 	protected void callValidate() {
 		if (!this.isCurrent())
 			throw new FeroxException("Method unavailable when RenderContext isn't current");

src/com/ferox/core/util/InputManager.java

+package com.ferox.core.util;
+
+import java.awt.event.*;
+import java.awt.*;
+import javax.swing.SwingUtilities;
+//FIXME: make this better, cleaner and more interfacy, not permanent
+public class InputManager implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener {
+	public static final int NORMAL=0;
+	public static final int RELATIVE=1;
+	public static final int INITIAL_PRESS=1;
+	public static final int MOVED=0;
+	public static final int DRAGGED=1;
+	public static final int RELEASED=0;
+	public static final int PRESSED=1;
+	public static final int CLICKED=2;
+	public static final int WAITING_FOR_RELEASE=2;
+	
+	private static final int NUM_KEYS=600;
+	private static final int NUM_MOUSE_BUTTONS=3;
+	
+	public static final Cursor INVISIBLE_CURSOR=Toolkit.getDefaultToolkit().createCustomCursor(Toolkit.getDefaultToolkit().getImage(""),new Point(0,0),"invisible");
+	
+	private int keyPressed[][];
+	private int mouseButtonPressed[];
+	private int mouseMotionType=0;
+	private int mouseWheelChange=0;
+	private int mouseXChange=0;
+	private int mouseYChange=0;
+	private int mouseBehavior;
+	private int buttonOnDrag;
+	
+	public static final int centerWheel=2;
+	public static final int leftClick=1;
+	public static final int rightClick=3;
+	
+	private Point mouseLocation;
+	private Point centerLocation;
+	private Point mouseAtButton1[];
+	private Point mouseAtButton2[];
+	private Point mouseAtButton3[];
+	private Point mouseAtWheel;
+	
+	private Component comp;
+	public Robot robot;
+	
+	private boolean isRecentering;
+	
+	public InputManager(Component comp) {
+		this(comp,NORMAL);
+	}
+	
+	public InputManager(Component comp,int behavior) {
+		mouseLocation=new Point();
+		centerLocation=new Point();
+		mouseAtButton1=new Point[3];
+		mouseAtButton2=new Point[3];
+		mouseAtButton3=new Point[3];
+		mouseAtWheel=new Point();
+		
+		for (int i=0;i<3;i++) {
+			mouseAtButton1[i]=new Point();
+			mouseAtButton2[i]=new Point();
+			mouseAtButton3[i]=new Point();
+		}
+		
+		keyPressed=new int[NUM_KEYS][2];
+		mouseButtonPressed=new int[NUM_MOUSE_BUTTONS];
+		setInputComponent(comp);
+		setBehavior(behavior);
+	}
+	
+	public void setInputComponent(Component comp) {
+		if (this.comp!=null) {
+			this.comp.removeKeyListener(this);
+			this.comp.removeMouseListener(this);
+			this.comp.removeMouseMotionListener(this);
+			this.comp.removeMouseWheelListener(this);
+		}
+		this.comp=comp;
+		if (this.comp!=null) {
+			this.comp.addKeyListener(this);
+			this.comp.addMouseListener(this);
+			this.comp.addMouseMotionListener(this);
+			this.comp.addMouseWheelListener(this);
+		}
+	}
+	
+	public void setCursor(Cursor cursor) {
+		comp.setCursor(cursor);
+	}
+	
+	public void setBehavior(int behavior) {
+		if (behavior==0||behavior==1) 
+			mouseBehavior=behavior;
+		else mouseBehavior=0;
+		if (behavior==1) 
+			setRelativeMouseMode(mouseBehavior);
+	}
+	
+	private void setRelativeMouseMode(int mode) {
+		if (mode==isRelativeMouseMode()) 
+			return;
+		if (mode==RELATIVE) {
+				try {
+					robot=new Robot();
+					recenterMouse();
+					setCursor(INVISIBLE_CURSOR);
+				}
+				catch (AWTException ex) {
+					robot=null;
+				}
+		} else {
+			robot=null;
+		}
+	}
+	
+	public int isRelativeMouseMode() {
+		if (robot==null) 
+			return NORMAL;
+		else return RELATIVE;
+	}
+	
+	private synchronized void recenterMouse() {
+		if (robot!=null&&comp.isShowing()) {
+			centerLocation.x=comp.getWidth()/2;
+			centerLocation.y=comp.getHeight()/2;
+			SwingUtilities.convertPointToScreen(centerLocation,comp);
+			isRecentering=true;
+			robot.mouseMove(centerLocation.x,centerLocation.y);
+		}
+		
+	}
+	
+	public void mouseMoved(MouseEvent e) {
+		if (isRecentering&&mouseBehavior==1&&centerLocation.x==e.getX()&&centerLocation.y==e.getY()) {
+			isRecentering=false;
+		} else {
+			mouseXChange=(e.getX())-mouseLocation.x;
+			mouseYChange=(e.getY())-mouseLocation.y;
+			if (mouseBehavior==1)
+				recenterMouse();
+			mouseMotionType=MOVED;
+		}
+		mouseLocation.x=(e.getX());
+		mouseLocation.y=(e.getY());
+		e.consume();
+	}
+	
+	public void mouseDragged(MouseEvent e) {
+		mouseMoved(e);
+		mouseMotionType=DRAGGED;
+	}
+	
+	public void mousePressed(MouseEvent e) {
+		int button=0;
+		
+		switch(e.getButton()) {
+			case MouseEvent.BUTTON1:button=0;mouseAtButton1[0].x=(e.getX());mouseAtButton1[0].y=(e.getY());break;
+			case MouseEvent.BUTTON2:button=1;mouseAtButton2[0].x=(e.getX());mouseAtButton2[0].y=(e.getY());break;
+			case MouseEvent.BUTTON3:button=2;mouseAtButton3[0].x=(e.getX());mouseAtButton3[0].y=(e.getY());break;
+			default:button=0;break;
+		}
+		mouseButtonPressed[button]=PRESSED;
+		buttonOnDrag=e.getButton();
+		e.consume();
+	}
+	
+	public void mouseReleased(MouseEvent e) {
+		int button=0;
+		
+		switch(e.getButton()) {
+			case MouseEvent.BUTTON1:button=0;mouseAtButton1[1].x=(e.getX());mouseAtButton1[1].y=(e.getY());break;
+			case MouseEvent.BUTTON2:button=1;mouseAtButton2[1].x=(e.getX());mouseAtButton2[1].y=(e.getY());break;
+			case MouseEvent.BUTTON3:button=2;mouseAtButton3[1].x=(e.getX());mouseAtButton3[1].y=(e.getY());break;
+			default:button=0;break;
+		}
+		mouseButtonPressed[button]=RELEASED;
+		buttonOnDrag=-1;
+		e.consume();
+	}
+	
+	public void mouseClicked(MouseEvent e) {
+		int button=0;
+		
+		switch(e.getButton()) {
+			case MouseEvent.BUTTON1:button=0;mouseAtButton1[2].x=(e.getX());mouseAtButton1[2].y=(e.getY());break;
+			case MouseEvent.BUTTON2:button=1;mouseAtButton2[2].x=(e.getX());mouseAtButton2[2].y=(e.getY());break;
+			case MouseEvent.BUTTON3:button=2;mouseAtButton3[2].x=(e.getX());mouseAtButton3[2].y=(e.getY());break;
+			
+			default:button=0;break;
+		}
+		mouseButtonPressed[button]=CLICKED;
+		buttonOnDrag=-1;
+		e.consume();
+	}
+	
+	public void mouseEntered(MouseEvent e) {
+		mouseMoved(e);
+	}
+	
+	public void mouseExited(MouseEvent e) {
+		mouseMoved(e);
+	}
+	
+	public void keyPressed(KeyEvent e) {
+		if (e.getKeyCode()>=0&&e.getKeyCode()<600) {
+			if (keyPressed[e.getKeyCode()][0]==RELEASED||keyPressed[e.getKeyCode()][0]!=WAITING_FOR_RELEASE) 
+				keyPressed[e.getKeyCode()][0]=PRESSED;
+		}
+		e.consume();
+	}
+	
+	public void keyReleased(KeyEvent e) {
+		if (e.getKeyCode()>=0&&e.getKeyCode()<600) {
+			keyPressed[e.getKeyCode()][0]=RELEASED;
+		}
+		e.consume();
+	}
+	
+	public void keyTyped(KeyEvent e) {
+		e.consume();
+	}
+	
+	public void mouseWheelMoved(MouseWheelEvent e) {
+		mouseWheelChange=e.getWheelRotation();
+		mouseAtWheel.x=(e.getX());//+(int)currentComp.getBounds().getX());//
+		mouseAtWheel.y=(e.getY());//)+(int)currentComp.getBounds().getY());
+		e.consume();
+	}
+	
+	public boolean isKeyPressed(int keyCode) {
+		if (keyCode>=0&&keyCode<600) {
+			//return (keyPressed[keyCode][0]==PRESSED);
+			if (keyPressed[keyCode][0]==PRESSED&&keyPressed[keyCode][1]==NORMAL)
+				return true;
+			if (keyPressed[keyCode][0]==PRESSED&&keyPressed[keyCode][1]==INITIAL_PRESS) {
+				keyPressed[keyCode][0]=WAITING_FOR_RELEASE;
+				return true;
+			}
+			return false;
+		} else return false;
+	}
+	
+	public boolean isMousePressed(int button) {
+		button--;
+		if (button>=0&&button<3) {
+			if (mouseButtonPressed[button]==PRESSED) {
+				//mouseButtonPressed[button]=RELEASED;
+				return true;
+			} else return false;
+		}
+		return false;
+	}
+	
+	public int getLastMouseX() {
+		return mouseLocation.x;
+	}
+	
+	public int getLastMouseY() {
+		return mouseLocation.y;
+	}
+	
+	public int getMouseAtLastButtonPressX(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[0].x;
+			case 2:return mouseAtButton2[0].x;
+			case 3:return mouseAtButton3[0].x;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastButtonPressY(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[0].y;
+			case 2:return mouseAtButton2[0].y;
+			case 3:return mouseAtButton3[0].y;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastButtonReleaseX(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[1].x;
+			case 2:return mouseAtButton2[1].x;
+			case 3:return mouseAtButton3[1].x;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastButtonReleaseY(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[1].y;
+			case 2:return mouseAtButton2[1].y;
+			case 3:return mouseAtButton3[1].y;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastButtonClickX(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[2].x;
+			case 2:return mouseAtButton2[2].x;
+			case 3:return mouseAtButton3[2].x;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastButtonClickY(int button) {
+		switch(button) {
+			case 1:return mouseAtButton1[2].y;
+			case 2:return mouseAtButton2[2].y;
+			case 3:return mouseAtButton3[2].y;
+			default: return -0;
+		}
+	}
+	
+	public int getMouseAtLastWheelMoveX() {
+		return mouseAtWheel.x;
+	}
+	
+	public int getMouseAtLastWheelMoveY() {
+		return mouseAtWheel.y;
+	}
+
+	public int getMouseXChange() {
+		int mouseX=mouseXChange;
+		mouseXChange=0;
+		if (mouseBehavior==RELATIVE)
+			return -mouseX;
+		return mouseX;
+	}
+	
+	public int getMouseYChange() {
+		int mouseY=mouseYChange;
+		mouseYChange=0;
+		if (mouseBehavior==RELATIVE)
+			return -mouseY;
+		return mouseY;
+	}
+	
+	public int getMouseMotionType() {
+		return mouseMotionType;
+	}
+	
+	public int getWheelChange() {
+		int wheel=mouseWheelChange;
+		mouseWheelChange=0;
+		return wheel;
+	}
+	
+	public int getMouseBehavior() {
+		return mouseBehavior;
+	}
+	
+	public int getKeyBehavior(int keyCode) {
+		if (keyCode>=0&&keyCode<600) {
+			return keyPressed[keyCode][1];
+		} else return -1;
+	}
+	
+	public Component getListeningComponent() {
+		return comp;
+	}
+	
+	public int getButtonOnDrag() {
+		return buttonOnDrag;
+	}
+	
+	public void setKeyBehavior(int keyCode,int behavior) {
+		if (keyCode>=0&&keyCode<600) {
+			if(behavior==0||behavior==1) {
+				keyPressed[keyCode][1]=behavior;
+			}
+		}
+	}
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.