001: /*
002: * @(#)VirtualWindowUI.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.Color;
030: import java.awt.Point;
031: import java.awt.Window;
032: import java.awt.Graphics;
033: import java.awt.Rectangle;
034: import java.awt.Image;
035: import java.awt.event.FocusListener;
036: import java.awt.event.FocusEvent;
037:
038: /**
039: * This UI part of the VirtualWindow framework is in charge of intercepting
040: * all Window events related to drawing, in order to keep the invisible
041: * facade.
042: * <P>
043: * There is a problem with the transparency right now. I'm investigating
044: * a solution. The final (and unwanted) solution is to capture the background
045: * behind the Window, and display that on {@link #paint( Graphics )} calls.
046: * <P>
047: * WARNING: if the screen size is to resize, then this will not work correctly.
048: *
049: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
050: * @since Jan 4, 2002
051: * @version Mar 13, 2002
052: */
053: public class VirtualWindowUI extends Window implements Runnable,
054: FocusListener {
055: //-------------------------------------------------------------------------
056: // Private fields
057:
058: /**
059: * @serial
060: */
061: private boolean enableGlass = false;
062:
063: // Thread-based variables
064: // Synchronization must be used with care!
065: private transient Thread runner = null;
066: private transient Object syncObj = new Object();
067: private transient boolean doRun = true;
068:
069: private transient Image background = null;
070:
071: private static final Color INVISIBLE = new Color(0, 0, 0, 0);
072: private static final int SLEEP_LENGTH = 100;
073:
074: //-------------------------------------------------------------------------
075: // Constructors
076:
077: /**
078: * Creates a disabled window with no parent.
079: */
080: public VirtualWindowUI(Window owner) {
081: super (owner);
082:
083: // setup Window variables
084: // setFocusableWindow( true );
085: setBackground(INVISIBLE);
086: setForeground(INVISIBLE);
087:
088: // start the stay-focused thread
089: /*
090: this.runner = new Thread( this, "UI Capture always-focused thread" );
091: this.runner.setDaemon( true );
092: this.runner.setPriority(
093: (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 );
094: this.runner.start();
095: */
096:
097: // get the window registered in the UI.
098: setSize(0, 0);
099: super .show();
100: setGlassEnabled(false);
101: }
102:
103: //-------------------------------------------------------------------------
104: // Public methods
105:
106: /**
107: * Sets the inner state for displaying the glass pane. If the pane is
108: * enabled, then the glass pane will attempt to maximize itself and
109: * keep itself on the foreground at all costs.
110: * <P>
111: * This should be the only method where the inner <tt>enableGlass</tt>
112: * field is manipulated. Any inner testing against this variable
113: * needs to be synchronized.
114: *
115: * @param on <tt>true</tt> if the glass pane is enabled (active and
116: * intercepting events), or <tt>false</tt> if is disabled.
117: */
118: public void setGlassEnabled(boolean on) {
119: synchronized (this .syncObj) {
120: this .enableGlass = on;
121:
122: if (on) {
123: this .syncObj.notifyAll();
124: maximize();
125: }
126: }
127: }
128:
129: /**
130: * Retrieves the current glass enabled state.
131: *
132: * @return <tt>true</tt> if the glass pane is enabled (active and
133: * intercepting events), or <tt>false</tt> if is disabled.
134: */
135: public boolean isGlassEnabled() {
136: return this .enableGlass;
137: }
138:
139: /**
140: * Enlarge the window to the size of the screen.
141: */
142: public void maximize() {
143: synchronized (this .syncObj) {
144: if (this .enableGlass) {
145: Rectangle rect = getCoveredScreen();
146: setLocation(rect.getLocation());
147: setSize(rect.getSize());
148: }
149: }
150: }
151:
152: /**
153: * Retrieve the size of the covered window.
154: */
155: public Rectangle getCoveredScreen() {
156: return new Rectangle(new Point(0, 0), getToolkit()
157: .getScreenSize());
158: }
159:
160: /**
161: * Update the background image. This may set the background to
162: * <tt>null</tt>.
163: */
164: public void setBackground(Image img) {
165: synchronized (this .syncObj) {
166: this .background = img;
167: }
168: }
169:
170: //-------------------------------------------------------------------------
171: // Inherited methods
172:
173: /**
174: *
175: */
176: public void show() {
177: setGlassEnabled(true);
178:
179: super .show();
180: toFront();
181: }
182:
183: /**
184: *
185: */
186: public void hide() {
187: setGlassEnabled(false);
188: toBack();
189: super .hide();
190: }
191:
192: /**
193: *
194: */
195: public void dispose() {
196: if (this .runner != null) {
197: this .doRun = false;
198: synchronized (this .syncObj) {
199: setGlassEnabled(false);
200: this .syncObj.notifyAll();
201: }
202:
203: // wait for thread to die.
204: try {
205: this .runner.join(1000);
206: this .runner = null;
207: } catch (InterruptedException ie) {
208: // !!!
209: // ignore for now.
210: }
211: this .background = null;
212: }
213:
214: super .dispose();
215: }
216:
217: /**
218: *
219: */
220: public void update(Graphics g) {
221: // do nothing
222: }
223:
224: /**
225: *
226: */
227: public void paint(Graphics g) {
228: // Fill the background with the transparent color.
229: synchronized (this .syncObj) {
230: if (this .background == null) {
231: // g.setColor( INVISIBLE );
232: g.fillRect(0, 0, getWidth(), getHeight());
233: } else {
234: g.drawImage(this .background, 0, 0, this );
235: }
236: }
237: }
238:
239: /**
240: *
241: public void repaint()
242: {
243: // do nothing
244: }
245: */
246:
247: //-------------------------------------------------------------------------
248: // Event methods
249:
250: /**
251: * Thread runner to keep the window in the front.
252: */
253: public void run() {
254: while (this .doRun) {
255: boolean doToFront = false;
256: try {
257: synchronized (this .syncObj) {
258: if (this .enableGlass == false) {
259: // pause
260: this .syncObj.wait();
261: } else {
262: doToFront = true;
263: }
264: }
265: if (doToFront) {
266: // send to the foreground
267: toFront();
268:
269: // sleep for a bit
270: Thread.sleep(SLEEP_LENGTH);
271: }
272: } catch (InterruptedException ie) {
273: // *must* be something important!
274: break;
275: }
276: }
277: }
278:
279: /**
280: *
281: */
282: public void focusGained(FocusEvent fe) {
283: // do nothing
284: }
285:
286: /**
287: *
288: */
289: public void focusLost(FocusEvent fe) {
290: // bring us back to the front.
291: synchronized (this .syncObj) {
292: if (this .enableGlass) {
293: toFront();
294: }
295: }
296: }
297:
298: //-------------------------------------------------------------------------
299: // Protected methods
300:
301: /**
302: * @exception Throwable thrown if the super throws anything.
303: */
304: protected void finalize() throws Throwable {
305: if (this.runner != null) {
306: dispose();
307: }
308:
309: super.finalize();
310: }
311:
312: }
|