package grapher2D;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.BoxLayout;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import logEnabledComponents.LogEnabledJFrame;
import parser.RecursiveDescentParser;
import parser.Value;
import valueTypes.BooleanValue;
import variableEditorUI.EditVariablesMenuPanel;
import variables.Variable;
import actionScript.ActionScriptLoadUtilities;
import actionScript.ActionScriptSaveUtilities;
import expressionConsole.ExpressionConsoleHistory;
import expressionConsole.ExpressionConsoleHistoryEntry;
import expressionConsole.ExpressionConsoleModel;
/**
* The GUI for the 2D Grapher.
*
* @author Curran Kelleher
*
*/
public class Grapher2DGUI extends LogEnabledJFrame implements ActionListener {
private static final long serialVersionUID = 7205295946430619788L;
/**
* A reference to the singleton console. (so we don't need call
* ExpressionConsoleModel.getInstance() more than once)
*/
ExpressionConsoleModel console = ExpressionConsoleModel.getInstance();
/**
* The panel which is displayed in the menu for editing veriables
*/
private EditVariablesMenuPanel editVariablesMenuPanel = new EditVariablesMenuPanel();
/**
* The controller part of the GUI.
*/
Grapher2DController controller;
/**
* The view part of the GUI.
*/
Grapher2DView view;
/**
* Upon construction, the bounds are set such that the frame will have the
* specified Dimension and will be centered on the screen.
*
* @param frameSize
* the size of the frame
*/
public Grapher2DGUI(Dimension frameSize) {
// initialize the GUI frame as a LogEnabledJFrame whose parameters will
// be kept in the Variable named grapher2DGUIFrame
super("grapher2DGUIFrame");
// initialize the animation variable to be true
Variable.getVariable(Grapher2DConstants.Grapher2DAnimateFlag).set(
new BooleanValue(true));
// set up the controller and view
// the Model part of the MVC paradigm exists as a collection of
// variables within the parser inside of the singleton
// ExpressionConsoleModel
controller = new Grapher2DController();
view = new Grapher2DView();
// set the initial function string
// the function string variable is being observed by the view and
// controller, and as a result of this code execution will be
// appropriately notified.
ExpressionConsoleModel
.getInstance()
.getParser()
.parse(
Grapher2DConstants.Grapher2DFunctionString
+ "= \"a = sin(t/5); y = x>0?(sin(x*10-t)+3*sin(x/4+t)+2*sin(x-t))*a:sin(x+t)*x*a\"")
.evaluate();
// SET UP THE GUI
// this panel will contain a split pane, which will contain the entire
// GUI.
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.X_AXIS));
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
controller, view);
splitPane.setDividerSize(3);
splitPane.setEnabled(false);
content.add(splitPane);
// put the content panel into the frame.
getContentPane().add(content);
// set up the menu bar
setJMenuBar(createMenuBar());
// Set the icon
setIconImage(Toolkit.getDefaultToolkit().getImage("icon.gif"));
// set the title
setTitle("2D Grapher");
// set the frame to dispose itself upon closing
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// set the bounds to center the frame with the given dimensions
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(new Rectangle(new Point(screenSize.width / 2
- frameSize.width / 2, screenSize.height / 2 - frameSize.height
/ 2), frameSize));
// set up the listener so the animation pauses when the frame is
// minimized
addWindowListener(new WindowAdapter() {
/**
* Called when the window is minimized
*/
public void windowIconified(WindowEvent e) {
// pause the animation by setting the flag in the model.
Variable.getVariable(Grapher2DConstants.Grapher2DAnimateFlag)
.set(new BooleanValue(false));
}
/**
* Called when the window is restored
*/
public void windowDeiconified(WindowEvent e) {
// start the animation again by setting the flag in the model.
Variable.getVariable(Grapher2DConstants.Grapher2DAnimateFlag)
.set(new BooleanValue(true));
}
});
}
/**
* Upon disposal, stop the view's Thread
*/
public void dispose() {
if (view != null)
view.stopThread = true;
view = null;
controller = null;
super.dispose();
}
/**
* Constructs the menu bar.
*
* @return the menu bar.
*/
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
menuBar.add(createFileMenu());
menuBar.add(createExamplesMenu());
menuBar.add(createEditVariableMenu());
menuBar.add(createAnimationMenu());
return (menuBar);
}
/**
* Creates the File menu
*
* @return
*/
private JMenu createFileMenu() {
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic('f');
fileMenu.add(createSaveHistoryMenuItem());
fileMenu.add(createLoadHistoryMenuItem());
return fileMenu;
}
/**
* Creates the "Load History" menu item.
*
* @return
*/
private JMenuItem createLoadHistoryMenuItem() {
JMenuItem loadHistory = new JMenuItem("Load History");
loadHistory.setMnemonic('l');
loadHistory.setVisible(true);
loadHistory.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// stop animation temporarily if it was on
Variable animateVar = Variable
.getVariable(Grapher2DConstants.Grapher2DAnimateFlag);
animateVar.set(new BooleanValue(false));
// load the script
ExpressionConsoleHistory history = ActionScriptLoadUtilities
.promptUserToLoadScript();
Map<String, String> initialSymbolTable = history
.getInitialSymbolTableSnapshot();
Map<String, Variable> currentSymbolTable = Variable
.getSymbolTable();
ExpressionConsoleModel console = ExpressionConsoleModel
.getInstance();
RecursiveDescentParser parser = console.getParser();
// initialize the symbol table
for (Iterator<Entry<String, Variable>> it = currentSymbolTable
.entrySet().iterator(); it.hasNext();) {
Entry<String, Variable> currentEntry = it.next();
String newValue = initialSymbolTable.get(currentEntry
.getKey());
String expressionToEvaluate = currentEntry.getKey() + " = "
+ (newValue == null ? "0" : newValue);
parser.parse(expressionToEvaluate).evaluate();
}
// step through all events
List<ExpressionConsoleHistoryEntry> historyList = history
.getExpressionList();
for (Iterator<ExpressionConsoleHistoryEntry> it = historyList
.iterator(); it.hasNext();) {
ExpressionConsoleHistoryEntry currentEntry = it.next();
if (currentEntry.getType() == ExpressionConsoleHistoryEntry.INPUT)
console.enterExpression(currentEntry.getString());
}
// restart animation if it was on before
// animateVar.set(oldAnimateValue);
}
});
return loadHistory;
}
/**
* Creates the "Save History" menu item.
*
* @return
*/
private JMenuItem createSaveHistoryMenuItem() {
JMenuItem saveHistory = new JMenuItem("Save History");
saveHistory.setMnemonic('s');
saveHistory.setVisible(true);
saveHistory.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// stop animation temporarily if it was on
Variable animateVar = Variable
.getVariable(Grapher2DConstants.Grapher2DAnimateFlag);
Value oldAnimateValue = animateVar.evaluate();
animateVar.set(new BooleanValue(false));
// save the script
ActionScriptSaveUtilities.promptUserToSaveScript();
// restart animation if it was on before
animateVar.set(oldAnimateValue);
}
});
return saveHistory;
}
/**
* Creates the "Animation" menu.
*
* @return
*/
private JMenu createAnimationMenu() {
JMenu animateMenu = new JMenu("Animation");
animateMenu.setMnemonic('a');
JMenuItem animationSettings = new JMenuItem("Animation Settings");
animationSettings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ExpressionConsoleModel.getInstance().enterExpression(
"edit(" + Grapher2DConstants.Grapher2DAnimateFlag + ","
+ Grapher2DConstants.TimeIncrement + ")");
}
});
// animationSettings.setVisible(true);
animateMenu.add(animationSettings);
return animateMenu;
}
/**
* Creates the "Edit Variables" menu.
*
* @return
*/
private JMenu createEditVariableMenu() {
JMenu editVariablesMenu = new JMenu("Edit Variables");
editVariablesMenu.setMnemonic('v');
editVariablesMenu.add(editVariablesMenuPanel);
return editVariablesMenu;
}
/**
* Creates the Examples menu.
*
* @return
*/
private JMenu createExamplesMenu() {
JMenu examplesMenu = new JMenu("Examples");
examplesMenu.setMnemonic('e');
examplesMenu.add(constructExampleMenuItem("Simple sine wave",
"y = sin(x)", 's'));
examplesMenu.add(constructExampleMenuItem("Time", "y = sin(x+t)", 't'));
examplesMenu.add(constructExampleMenuItem("Variables",
"a = sin(4*x); b = 2*sin(x/2);y = a+b", 'v'));
examplesMenu.add(constructExampleMenuItem("Binary Operators",
"y = 1+2-3*4/5^6E-7%8!", 'b'));
examplesMenu
.add(constructExampleMenuItem(
"Trigonometry",
"y = sin(3)+cos(5)+tan(5)+cot(.5)+sec(.5)+csc(.5)+asin(0.5)+acos(.5)+atan(.5)",
't'));
examplesMenu
.add(constructExampleMenuItem(
"Other Operators",
"y = ln(5)+log(1000)+abs(-5)+sqrt(25)+round(6.5)+floor(6.6)+ceil(6.4)-30",
'o'));
examplesMenu.add(constructExampleMenuItem("Constants",
"y = pi+e+1/Infinity", 'o'));
examplesMenu.add(constructExampleMenuItem("Conditional",
"y = x<1?5^x:5", 'n'));
examplesMenu.add(constructExampleMenuItem("Boolean Algebra",
"y = (x<1&&x>-1)||x>8?5:-5", 'b'));
examplesMenu
.add(constructExampleMenuItem(
"Magical Mathematics",
"a = .7;y = sin(x*5*(1+sin(t/8))-t);b = sin(a+x/2+t/4);y = y<b?y*10:b*10",
'm'));
return examplesMenu;
}
/**
* Constructs a menu item intended for use in the Examples menu.
*
* @param menuText
* @param functionString
* @param mnemonicChar
* @return
*/
private JMenuItem constructExampleMenuItem(String menuText,
String functionString, char mnemonicChar) {
JMenuItem menuItem = new JMenuItem(menuText);
// store the function in the action command for use later
menuItem.setActionCommand(functionString);
menuItem.addActionListener(this);
menuItem.setVisible(true);
menuItem.setMnemonic(mnemonicChar);
return menuItem;
}
/**
* Recieves action events when file menu items are clicked
*
* @param e
*/
public void actionPerformed(ActionEvent e) {
// set the function string to the one which is in the action
// command of the menu item by entering it as an expression in the
// expression console
console.enterExpression(Grapher2DConstants.Grapher2DFunctionString
+ " = \"" + e.getActionCommand() + "\"");
// the function string variable is being observed by the view and
// controller, and as a result of this code execution will be
// appropriately notified.
}
}