package logEnabledComponents; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.Observer; import javax.swing.JFrame; import javax.swing.WindowConstants; import parser.ExpressionList; import parser.ExpressionNode; import parser.Value; import valueTypes.DecimalValue; import variables.Variable; import actionScript.ActionScriptFlags; import expressionConsole.ExpressionConsoleModel; /** * A JFrame which has all of it's actions logged for later replay. * * @author Curran Kelleher * */ public class LogEnabledJFrame extends JFrame implements ComponentListener, WindowListener, Observer { private static final long serialVersionUID = 4826646701968523374L; /** * The icon image that all newly created frames get. */ public static Image defaultIconImage = null; /** * A list of all names which have been used in LogEnabledJFrames. It is * checked against for every new name to ensure uniqueness. */ private static List<String> allUniqueNames = new LinkedList<String>(); /** * the Variable which will be used to keep track of this frame's parameters. * (the "parameter variable") */ Variable thisFramesParameterVariable; /** * A reference to the console model so getInstance() only needs to be called * once. */ ExpressionConsoleModel console = ExpressionConsoleModel.getInstance(); /** * A flag which is set to false when the update is self-generated (to * prevent an infinite loop) */ boolean respondToUpdates = true; /** * If this is false, logging is disabled for this JFrame. It is here for * constructors and methods to use, so events generated programmatically by * code in the subclass don't get logged as user-generated events would. The * code should set this to false, do the action, then set this to true * again. */ private boolean logEventsForThisFrame = true; /** * For keeping track of the bounds for checking if they have changed. */ Rectangle previousBounds = new Rectangle(); /** * Keeps track of the extended state which is stored in the variable, used * for checking if it has changed. */ int previousExtendedState = -5; /** * Constructs a LogEnabledJFrame whose parameters will be kept track of by * the Variable with the specified name. * * @param uniqueName * the nme of the Variable which will be used to keep track of * this frame's parameters. It must be unique, something long * like "grapher2DGUIFrame" is recommended. */ public LogEnabledJFrame(String uniqueName) { if (allUniqueNames.contains(uniqueName)) console .enterErrorMessage("There already exists a frame with the name " + uniqueName + "! After this, logging and replay may not work correctly."); else allUniqueNames.add(uniqueName); Variable.getVariable(uniqueName).addObserver(this, "Used by a frame for logging it's parameters"); // this is called in this manner because the act of adding the observer // replaces the existing Variable with an ObservableVariable in the // symbol table. thisFramesParameterVariable = Variable.getVariable(uniqueName); addComponentListener(this); addWindowListener(this); // Set the icon if (defaultIconImage != null) setIconImage(defaultIconImage); } /** * Updates the value of the parameters variable with the curren bounds via a * command on the console. * */ private void updateParameters() { if (ActionScriptFlags.LOGGING_ENABLED && logEventsForThisFrame) { Rectangle newBoundsRect = getBounds(); if (!previousBounds.equals(newBoundsRect) || previousExtendedState != getExtendedState()) { // don't respond to self-generated updates. respondToUpdates = false; console.enterExpression(thisFramesParameterVariable.toString() + " = [" + newBoundsRect.x + ", " + newBoundsRect.y + ", " + newBoundsRect.width + ", " + newBoundsRect.height + ", " + getExtendedState() + "]"); // do respond to other updates. respondToUpdates = true; } } } public void componentResized(ComponentEvent e) { updateParameters(); } public void componentMoved(ComponentEvent e) { updateParameters(); } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } /** * Gets updates whenever the parameter variable changes. If the parameter * variable gets set to 0, this frame is closed and disposed of. * * @param o * @param arg */ public void update(Observable o, Object arg) { if (respondToUpdates) if (arg == thisFramesParameterVariable) { boolean invalidValue = false; Value currentValue = thisFramesParameterVariable.evaluate(); // If the parameter variable gets set to 0, this frame is closed // and disposed of. if (currentValue instanceof DecimalValue) if (((DecimalValue) currentValue).value == 0) { allUniqueNames.remove(thisFramesParameterVariable .toString()); o.deleteObserver(this); dispose(); } else invalidValue = true; else if (currentValue instanceof ExpressionList) { ExpressionNode[] allParameters = ((ExpressionList) currentValue) .getNodes(); if (allParameters.length == 5) { int[] parameterValues = new int[5]; for (int i = 0; i < parameterValues.length && !invalidValue; i++) { Value xValue = allParameters[i].evaluate(); if (xValue instanceof DecimalValue) parameterValues[i] = (int) ((DecimalValue) xValue).value; else invalidValue = true; } if (!invalidValue) { // first make sure the values are not negative for (int i = 0; i < parameterValues.length - 1; i++) parameterValues[i] = parameterValues[i] < 1 ? 1 : parameterValues[i]; // ********************************* // get the bounds out of the first 4 Rectangle newBoundsRect = new Rectangle( parameterValues[0], parameterValues[1], parameterValues[2], parameterValues[3]); previousBounds = newBoundsRect; previousExtendedState = parameterValues[4]; if (!newBoundsRect.equals(getBounds())) setBounds(newBoundsRect); // ********************************* // get the extended state out of the 5th if (getExtendedState() != parameterValues[4]) setExtendedState(parameterValues[4]); } } else invalidValue = true; } else invalidValue = true; if (invalidValue) { console .enterErrorMessage(thisFramesParameterVariable .toString() + " is being used by a frame and has been set to an invalid value, " + currentValue + ". It will be reset to the frame's current parameters."); updateParameters(); } } } /* * public void dispose() { * allUniqueNames.remove(thisFramesParameterVariable.toString()); * super.dispose(); } */ /** * Ensure that code which calls setVisible() does not cause events to be * logged. */ public void setVisible(boolean b) { // make sure the events generated by calling // setVisible() are not logged. logEventsForThisFrame = false; super.setVisible(b); // log real events hereafter logEventsForThisFrame = true; } public void windowOpened(WindowEvent e) { } public void windowClosing(WindowEvent e) { // log the destruction of this frame so that it replays properly. if (getDefaultCloseOperation() == WindowConstants.DISPOSE_ON_CLOSE) console.enterExpression(thisFramesParameterVariable.toString() + " = 0"); } public void windowClosed(WindowEvent e) { } public void windowIconified(WindowEvent e) { updateParameters(); } public void windowDeiconified(WindowEvent e) { updateParameters(); } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } }