/*
* @(#)VirtualWindowUI.java
*
* Copyright (C) 2002-2003 Matt Albrecht
* groboclown@users.sourceforge.net
* http://groboutils.sourceforge.net
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package net.sourceforge.groboutils.uicapture.v1;
import java.awt.Color;
import java.awt.Point;
import java.awt.Window;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Image;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
/**
* This UI part of the VirtualWindow framework is in charge of intercepting
* all Window events related to drawing, in order to keep the invisible
* facade.
* <P>
* There is a problem with the transparency right now. I'm investigating
* a solution. The final (and unwanted) solution is to capture the background
* behind the Window, and display that on {@link #paint( Graphics )} calls.
* <P>
* WARNING: if the screen size is to resize, then this will not work correctly.
*
* @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
* @since Jan 4, 2002
* @version Mar 13, 2002
*/
public class VirtualWindowUI extends Window implements Runnable, FocusListener
{
//-------------------------------------------------------------------------
// Private fields
/**
* @serial
*/
private boolean enableGlass = false;
// Thread-based variables
// Synchronization must be used with care!
private transient Thread runner = null;
private transient Object syncObj = new Object();
private transient boolean doRun = true;
private transient Image background = null;
private static final Color INVISIBLE = new Color( 0, 0, 0, 0 );
private static final int SLEEP_LENGTH = 100;
//-------------------------------------------------------------------------
// Constructors
/**
* Creates a disabled window with no parent.
*/
public VirtualWindowUI( Window owner )
{
super( owner );
// setup Window variables
// setFocusableWindow( true );
setBackground( INVISIBLE );
setForeground( INVISIBLE );
// start the stay-focused thread
/*
this.runner = new Thread( this, "UI Capture always-focused thread" );
this.runner.setDaemon( true );
this.runner.setPriority(
(Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 );
this.runner.start();
*/
// get the window registered in the UI.
setSize( 0, 0 );
super.show();
setGlassEnabled( false );
}
//-------------------------------------------------------------------------
// Public methods
/**
* Sets the inner state for displaying the glass pane. If the pane is
* enabled, then the glass pane will attempt to maximize itself and
* keep itself on the foreground at all costs.
* <P>
* This should be the only method where the inner <tt>enableGlass</tt>
* field is manipulated. Any inner testing against this variable
* needs to be synchronized.
*
* @param on <tt>true</tt> if the glass pane is enabled (active and
* intercepting events), or <tt>false</tt> if is disabled.
*/
public void setGlassEnabled( boolean on )
{
synchronized( this.syncObj )
{
this.enableGlass = on;
if (on)
{
this.syncObj.notifyAll();
maximize();
}
}
}
/**
* Retrieves the current glass enabled state.
*
* @return <tt>true</tt> if the glass pane is enabled (active and
* intercepting events), or <tt>false</tt> if is disabled.
*/
public boolean isGlassEnabled()
{
return this.enableGlass;
}
/**
* Enlarge the window to the size of the screen.
*/
public void maximize()
{
synchronized( this.syncObj )
{
if (this.enableGlass)
{
Rectangle rect = getCoveredScreen();
setLocation( rect.getLocation() );
setSize( rect.getSize() );
}
}
}
/**
* Retrieve the size of the covered window.
*/
public Rectangle getCoveredScreen()
{
return new Rectangle(
new Point( 0, 0 ),
getToolkit().getScreenSize() );
}
/**
* Update the background image. This may set the background to
* <tt>null</tt>.
*/
public void setBackground( Image img )
{
synchronized( this.syncObj )
{
this.background = img;
}
}
//-------------------------------------------------------------------------
// Inherited methods
/**
*
*/
public void show()
{
setGlassEnabled( true );
super.show();
toFront();
}
/**
*
*/
public void hide()
{
setGlassEnabled( false );
toBack();
super.hide();
}
/**
*
*/
public void dispose()
{
if (this.runner != null)
{
this.doRun = false;
synchronized( this.syncObj )
{
setGlassEnabled( false );
this.syncObj.notifyAll();
}
// wait for thread to die.
try
{
this.runner.join( 1000 );
this.runner = null;
}
catch (InterruptedException ie)
{
// !!!
// ignore for now.
}
this.background = null;
}
super.dispose();
}
/**
*
*/
public void update( Graphics g )
{
// do nothing
}
/**
*
*/
public void paint( Graphics g )
{
// Fill the background with the transparent color.
synchronized( this.syncObj )
{
if (this.background == null)
{
// g.setColor( INVISIBLE );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
else
{
g.drawImage( this.background, 0, 0, this );
}
}
}
/**
*
public void repaint()
{
// do nothing
}
*/
//-------------------------------------------------------------------------
// Event methods
/**
* Thread runner to keep the window in the front.
*/
public void run()
{
while (this.doRun)
{
boolean doToFront = false;
try
{
synchronized( this.syncObj )
{
if (this.enableGlass == false)
{
// pause
this.syncObj.wait();
}
else
{
doToFront = true;
}
}
if (doToFront)
{
// send to the foreground
toFront();
// sleep for a bit
Thread.sleep( SLEEP_LENGTH );
}
}
catch (InterruptedException ie)
{
// *must* be something important!
break;
}
}
}
/**
*
*/
public void focusGained( FocusEvent fe )
{
// do nothing
}
/**
*
*/
public void focusLost( FocusEvent fe )
{
// bring us back to the front.
synchronized( this.syncObj )
{
if (this.enableGlass)
{
toFront();
}
}
}
//-------------------------------------------------------------------------
// Protected methods
/**
* @exception Throwable thrown if the super throws anything.
*/
protected void finalize() throws Throwable
{
if (this.runner != null)
{
dispose();
}
super.finalize();
}
}
|