001: /*
002: * @(#)VirtualWindow.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.uicapture.v1;
028:
029: import java.awt.Robot;
030: import java.awt.Frame;
031: import java.awt.Rectangle;
032: import java.awt.image.BufferedImage;
033: import java.awt.event.KeyListener;
034: import java.awt.event.MouseListener;
035: import java.awt.event.MouseMotionListener;
036: import java.awt.event.MouseWheelListener;
037: import java.awt.event.MouseEvent;
038: import java.awt.event.KeyEvent;
039: import java.awt.event.MouseWheelEvent;
040:
041: import java.util.LinkedList;
042: import java.util.Iterator;
043:
044: import net.sourceforge.groboutils.uicapture.v1.event.ICaptureListener;
045: import net.sourceforge.groboutils.uicapture.v1.event.CaptureEvent;
046: import net.sourceforge.groboutils.uicapture.v1.event.KeyPressedCaptureEvent;
047: import net.sourceforge.groboutils.uicapture.v1.event.KeyReleasedCaptureEvent;
048: import net.sourceforge.groboutils.uicapture.v1.event.MousePressedCaptureEvent;
049: import net.sourceforge.groboutils.uicapture.v1.event.MouseReleasedCaptureEvent;
050: import net.sourceforge.groboutils.uicapture.v1.event.MouseMovedCaptureEvent;
051: import net.sourceforge.groboutils.uicapture.v1.event.MouseWheelCaptureEvent;
052: import net.sourceforge.groboutils.uicapture.v1.event.IAllowCapturePassThroughListener;
053:
054: /**
055: * A window which covers the whole screen, and does not paint in the background.
056: * It captures keyboard and mouse events, and sends them to both all registered
057: * listeners, and to the underlying GUI as well. This transparent window is
058: * similar to the "glass pane" concept in Swing JFrames.
059: * <P>
060: * For the moment, there is no way for listeners to prevent an event from
061: * being passed to the underlying UI. This needs to be changed.
062: * <P>
063: * WARNING: if the screen size is to resize, then this will not work correctly.
064: *
065: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
066: * @since Jan 4, 2002
067: * @version Mar 13, 2002
068: */
069: public class VirtualWindow implements KeyListener, MouseListener,
070: MouseMotionListener, MouseWheelListener {
071: //-------------------------------------------------------------------------
072: // Private fields
073:
074: private LinkedList captureListeners = new LinkedList();
075: private Robot robot = null;
076: private VirtualWindowUI window = null;
077: private boolean enableGlass = true;
078: private Frame owningFrame = null;
079:
080: private static final String DEFAULT_TITLE = "UI Capture";
081:
082: //-------------------------------------------------------------------------
083: // Constructors
084:
085: /**
086: * Create a new VirtualWindow, with the glass enabled.
087: *
088: * @exception java.awt.AWTException thrown if a Robot is not supported
089: * in the current JDK implementation.
090: */
091: public VirtualWindow() throws java.awt.AWTException {
092: this (null, true);
093: }
094:
095: /**
096: * Specify the initial enabled state of the window.
097: *
098: * @param enable set to the initial glass pane state.
099: * @exception java.awt.AWTException thrown if a Robot is not supported
100: * in the current JDK implementation.
101: */
102: public VirtualWindow(String title, boolean enable)
103: throws java.awt.AWTException {
104: if (title == null) {
105: title = DEFAULT_TITLE;
106: }
107: this .owningFrame = new Frame(title);
108: this .owningFrame.setSize(0, 0);
109: // this.owningFrame.show();
110: // this.owningFrame.hide();
111: this .window = new VirtualWindowUI(this .owningFrame);
112: this .robot = new Robot();
113:
114: this .window.addKeyListener(this );
115: this .window.addMouseListener(this );
116: this .window.addMouseMotionListener(this );
117: this .window.addMouseWheelListener(this );
118:
119: update();
120:
121: setGlassEnabled(enable);
122: }
123:
124: //-------------------------------------------------------------------------
125: // Public methods
126:
127: /**
128: * Close out all inner instances and shut down the UI
129: */
130: public void dispose() {
131: this .window.removeKeyListener(this );
132: this .window.removeMouseListener(this );
133: this .window.removeMouseMotionListener(this );
134: this .window.removeMouseWheelListener(this );
135:
136: this .window.dispose();
137: this .owningFrame.dispose();
138: this .captureListeners.clear();
139: this .robot = null;
140: this .window = null;
141: }
142:
143: /**
144: *
145: * @return the inner Window reference.
146: */
147: public VirtualWindowUI getWindow() {
148: return this .window;
149: }
150:
151: /**
152: * Sets the inner state for displaying the glass pane. If the pane is
153: * enabled, then the glass pane will attempt to maximize itself and
154: * keep itself on the foreground at all costs.
155: *
156: * @param on <tt>true</tt> if the glass pane is enabled (active and
157: * intercepting events), or <tt>false</tt> if is disabled.
158: */
159: public synchronized void setGlassEnabled(boolean on) {
160: this .enableGlass = on;
161: this .window.setGlassEnabled(on);
162: }
163:
164: /**
165: * Retrieves the current glass enabled state.
166: *
167: * @return <tt>true</tt> if the glass pane is enabled (active and
168: * intercepting events), or <tt>false</tt> if is disabled.
169: */
170: public boolean isGlassEnabled() {
171: return this .enableGlass;
172: }
173:
174: /**
175: * Simulates the given captured event. This minimizes the glass window,
176: * performs the event with the Robot, and restores the glass window if
177: * the glass pane is enabled.
178: *
179: * @param ce the event to simulate.
180: */
181: public synchronized void simulateEvent(CaptureEvent ce) {
182: hide();
183:
184: // ensure that the window is restored, no matter the exception.
185: try {
186: ce.performEvent(this .robot);
187: } finally {
188: show();
189: }
190: }
191:
192: /**
193: * Sleeps for the specified number of milliseconds. This is passed
194: * directly through to the underlying Robot.
195: *
196: * @param ms Time to sleep in milliseconds.
197: * @exception IllegalArgumentException thrown by Robot if <tt>ms</tt> is
198: * not between 0 and 60,000, inclusive.
199: */
200: public void delay(int ms) {
201: this .robot.delay(ms);
202: }
203:
204: /**
205: * Waits until all events currently on the event queue have been processed.
206: * This is passed directly through to the underlying Robot.
207: */
208: public void waitForIdle() {
209: this .robot.waitForIdle();
210: }
211:
212: /**
213: * Scrapes the current screen into a BufferedImage the same size as the
214: * window.
215: *
216: * @return the captured screen image.
217: */
218: public BufferedImage createScreenScrape() {
219: return createScreenScrape(this .window.getCoveredScreen());
220: }
221:
222: /**
223: * Scrapes the current screen into a BufferedImage from the given area
224: * on the screen. This is passed directly to the underlying Robot.
225: *
226: * @return the captured screen image.
227: */
228: public BufferedImage createScreenScrape(Rectangle bounds) {
229: // check bounds against the window's bounds
230: if (bounds == null
231: || !this .window.getCoveredScreen().contains(bounds)) {
232: throw new IllegalArgumentException("Bounds " + bounds
233: + " is not within the screen dimension.");
234: }
235:
236: // ensure the window is hidden, to get the current screen image,
237: // then restore the window, no matter the thrown exceptions.
238: hide();
239:
240: try {
241: return this .robot.createScreenCapture(bounds);
242: } finally {
243: show();
244: }
245: }
246:
247: /**
248: * Adds an <tt>ICaptureListener</tt> to the list of recipients of input
249: * events. If the given listener is <tt>null</tt>, then the request is
250: * ignored.
251: *
252: * @param cl the listener to add.
253: */
254: public void addCaptureListener(ICaptureListener cl) {
255: if (cl != null) {
256: this .captureListeners.add(cl);
257: }
258: }
259:
260: /**
261: * Removes the given <tt>ICaptureListener</tt> from the inner list of
262: * input events recipients. If the given listener is <tt>null</tt> or
263: * not registered, then the request is ignored.
264: *
265: * @param cl the listener to remove.
266: */
267: public void removeCaptureListener(ICaptureListener cl) {
268: if (cl != null) {
269: this .captureListeners.remove(cl);
270: }
271: }
272:
273: /**
274: * Hides the glass pane, and stops all input event capturing. This is
275: * only executed if the glass is enabled, and has no effect on the
276: * enabled state of the glass.
277: */
278: public void hide() {
279: if (this .enableGlass) {
280: this .window.hide();
281: }
282: }
283:
284: /**
285: * Shows the glass pane, and continues all input event capturing. This is
286: * only executed if the glass is enabled, and has no effect on the
287: * enabled state of the glass.
288: */
289: public void show() {
290: if (this .enableGlass) {
291: this .window.show();
292: }
293: }
294:
295: /**
296: * Updates the background image.
297: */
298: public void update() {
299: this .window.setBackground(createScreenScrape());
300: }
301:
302: //-------------------------------------------------------------------------
303: // Event methods
304:
305: /**
306: * @see java.awt.event.MouseWheelListener
307: */
308: public void mouseWheelMoved(MouseWheelEvent me) {
309: MouseWheelCaptureEvent ce = new MouseWheelCaptureEvent(me);
310: Iterator iter = getCaptureListeners();
311: boolean allow = true;
312: while (iter.hasNext()) {
313: ICaptureListener icl = (ICaptureListener) iter.next();
314: if (icl instanceof IAllowCapturePassThroughListener) {
315: if (!((IAllowCapturePassThroughListener) icl)
316: .allowMouseWheelMoved(ce)) {
317: allow = false;
318: }
319: }
320: icl.mouseWheelMoved(ce);
321: }
322: if (allow) {
323: simulateEvent(ce);
324: }
325: }
326:
327: /**
328: * @see java.awt.event.MouseMotionListener
329: */
330: public void mouseDragged(MouseEvent me) {
331: // ignore this method
332: }
333:
334: /**
335: * @see java.awt.event.MouseMotionListener
336: */
337: public void mouseMoved(MouseEvent me) {
338: MouseMovedCaptureEvent ce = new MouseMovedCaptureEvent(me);
339:
340: // do not need to simulate event: the mouse already moved.
341: // simulateEvent( ce );
342:
343: Iterator iter = getCaptureListeners();
344: while (iter.hasNext()) {
345: ((ICaptureListener) iter.next()).mouseMoved(ce);
346: }
347: }
348:
349: /**
350: * @see java.awt.event.MouseListener
351: */
352: public void mousePressed(MouseEvent me) {
353: MousePressedCaptureEvent ce = new MousePressedCaptureEvent(me);
354: Iterator iter = getCaptureListeners();
355: boolean allow = true;
356: while (iter.hasNext()) {
357: ICaptureListener icl = (ICaptureListener) iter.next();
358: if (icl instanceof IAllowCapturePassThroughListener) {
359: if (!((IAllowCapturePassThroughListener) icl)
360: .allowMousePressed(ce)) {
361: allow = false;
362: }
363: }
364: icl.mousePressed(ce);
365: }
366: if (allow) {
367: simulateEvent(ce);
368: }
369: }
370:
371: /**
372: * @see java.awt.event.MouseListener
373: */
374: public void mouseReleased(MouseEvent me) {
375: MouseReleasedCaptureEvent ce = new MouseReleasedCaptureEvent(me);
376: Iterator iter = getCaptureListeners();
377: boolean allow = true;
378: while (iter.hasNext()) {
379: ICaptureListener icl = (ICaptureListener) iter.next();
380: if (icl instanceof IAllowCapturePassThroughListener) {
381: if (!((IAllowCapturePassThroughListener) icl)
382: .allowMouseReleased(ce)) {
383: allow = false;
384: }
385: }
386: icl.mouseReleased(ce);
387: }
388: if (allow) {
389: simulateEvent(ce);
390: }
391: }
392:
393: /**
394: * @see java.awt.event.MouseListener
395: */
396: public void mouseClicked(MouseEvent me) {
397: // ignore this method
398: }
399:
400: /**
401: * @see java.awt.event.MouseListener
402: */
403: public void mouseEntered(MouseEvent me) {
404: // ignore this method
405: }
406:
407: /**
408: * @see java.awt.event.MouseListener
409: */
410: public void mouseExited(MouseEvent me) {
411: // ignore this method
412: }
413:
414: /**
415: * @see java.awt.event.KeyListener
416: */
417: public void keyTyped(KeyEvent me) {
418: // ignore this method
419: }
420:
421: /**
422: * @see java.awt.event.KeyListener
423: */
424: public void keyPressed(KeyEvent ke) {
425: KeyPressedCaptureEvent ce = new KeyPressedCaptureEvent(ke);
426: Iterator iter = getCaptureListeners();
427: boolean allow = true;
428: while (iter.hasNext()) {
429: ICaptureListener icl = (ICaptureListener) iter.next();
430: if (icl instanceof IAllowCapturePassThroughListener) {
431: if (!((IAllowCapturePassThroughListener) icl)
432: .allowKeyPressed(ce)) {
433: allow = false;
434: }
435: }
436: icl.keyPressed(ce);
437: }
438: if (allow) {
439: simulateEvent(ce);
440: }
441: }
442:
443: /**
444: * @see java.awt.event.KeyListener
445: */
446: public void keyReleased(KeyEvent ke) {
447: KeyReleasedCaptureEvent ce = new KeyReleasedCaptureEvent(ke);
448: Iterator iter = getCaptureListeners();
449: boolean allow = true;
450: while (iter.hasNext()) {
451: ICaptureListener icl = (ICaptureListener) iter.next();
452: if (icl instanceof IAllowCapturePassThroughListener) {
453: if (!((IAllowCapturePassThroughListener) icl)
454: .allowKeyReleased(ce)) {
455: allow = false;
456: }
457: }
458: icl.keyReleased(ce);
459: }
460: if (allow) {
461: simulateEvent(ce);
462: }
463: }
464:
465: //-------------------------------------------------------------------------
466: // Protected methods
467:
468: /**
469: * Returns a list of all the current ICaptureListeners.
470: *
471: * @return an iterator of the listeners.
472: */
473: protected Iterator getCaptureListeners() {
474: return this.captureListeners.iterator();
475: }
476: }
|