/*
Part of the GUI library for Processing
http://www.lagers.org.uk/g4p/index.html
http://sourceforge.net/projects/g4p/files/?source=navbar
Copyright (c) 2012 Peter Lager
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package automenta.vivisect.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Method;
import processing.core.PApplet;
import processing.core.PImage;
import processing.event.KeyEvent;
import processing.event.MouseEvent;
/**
* Objects of this class are separate windows which can be used to hold
GUI GUI components or used for drawing or both combined.
* <br><br>
* A number of examples are included in the library and can be found
* at www.lagers.org.uk
*
*
* @author Peter Lager
*
*/
@SuppressWarnings("serial")
public class GWindow extends Frame implements GConstants, GConstantsInternal {
protected PApplet app;
/**
* Gives direct access to the PApplet object inside the frame
*
*/
public GWinApplet papplet;
protected String winName;
public GWinData data;
protected WindowAdapter winAdapt = null;
protected int actionOnClose = KEEP_OPEN;
/** The object to handle the pre event */
protected Object preHandlerObject = null;
/** The method in preHandlerObject to execute */
protected Method preHandlerMethod = null;
/** the name of the method to handle the event */
protected String preHandlerMethodName;
/** The object to handle the draw event */
protected Object drawHandlerObject = null;
/** The method in drawHandlerObject to execute */
protected Method drawHandlerMethod = null;
/** the name of the method to handle the event */
protected String drawHandlerMethodName;
/** The object to handle the key event */
public Object keyHandlerObject = null;
/** The method in keyHandlerObject to execute */
public Method keyHandlerMethod = null;
/** the name of the method to handle the event */
protected String keyHandlerMethodName;
/** The object to handle the mouse event */
public Object mouseHandlerObject = null;
/** The method in mouseHandlerObject to execute */
public Method mouseHandlerMethod = null;
/** the name of the method to handle the event */
protected String mouseHandlerMethodName;
/** The object to handle the post event */
protected Object postHandlerObject = null;
/** The method in postHandlerObject to execute */
protected Method postHandlerMethod = null;
/** the name of the method to handle the event */
protected String postHandlerMethodName;
/** The object to handle the window closing event */
protected Object closeHandlerObject = null;
/** The method in closeHandlerObject to execute */
protected Method closetHandlerMethod = null;
/** the name of the method to handle the event */
protected String closetHandlerMethodName;
/**
* Create a window that can be used to hold GUI components or used
for drawing or both together.
*
* @param theApplet
* @param name
* @param x initial position on the screen
* @param y initial position on the screen
* @param w width of the drawing area (the frame will be bigger to accommodate border)
* @param h height of the drawing area (the frame will be bigger to accommodate border and title bar)
* @param noFrame if true then the frame has no border
* @param mode JAVA2D / P2D / P3D / OPENGL
*/
public GWindow(PApplet theApplet, String name, int x, int y, int w, int h, boolean noFrame, String mode) {
super(name);
winName = name;
windowCtorCore(theApplet, x, y, w, h, null, noFrame, mode, name);
}
/**
*
* @param theApplet
* @param name
* @param x initial position on the screen
* @param y initial position on the screen
* @param image background image (used to size window)
* @param noFrame if true then the frame has no border
* @param mode JAVA2D / OPENGL
*/
public GWindow(PApplet theApplet, String name, int x, int y, PImage image, boolean noFrame, String mode) {
super(name);
windowCtorCore(theApplet, x, y, image.width, image.height, image, noFrame, mode, name);
}
/**
* Core stuff for GWindows ctor
*
* @param x
* @param y
* @param w
* @param h
* @param noFrame
* @param mode
*/
private void windowCtorCore(PApplet theApplet, int x, int y, int w, int h, PImage image, boolean noFrame, String mode, String name){
// If this is the first control to be created then theAapplet must be the sketchApplet
if(GUI.applet == null)
GUI.applet = theApplet;
app = theApplet;
winName = name;
if(mode == null || mode.equals(""))
mode = PApplet.JAVA2D;
papplet = new GWinApplet(mode);
papplet.owner = this;
papplet.frame = this;
// So we can resize the frame to get the sketch canvas size reqd.
papplet.frame.setResizable(true);
// Now set the window width and height
if(image == null){
papplet.appWidth = w;
papplet.appHeight = h;
} else {
papplet.bkImage = image;
papplet.appWidth = image.width;
papplet.appHeight = image.height;
}
papplet.bkColor = papplet.color(180);
// Set the papplet size preferences
papplet.resize(papplet.appWidth, papplet.appHeight);
papplet.setPreferredSize(new Dimension(papplet.appWidth, papplet.appHeight));
papplet.setMinimumSize(new Dimension(papplet.appWidth, papplet.appHeight));
// add the PApplet to the Frame
setLayout(new BorderLayout());
add(papplet, BorderLayout.CENTER);
// ensures that the animation thread is started and
// that other internal variables are properly set.
papplet.init();
// Set the sketch path to the same as the main PApplet object
papplet.sketchPath = theApplet.sketchPath;
// Pack the window, position it and make visible
setUndecorated(noFrame);
pack();
setLocation(x,y);
setVisible(true);
// Make the window always on top
setOnTop(true);
// Make sure we have some data even if not used
data = new GWinData();
data.owner = this;
// Not resizeable if we are using a back image
super.setResizable(image == null);
// Make sure GUI knows about this window
GUI.addWindow(this);
}
/**
* Attempt to create the on-close-window event handler for this GWindow.
* The default event handler is a method that returns void and has a single
* parameter of type GWindow (this will be a reference to the window that is
* closing) <br/>
*
* The handler will <b>not be called</> if the setActionOnClose flag is set
* to EXIT_APP <br/>
* If the flag is set to CLOSE_WINDOW then the handler is called when the window
* is closed by clicking on the window-close-icon or using either the close or
* forceClose methods. <br/>
* If the flag is set to KEEP_OPEN the window can only be closed using the
* forceClose method. In this case the handler will be called.
*
*
* @param obj the object to handle the on-close-window event
* @param methodName the method to execute in the object handler class
*/
public void addOnCloseHandler(Object obj, String methodName){
try{
closeHandlerObject = obj;
closetHandlerMethodName = methodName;
closetHandlerMethod = obj.getClass().getMethod(methodName, new Class<?>[] {GWindow.class } );
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWindow.class } } );
closeHandlerObject = null;
closetHandlerMethodName = "";
}
}
/**
* This method will be called by this windows GWindowCloser object
*/
void onClose(){
if(closeHandlerObject != null){
try {
closetHandlerMethod.invoke(closeHandlerObject, new Object[] { this } );
} catch (Exception e) {
GMessenger.message(EXCP_IN_HANDLER,
new Object[] {closeHandlerObject, closetHandlerMethod, e } );
}
}
}
/**
* Add an object that holds the data this window needs to use.
*
* Note: the object can be of any class that extends GWinData.
*
* @param data
*/
public void addData(GWinData data){
this.data = data;
this.data.owner = this;
}
/**
* Always make this window appear on top of other windows (or not). <br>
* This will not work when run from a remote server (ie Applet over the web)
* for security reasons. In this situation a call to this method is ignored
* and a warning is generated.
*
* @param onTop
*/
public void setOnTop(boolean onTop){
try{
setAlwaysOnTop(onTop);
} catch (Exception e){
if(GUI.showMessages)
System.out.println("Warning: setOnTop() method will not work when the sketch is run from a remote location.");
}
}
/**
* Sets the location of the window. <br>
(Already available from the Frame class - helps visibility
of method in GUI reference)
*/
public void setLocation(int x, int y){
super.setLocation(x,y);
}
/**
* Sets the visibility of the window <br>
(Already available from the Frame class - helps visibility
of method in GUI reference)
*/
public void setVisible(boolean visible){
super.setVisible(visible);
}
/**
* Determines whether the window is resizabale or not. <br>
* This cannot be set to true if a background image is used.
*/
public void setResizable(boolean resizable){
if(resizable == false)
super.setResizable(false);
else {
if(papplet.bkImage == null)
super.setResizable(true);
}
}
/**
* Set the background image to be used instead of a plain color background <br>
* The window will resize to accommodate the image. This will also turn on autoClear.
* @param image
*/
public void setBackground(PImage image){
papplet.noLoop();
papplet.bkImage = null;
super.setResizable(true);
papplet.resize(image.width, image.height);
papplet.bkImage = image;
papplet.appWidth = image.width;
papplet.appHeight = image.height;
papplet.setPreferredSize(new Dimension(papplet.appWidth, papplet.appHeight));
papplet.setMinimumSize(new Dimension(papplet.appWidth, papplet.appHeight));
pack();
super.setResizable(false);
papplet.autoClear = true;
papplet.loop();
}
/**
* Set the background color for the window. This will also turn on autoClear.
*
* @param col
*/
public void setBackground(int col){
papplet.bkColor = col;
papplet.autoClear = true;
}
/**
* Like the draw() method in the main sketch tab the user must include
* the background(...) statement to clear the background. It autoClear is set
* to true then this is done automatically otherwise you may want to add your own
* call to background in the draw handler method. <br>
* If you have called one of the background(...) methods autoClear will be set to true.
*
* @param auto_clear whether to call the background() method or not
*/
public void setAutoClear(boolean auto_clear){
papplet.autoClear = auto_clear;
}
/**
* This sets what happens when the users attempts to close the window. <br>
* There are 3 possible actions depending on the value passed. <br>
* GWindow.KEEP_OPEN - ignore attempt to close window (default action) <br>
* GWindow.CLOSE_WINDOW - close this window, if it is the main window it causes the app to exit <br>
* GWindow.EXIT_APP - exit the app, this will cause all windows to close. <br>
* @param action the required close action
*/
public void setActionOnClose(int action){
switch(action){
case KEEP_OPEN:
removeWindowListener(winAdapt);
winAdapt = null;
actionOnClose = action;
break;
case CLOSE_WINDOW:
case EXIT_APP:
if(winAdapt == null){
winAdapt = new GWindowAdapter(this);
addWindowListener(winAdapt);
} // end if
actionOnClose = action;
break;
} // end switch
}
/**
* Get the action to be performed when the user attempts to close
* the window.
* @return actionOnClose
*/
public int getActionOnClose(){
return actionOnClose;
}
/**
* This method will fire a WindowClosing event to be captured by the
* GWindow$GWindowAdapter object. <br>
* There are 3 possible actions depending on the value passed. <br>
* GWindow.KEEP_OPEN - ignore attempt to close window (default action) <br>
* GWindow.CLOSE_WINDOW - close this window <br>
* GWindow.EXIT_APP - exit the app, this will cause all windows to close. <br>
*/
public void close(){
getToolkit().getSystemEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
/**
* This method guarantees that the window is closed by overriding the KEEP_OPEN action-on-close
* and will fire a WindowClosing event to be captured by the GWindow$GWindowAdapter object. <br>
* There are 2 possible actions depending on the currently specified action-on-close. <br>
* GWindow.KEEP_OPEN - close this window <br>
* GWindow.CLOSE_WINDOW - close this window <br>
* GWindow.EXIT_APP - exit the app, this will cause all windows to close. <br>
*/
public void forceClose(){
if(actionOnClose == KEEP_OPEN)
setActionOnClose(CLOSE_WINDOW);
getToolkit().getSystemEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
/**
* Attempt to add the 'draw' handler method.
* The default event handler is a method that returns void and has two
* parameters PApplet and GWinData
*
* @param obj the object to handle the event
* @param methodName the method to execute in the object handler class
*/
public void addDrawHandler(Object obj, String methodName){
try{
drawHandlerMethod = obj.getClass().getMethod(methodName, new Class<?>[] {GWinApplet.class, GWinData.class } );
drawHandlerObject = obj;
drawHandlerMethodName = methodName;
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWinApplet.class, GWinData.class } } );
}
}
/**
* Attempt to add the 'pre' handler method.
* The default event handler is a method that returns void and has two
* parameters GWinApplet and GWinData
*
* @param obj the object to handle the event
* @param methodName the method to execute in the object handler class
*/
public void addPreHandler(Object obj, String methodName){
try{
preHandlerMethod = obj.getClass().getMethod(methodName, new Class<?>[] {GWinApplet.class, GWinData.class } );
preHandlerObject = obj;
preHandlerMethodName = methodName;
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWinApplet.class, GWinData.class } } );
}
}
/**
* Attempt to add the 'mouse' handler method.
* The default event handler is a method that returns void and has three
* parameters GWinApplet, GWinData and a MouseEvent
*
* @param obj the object to handle the event
* @param methodName the method to execute in the object handler class
*/
public void addMouseHandler(Object obj, String methodName){
try{
mouseHandlerMethod = obj.getClass().getMethod(methodName,
new Class<?>[] {GWinApplet.class, GWinData.class, MouseEvent.class } );
mouseHandlerObject = obj;
mouseHandlerMethodName = methodName;
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWinApplet.class, GWinData.class, MouseEvent.class } } );
}
}
/**
* Attempt to add the 'key' handler method.
* The default event handler is a method that returns void and has three
* parameters GWinApplet, GWinData and a KeyEvent
*
* @param obj the object to handle the event
* @param methodName the method to execute in the object handler class
*/
public void addKeyHandler(Object obj, String methodName){
try{
keyHandlerMethod = obj.getClass().getMethod(methodName,
new Class<?>[] {GWinApplet.class, GWinData.class, KeyEvent.class } );
keyHandlerObject = obj;
keyHandlerMethodName = methodName;
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWinApplet.class, GWinData.class, KeyEvent.class } } );
}
}
/**
* Attempt to add the 'post' handler method.
* The default event handler is a method that returns void and has two
* parameters GWinApplet and GWinData
*
* @param obj the object to handle the event
* @param methodName the method to execute in the object handler class
*/
public void addPostHandler(Object obj, String methodName){
try{
postHandlerMethod = obj.getClass().getMethod(methodName,
new Class<?>[] {GWinApplet.class, GWinData.class } );
postHandlerObject = obj;
postHandlerMethodName = methodName;
} catch (Exception e) {
GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { GWinApplet.class, GWinData.class } } );
}
}
/**
* Window adapter class that remembers the window it belongs to so
* it can be used to mark it for closure if required.
*
* @author Peter Lager
*/
public class GWindowAdapter extends WindowAdapter {
GWindow window;
public GWindowAdapter(GWindow window){
this.window = window;
}
public void windowClosing(WindowEvent evt) {
switch(actionOnClose){
case CLOSE_WINDOW:
window.papplet.noLoop();
GUI.markWindowForClosure(window);
break;
case EXIT_APP:
System.exit(0);
break;
}
}
}
}