Created
November 20, 2012 21:43
-
-
Save joelgallant/4121387 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package calculator; | |
import java.awt.GridBagConstraints; | |
import java.awt.GridBagLayout; | |
import java.awt.event.ActionEvent; | |
import java.awt.event.ActionListener; | |
import javax.swing.JButton; | |
import javax.swing.JFrame; | |
import javax.swing.JTextField; | |
import javax.swing.SwingUtilities; | |
/** | |
* Main class of the program. There is only one class in this program. | |
* | |
* <p>This class does what is called "extending" a different class. This means | |
* that it inherits all of the 'qualities' of the class. Basically | |
* {@link MainClass} is a custom type of {@link JFrame}, just as a mountain bike | |
* is a 'type' of bicycle. In this case, it extends {@link JFrame}. | |
* {@link JFrame} is the base for graphic windows.</p> | |
* | |
* <p>More information about inheritance and subclasses is available here : | |
* http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html</p> | |
* | |
* @author joel | |
*/ | |
public class MainClass extends JFrame { | |
final JTextField textArea = new JTextField(); | |
/** | |
* This is the main method. This is the only thing called by the execution. | |
* Everything must start from here. | |
* | |
* @param args The arguments given in the command-line. This isn't needed, | |
* but is a default property of main methods. | |
*/ | |
public static void main(String[] args) { | |
// Here we call what is called the Event Dispatch Thread to open up the window. | |
// This is a Java specific way of creating and showing GUI (Graphical User Interfaces) | |
SwingUtilities.invokeLater(new Runnable() { | |
/** | |
* This is what is called an overriding method. It is run | |
* automatically by the SwingUtilities#invokeLater(). | |
*/ | |
@Override | |
public void run() { | |
// This is creating what is called a new 'instance' of the main class. | |
// Because this is a static method (static void main()), there is no instances of the main class yet. | |
// (This isn't really important for now) | |
MainClass m = new MainClass(); | |
// This is setting the properties of the window. Without a size or location, it would show up as small as possible | |
m.setSize(400, 300); | |
// This is the way to set the window to go in the middle of the screen | |
m.setLocationRelativeTo(null); | |
// This sets the window as visible to the user. By default it is not visible | |
m.setVisible(true); | |
// This makes sure that the program will quit when the window is closed | |
m.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
} | |
}); | |
} | |
/** | |
* This is what is called a 'constructor'. It is run every time a new | |
* instance of the class ({@link MainClass}) is made. | |
*/ | |
public MainClass() { | |
// This sets the title of the window. It uses what is called a 'super' constructor, meaning | |
// that it is calling the constructor from the class it extends (JFrame) | |
super("Calculator"); | |
// Sets the GUI layout. This isn't too important to understand. | |
setLayout(new GridBagLayout()); | |
GridBagConstraints constraints = new GridBagConstraints(); | |
// Here, we create an array of all of the number buttons. Because it is | |
// 2 dimensional (Rows and columns), it is a 2 dimensional array. | |
JButton[][] numbers = new JButton[3][4]; | |
// All constraints.whatever going on is layout manager things on the GUI. | |
// This isn't important to understand, but if you wanted to look into it, | |
// check out http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html | |
constraints.fill = GridBagConstraints.BOTH; | |
constraints.weightx = 1; | |
constraints.weighty = 1; | |
constraints.gridy = 0; | |
constraints.gridx = 0; | |
constraints.gridwidth = 3; | |
add(textArea, constraints); | |
constraints.gridwidth = 1; | |
// This is a for loop going through all of the number buttons (The rows) | |
for (int x = 0; x < numbers.length; x++) { | |
// This is a second for loop that runs inside of the other one that goes through | |
// all of the columns of the buttons | |
for (int y = 0; y < numbers[x].length; y++) { | |
// If Y is 3, that means that there are already 3 lines of numbers | |
// (Arrays ALWAYS start at 0. This is a rule that applies everywhere) | |
if (y == 3) { | |
// If X is 1, that means it is the spot for the zero button | |
if (x == 1) { | |
// Creates the zero button | |
numbers[x][y] = new JButton("0"); | |
constraints.gridx = x; | |
constraints.gridy = y + 1; | |
add(numbers[x][y], constraints); | |
} else { | |
// Creates other buttons just to avoid debugging problems | |
numbers[x][y] = new JButton(); | |
} | |
} // Does everything indside if there is still room for numbers (row < 3) | |
else { | |
// Creates the button with a number written within. +10 points if | |
// you understand why (x + 1) + (y * 3) would give you the number | |
// of the button. (Tip: Think about how the row relates to the number) | |
numbers[x][y] = new JButton((x + 1) + (y * 3) + ""); | |
constraints.gridx = x; | |
constraints.gridy = y + 1; | |
add(numbers[x][y], constraints); | |
} | |
// This adds what is called an action listener. The method within is | |
// called whenever the button in question is pressed by the user. | |
numbers[x][y].addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
// Uses a custom method to make something happen when the button is pressed | |
// The "Integer.parseInt(((JButton) e.getSource()).getText())" is more | |
// complicated than you need to understand. +15 points if you can figure out. :) | |
// Custom method, find lower down | |
pressButton(Integer.parseInt(((JButton) e.getSource()).getText())); | |
} | |
}); | |
} | |
} | |
// Creates the decimal point button | |
JButton decimal = new JButton("."); | |
// Adds a listener to the decimal button that adds a . to the text area. | |
decimal.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
textArea.setText(textArea.getText() + '.'); | |
} | |
}); | |
constraints.gridx = 0; | |
constraints.gridy = 4; | |
add(decimal, constraints); | |
// Creates the equals button | |
JButton equals = new JButton("="); | |
// Adds a listener that uses a custom method to set the text area to display the result | |
equals.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
// Custom method, find lower down | |
pressOperator('='); | |
} | |
}); | |
constraints.gridx = 2; | |
constraints.gridy = 4; | |
add(equals, constraints); | |
// From here on all you need to know is that it is creating buttons and setting them do | |
// their actions when pressed. Scroll down to #pressButton() to learn more. | |
JButton clear = new JButton("CE"); | |
clear.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
textArea.setText(null); | |
} | |
}); | |
constraints.gridx = 3; | |
constraints.gridy = 0; | |
add(clear, constraints); | |
JButton add = new JButton("+"); | |
add.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
pressOperator('+'); | |
} | |
}); | |
constraints.gridy = 1; | |
add(add, constraints); | |
JButton substract = new JButton("-"); | |
substract.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
pressOperator('-'); | |
} | |
}); | |
constraints.gridy = 2; | |
add(substract, constraints); | |
JButton multiply = new JButton("x"); | |
multiply.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
pressOperator('x'); | |
} | |
}); | |
constraints.gridy = 3; | |
add(multiply, constraints); | |
JButton divide = new JButton("/"); | |
divide.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
pressOperator('/'); | |
} | |
}); | |
constraints.gridy = 4; | |
add(divide, constraints); | |
} | |
// This is a "Global Variable". It is usable anywhere in any methods. | |
// It signifies whether a new number being pressed should automatically clear | |
// the old result. (After a result has been found) | |
boolean clearable = false; | |
/** | |
* Virtually presses the button. Adds the number to the end of the text in | |
* the text box. | |
* | |
* @param button this parameter of the method is the number that was pressed | |
* (ex. 1, 3, 9, etc.) | |
*/ | |
void pressButton(int button) { | |
if (clearable) { | |
textArea.setText(null); | |
clearable = false; | |
} | |
textArea.setText(textArea.getText() + button); | |
} | |
/** | |
* Virtually presses an operator. This include addition, multiplication, | |
* equals, etc. | |
* | |
* @param operator this parameter is the symbol of the operator, in | |
* Character form | |
*/ | |
void pressOperator(final char operator) { | |
// This sets "clearable" to false because an equation is already being | |
// written, and should not be overriden | |
clearable = false; | |
// This is what is called a switch-case logic statement. | |
// it will test if the "operator" parameter matches any of the given 'cases' | |
switch (operator) { | |
// If operator is equal to '=', it will do the following things | |
case ('='): | |
try { | |
// Sets the text to the results of the "calculate()" method | |
textArea.setText(calculate() + ""); | |
} catch (Exception ex) { | |
// This 'catches' an exception, meaning that if something goes wrong | |
// in the try {} statement, this code is run. Displays that an error occured. | |
textArea.setText("ERROR - " + ex.getMessage()); | |
ex.printStackTrace(System.err); | |
} | |
// Escapes the switch-case statement. This is done so it runs faster. | |
break; | |
// Again, if the operator is equal to '+', it will do the following things. | |
case ('+'): | |
// Adds an addition sign with spaces on each side | |
textArea.setText(textArea.getText() + " + "); | |
break; | |
case ('-'): | |
// Adds a substraction sign with spaces on each side | |
textArea.setText(textArea.getText() + " - "); | |
break; | |
case ('x'): | |
// Adds a multiplication sign with spaces on each side | |
textArea.setText(textArea.getText() + " x "); | |
break; | |
case ('/'): | |
// Adds a division sign with spaces on each side | |
textArea.setText(textArea.getText() + " / "); | |
break; | |
} | |
} | |
/** | |
* This method is the heart of the calculator. It calculates the answer to | |
* the question asked in the text box. | |
* | |
* @return answer to the question | |
* @throws NumberFormatException this exception is thrown when an error | |
* occurs while calculating. This usually happens when things like two | |
* operator signs are put beside each other. | |
*/ | |
double calculate() throws NumberFormatException { | |
// Sets clearable to be true, since the answer has already been calculated | |
clearable = true; | |
// Creates the variable 'text' based on the text area. | |
String text = textArea.getText(); | |
// Splits the text into a bunch of different parts seperated by spaces. | |
String[] texts = text.split(" "); | |
// This goes through all of the parts, and removes spaces (to prevent errors) | |
for (int x = 0; x < texts.length; x++) { | |
texts[x] = texts[x].replace(" ", ""); | |
} | |
// This variable is used to store the current value of the equation | |
double currentValue = Double.MAX_VALUE; | |
// This variable is used to store the previously used operation | |
String prevOperation = null; | |
// This variable is used to tell if there was an operation sign before the current number | |
boolean operation = false; | |
// This for loop goes through every part of the equation seperately | |
for (int x = 0; x < texts.length; x++) { | |
// Tests to see if the current text is an operation | |
if (texts[x].equals("+") || texts[x].equals("-") || texts[x].equals("x") || texts[x].equals("/")) { | |
// Makes sure that the current value has been inputed (The first number in the equation) | |
if (currentValue != Double.MAX_VALUE) { | |
// Sets the operation variable to be true because there was an operator in the previous position | |
operation = true; | |
// Sets the previous operation as well | |
prevOperation = texts[x]; | |
} | |
} | |
// Does this if it is not an operator, and the previous part was an operator | |
else if (operation) { | |
// Uses a switch statement on the previous operation | |
switch (prevOperation) { | |
// Does the appropriate equation depending on operation | |
case ("+"): | |
currentValue += Double.parseDouble(texts[x]); | |
break; | |
case ("-"): | |
currentValue -= Double.parseDouble(texts[x]); | |
break; | |
case ("x"): | |
currentValue *= Double.parseDouble(texts[x]); | |
break; | |
case ("/"): | |
currentValue /= Double.parseDouble(texts[x]); | |
break; | |
} | |
// Sets operation to false, because now the previous part was a number | |
operation = false; | |
} | |
// Does this if there was no operation beforehand, and the part is a number | |
// This should only happen on the first number in the equation | |
else { | |
// Sets the current value to the first part | |
currentValue = Double.parseDouble(texts[x]); | |
} | |
} | |
// "Returns" the value of the equation to the method. This is used to be | |
// put on the text area, rather than just outputing to the text area here. | |
return currentValue; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment