Listing of CalculatorApp.java
CalculatorApp.java
A
stand-alone Java `Calculator program based on the Calculator
applet
defined in Calculator.java. All this program does is
embed
the applet in a Frame.
Kevin
Boone, September 1999
*/
import
java.applet.Applet;
import
java.awt.*;
import
java.awt.event.*;
/*
MainWindow
This
class defines the main window for the application. It is
defined
to implement WindowListener so that it can receive
window
events from the user interface
*/
class
MainWindow extends Frame implements WindowListener
{
public MainWindow()
{
// In the constructor we set the size and caption of the
// window, and specify `this as the window listener
super("Calculator");
setSize(300, 300);
addWindowListener (this);
add (new Calculator());
}
// These methods are specified by the WindowListener interface so
// we must include them. However, they dont do anything in this
// program
public void windowClosed (WindowEvent e) {}
public void windowOpened (WindowEvent e) {}
public void windowDeiconified (WindowEvent e) {}
public void windowIconified (WindowEvent e) {}
public void windowDeactivated (WindowEvent e) {}
public void windowActivated (WindowEvent e) {}
/*
windowClosing
This
method is called in response to a windowClosing event. It should
simply
exit the program
*/
public
void windowClosing (WindowEvent e)
{
System.exit(0);
}
} // class
////////////////////////////////////////////////////////////////////////
class
Calculator extends Applet implements ActionListener, KeyListener
{
/*==============================================================================
Variables
of Calculator class
==============================================================================*/
// MAX_INPUT_LENGTH is a constant indicating how large the number
// entered by
// the user can be. A Java `double can store a value with a
//precision of
// about 17 digits. Therefore there is no point in allowing the
// user to
// enter numbers any bigger than this.
final int MAX_INPUT_LENGTH = 17;
/*
Display
modes
The
variable `displayMode can take one of three values. For ease of reading
I have
defined three constants to represent these values. It doesnt matter
at all
what the values of these constants are (I have used 0, 1 and 2), as
long
as they are all different.
The
displayMode variable indicates what is currently being displayed. It
will
either be the number being entered by the user (INPUT_MODE), the
result
of the last calculation (RESULT_MODE) or an error message
(ERROR_MODE).
This variable is very important to the correct function of
the
program. For example, if the user types a digit when the program is
in
INPUT_MODE, the program should append the digit to the number
currently
in the display. If in RESULT_MODE or ERROR_MODE, the display
should
be cleared and the new digit put at the start. In fact,
many
of the methods in this class behave differently according to the
value
of displayMode
*/
// QUESTION: why have I defined constants for these values?
In the program
// I write
`if (displayMode == ERROR_MODE); why dont I just write
// `if
(displayMode == 2) ?
final int INPUT_MODE = 0;
final
int RESULT_MODE = 1;
final
int ERROR_MODE = 2;
int
displayMode;
//
displayLabel is the area of the applet in which all display will be
//shown.
// I
have used a Java `Label object for this.
Label
displayLabel;
//
clearOnNextDigit is set to `true whenever an operator (`+, `-,
// etc) is
//
typed. This causes the next digit entered to be the start of a new number.
boolean
clearOnNextDigit;
//
lastNumber is the number that was entered by the user before the
// last operator.
// for
example, if the user type `2 followed by `+, then lastNumber
//will be
// set
to `2
double
lastNumber;
//
lastOperator is the operator last typed by the user. If the user
//types a
//
`+ sign (or clicks on the `+ button), then lastOperator is set to
//
+. This variables value is set to zero to indicate that no
//operator
// has
been typed yet
char
lastOperator;
/*==============================================================================
Calculator
constructor
This
operator (as with all constructors) is executed when the new object
is
created. In this case we create all the buttons (and the panels that
contain
them), then reset the calculator
The
layout of buttons in this Applet is quite complex. We have three groups
of
buttons, one for digits, one for operators and one for `controls
(`=,
`AC and `C). Im not sure `controls is the right word here, but
I
couldnt think of a better one. So we create three `Panel objects
to
contain each group of buttons. A Panel is a blank screen area whose
job is
simply to hold other objects. Associated with each Panel is a
layout
manager. The job of the layout manager is to group the buttons in
the
right order. For example, the button panel has a grid of buttons, with
four
rows of three buttons like this:
7 8 9
4 5 6
1 2 3
. 0
+/-
So we
set the layout manager for this panel to a new GridLayout object.
When
we create the new GridLayout we specify `new GridLayout(4, 3)
which
means `create a new GridLayout with four rows of three columns.
Having
created the panels, we add the buttons. Finally we add all the
panels
into a single panel (called `buttonPanel) and add this to the
applet.
We also add the display area.
==============================================================================*/
public
Calculator()
{
super();
setLayout (new BorderLayout());
Panel buttonPanel = new Panel();
Panel numberPanel = new Panel();
numberPanel.setLayout(new GridLayout(4, 3));
addButtonToPanel (numberPanel, new Button("7"), Color.blue);
addButtonToPanel (numberPanel, new Button("8"), Color.blue);
addButtonToPanel (numberPanel, new Button("9"), Color.blue);
addButtonToPanel (numberPanel, new Button("4"), Color.blue);
addButtonToPanel (numberPanel, new Button("5"), Color.blue);
addButtonToPanel (numberPanel, new Button("6"), Color.blue);
addButtonToPanel (numberPanel, new Button("1"), Color.blue);
addButtonToPanel (numberPanel, new Button("2"), Color.blue);
addButtonToPanel (numberPanel, new Button("3"), Color.blue);
addButtonToPanel (numberPanel, new Button("."), Color.blue);
addButtonToPanel (numberPanel, new Button("0"), Color.blue);
addButtonToPanel (numberPanel, new Button("+/-"), Color.blue);
buttonPanel.add(numberPanel);
Panel operatorPanel = new Panel();
operatorPanel.setLayout(new GridLayout(4, 1));
addButtonToPanel (operatorPanel, new Button("/"), Color.green);
addButtonToPanel (operatorPanel, new Button("*"), Color.green);
addButtonToPanel (operatorPanel, new Button("-"), Color.green);
addButtonToPanel (operatorPanel, new Button("+"), Color.green);
buttonPanel.add(operatorPanel);
Panel controlPanel = new Panel();
controlPanel.setLayout(new GridLayout(4, 1));
addButtonToPanel (controlPanel, new Button("AC"), Color.cyan);
addButtonToPanel (controlPanel, new Button("C"), Color.cyan);
addButtonToPanel (controlPanel, new Button("="), Color.cyan);
buttonPanel.add(controlPanel);
displayLabel = new Label("");
displayLabel.setAlignment(Label.CENTER);
// QUESTION: what does `North mean here?
add(displayLabel, "North");
displayResult(0);
add(buttonPanel);
addKeyListener(this);
requestFocus();
//
clearAll sets the variables of the Calculator object to
//
initial values (this is the method that is called
//
when the user clicks on the `AC button
clearAll();
}
/*==============================================================================
setDisplayString
This
method sets the text in the display area to the specified string.
This
hardly needs to be a method at all, as it is so simple. However,
it is
called many many other methods, and using a method for this
function
would make it much easier to modify the program if we chose a
different
way to display data. Otherwise, a large number of lines would
have
to be modified.
==============================================================================*/
void
setDisplayString(String s)
{
displayLabel.setText(s);
}
/*==============================================================================
getDisplayString
This
method gets the text currently in the display area. See the note for
`getDisplayString
above for details.
==============================================================================*/
String
getDisplayString ()
{
return displayLabel.getText();
}
/*==============================================================================
clearAll
Sets
the state of the calculator to the `just switched on state, that is,
`0
in the display, ready to input digits, and the last operator equal to
zero
(that is, there is no last operator). This method is called by the
constructor
to initialize the Calculator, and whenever the user
clicks
on `AC
==============================================================================*/
void
clearAll()
{
setDisplayString("0");
lastOperator = 0;
lastNumber = 0;
displayMode = INPUT_MODE;
clearOnNextDigit = true;
}
/*==============================================================================
clearLastEntry
Clears
the number currently in the display. This is called when the user
clicks
on the `C button.
==============================================================================*/
void
clearLastEntry()
{
setDisplayString("0");
clearOnNextDigit = true;
displayMode = INPUT_MODE;
}
/*==============================================================================
displayResult
Displays
the specified number in the display area, and sets the variables to
indicate
that a result is being displayed. Specifically this method sets
`clearOnNextDigit
to `true. This means that as soon as the user types
a
digit, the display will be cleared to make space for a new number.
==============================================================================*/
void
displayResult(double result)
{
setDisplayString(Double.toString(result));
lastNumber = result;
displayMode = RESULT_MODE;
clearOnNextDigit = true;
}
/*==============================================================================
displayError
Displays
the specified error message in the display area, and sets the
variables
to indicate that an error message is being displayed. Specifically
this
method sets `clearOnNextDigit to `true. This means that as soon
as the
user types a digit, the display will be cleared to make space
for a
new number.
The
displayMode is set to ERROR_MODE to indicate that the display contains an
error
message and not a number. This is important because the user might
press
a button that modifies the current number (like `+/-). Clearly
we
cant change the sign of an error message (or take its square root, etc).
==============================================================================*/
void
displayError(String errorMessage)
{
setDisplayString(errorMessage);
lastNumber = 0;
displayMode = ERROR_MODE;
clearOnNextDigit = true;
}
/*==============================================================================
addButtonToPanel
This
is a `convenience function, that is, it exists simply to reduce the
number
of lines in the program (and therefore make it more convenient for the
programmer
to manage). This method is called by the constructor to add
a new
button to the display. It sets the buttons colour, and sets the
key
listener and action listener to the appropriate methods (this has
to be
done for every button, as we cant predict which button will have
the
input focus at any given time).
==============================================================================*/
void
addButtonToPanel(Panel panel, Button button, Color backgroundColour)
{
panel.add(button);
button.setBackground(backgroundColour);
button.addKeyListener(this);
button.addActionListener(this);
}
/*==============================================================================
actionPerformed
This
method is called whenever the user clicks a button. This happens
because
the method `addButtonToPanel calls `addActionListener for
every
button. All this method does is pass on the text on the button to
`processButton
==============================================================================*/
public
void actionPerformed (ActionEvent e)
{
processButton(e.getActionCommand());
}
/*==============================================================================
processButton
This
method takes action according to the users input. It is called from
two
other methods: actionPerformed (when user clicks a button) and
keyPressed
(when the user presses a key on the keyboard). This method
examines
the text on the button (the argument `command) and calls the
appropriate
method to process it.
==============================================================================*/
void
processButton(String command)
{
if (command.equals("0")) addDigit(0);
if (command.equals("1")) addDigit(1);
if (command.equals("2")) addDigit(2);
if (command.equals("3")) addDigit(3);
if (command.equals("4")) addDigit(4);
if (command.equals("5")) addDigit(5);
if (command.equals("6")) addDigit(6);
if (command.equals("7")) addDigit(7);
if (command.equals("8")) addDigit(8);
if (command.equals("9")) addDigit(9);
if (command.equals(".")) addDecimalPoint();
if (command.equals("*")) processOperator(*);
if(command.equals("-")) processOperator(-);
if (command.equals("/")) processOperator(/);
if (command.equals("+")) processOperator(+);
if (command.equals("=")) processEquals();
if (command.equals("+/-")) processSignChange();
if (command.equals("AC")) clearAll();
if (command.equals("C")) clearLastEntry();
}
/*==============================================================================
getNumberInDisplay
Returns
a double value indicating the number in the display. This method
should
never be called if the display does not contain a number. When an
error
message is being displayed, the value of `displayMode is ERROR_MODE,
so
this can be used to test whether the display contains a number.
This
method is only necessary to make it easier in future if we decide to
represent
data on the display in a different way (i.e., to use a different
object
rather than `Label)
==============================================================================*/
double
getNumberInDisplay()
{
String input = displayLabel.getText();
return Double.parseDouble(input);
}
/*==============================================================================
processLastOperator
Carries
out the arithmetic method specified by the last operator, the last
number
and the number in the display.
If the
operator causes an error condition (i.e., the user tries to divide
something
by zero), this method can throw an execption.
==============================================================================*/
// QUESTION: if the user clicks on the buttons `2,
`+ and `3, what values
//
will found in the variables `lastOperator and `lastNumber, and the
//
variable `numberInDisplay?
double processLastOperator() throws DivideByZeroException
{
double result = 0;
double numberInDisplay = getNumberInDisplay();
switch (lastOperator)
{
case *:
result = lastNumber * numberInDisplay;
break;
case +:
result = lastNumber + numberInDisplay;
break;
case -:
result = lastNumber - numberInDisplay;
break;
case /:
if (numberInDisplay == 0)
throw (new DivideByZeroException());
result = lastNumber / numberInDisplay;
break;
}
return result;
}
/*==============================================================================
processOperator
Processes
the operator most recently typed. All we do is evalute the
_last_
operator typed (not this one), and store this current variable
is
`lastOperator so it will get processed when the user types another
operator,
or `=.
==============================================================================*/
void
processOperator(char op)
{
if (displayMode != ERROR_MODE)
{
double numberInDisplay = getNumberInDisplay();
if (lastOperator != 0)
{
try
{
double result = processLastOperator();
displayResult(result);
lastNumber = result;
}
catch (DivideByZeroException e)
{
displayError("Division by zero!");
}
}
else
{
lastNumber = numberInDisplay;
}
clearOnNextDigit = true;
lastOperator = op;
}
}
/*==============================================================================
processEquals
Deals
with the user clicking the `= button. This method finishes the
most
recent calculator. If the displayMode is `ERROR_MODE (that is, the
display
contains an error message) this method should not do anything.
==============================================================================*/
void
processEquals()
{
if (displayMode != ERROR_MODE)
{
try
{
double result = processLastOperator();
displayResult(result);
}
catch (DivideByZeroException e)
{
displayError("Division by zero!");
}
lastOperator = 0;
}
}
/*==============================================================================
processSignChange
This
method is called when the user clicks on the `+/- (sign change)
button.
If the number in the display is negative it is converted to positive.
If the
number in the display is positive it is converted to negative. If
the
number in the display is zero, nothing happens (as `-0 is meaningless).
This
method behaves slightly differently depending on what mode the
display
is in. If the user is currently entering a number, then it looks
at the
_string_ in the display. If it is displaying a result it looks at
the
_number_ in the display.
==============================================================================*/
// QUESTION: Why is this? Why does this method
have to behave differently
// depending
on whether a result or a new number is in the display? Hint: it
// is quite
a subtle problem. The program would work correctly most of the
// time if
we always treated the display as a number.
{
if (displayMode == INPUT_MODE)
{
String input = getDisplayString();
if (input.length() > 0)
{
if (input.indexOf("-") == 0)
setDisplayString(input.substring(1));
else
setDisplayString("-" + input);
}
}
else if (displayMode == RESULT_MODE)
{
double numberInDisplay = getNumberInDisplay();
if (numberInDisplay != 0)
displayResult(-numberInDisplay);
}
// If displayMode is `ERROR_MODE then this method should have no
// effect (because you cant change the sign of an error message!)
}
/*==============================================================================
addDigit
This
method is called when the user clicks a digit button, or types a
digit
key. If the number currently being entered is less than 17 digits long,
then
it adds the new digit to the end of the display.
==============================================================================*/
void
addDigit(int digit)
{
// QUESTION: what are the next two lines for?
setDisplayString("");
// We have to be careful to prevent the user entering ugly numbers
// like `000. If the number in the display is `0, and the user
// clicks on `0, this display is not changed.
String
inputString = getDisplayString();
if
((!inputString.equals("0") || digit > 0) && inputString.length() <
MAX_INPUT_LENGTH)
{
setDisplayString(inputString + digit);
}
displayMode = INPUT_MODE;
clearOnNextDigit = false;
}
/*==============================================================================
addDecimalPoint
Called
when the user clicks on the decimal point button. Puts a decimal
point
on the end of the number currently being entered. If the number
already
contains a decimal point, this method should do nothing
(that
is, it should be impossible to enter a number like `1.2.3
==============================================================================*/
void
addDecimalPoint()
{
displayMode = INPUT_MODE;
if (clearOnNextDigit)
setDisplayString("");
String inputString = getDisplayString();
// If the input string already contains a decimal point, dont
// do anything to it.
if (inputString.indexOf(".") < 0)
setDisplayString(new String(inputString + "."));
}
/*==============================================================================
keyPressed
This
method is called when the user presses a key, when any button has the
input
focus. This happens because the method `addButtonToPanel calls
`addKeyboardListener
for each button that it knows about. However,
this
method does nothing in this program, as keyboard input is handled
in the
method `keyTyped. Because this applet is defined to `implement
KeyEventHandler
we have to provide this method, even if it does
nothing.
==============================================================================*/
public
void keyPressed(KeyEvent e)
{
}
/*==============================================================================
keyReleased
Called
automatically whenever the user presses and then releases a key. See
note
for `keyPressed for details
==============================================================================*/
public
void keyReleased(KeyEvent e)
{
}
/*==============================================================================
keyTyped
This
method is called when the user presses a key, and then releases it
when
any button has the input focus. This happens because the method
`addButtonToPanel
calls `addKeyboardListener for each button thait knows
about.
This
method converts the key press to the text of the corresponding button,
then
passes it to processButton to process. For example, if the user
presses
`enter, this should have the same effect as clicking `=. So this
method
calls `processButton with the text `=. If the user
presses
`escape this should be treated the same as the `C button. All other
keys
are treated as digits. If the user presses a letter rather than
a
digit, it will still be passed to `processButton, but processButton
will
ignore it.
==============================================================================*/
public
void keyTyped(KeyEvent e)
{
String command;
char keyChar = e.getKeyChar();
if (keyChar == KeyEvent.VK_ENTER)
command = new String("=");
else if (keyChar == KeyEvent.VK_ESCAPE)
command = new String("C");
else
{
/// QUESTION: what do these next two lines do?
byte bytes[] = {(byte)keyChar};
command = new String(bytes);
}
processButton(command);
}
} // class
/*==============================================================================
DivideByZeroException
class
This
class is used to indicate that the user has tried to divide a number
by
zero (which is, of course, impossible. The Calculator applet should
respond
by displaying an error message
The
issue of exception handling is covered in more detail later in the
module;
dont worry if it doesnt make much sense at the moment.
==============================================================================*/
class
DivideByZeroException extends Exception
{
DivideByZeroException()
{
super("Divide
by zero");
}
}
/*
CalculatorApp
This
is the programs primary class. All it does is supply a
`main
method which creates a new main window
*/
public
class CalculatorApp
{
public
static void main (String args[])
{
MainWindow
mainWindow = new MainWindow();
mainWindow.setVisible(true);
}
}
Back
to top

RITSEC - Global Campus
Copyright ?1999 RITSEC- Middlesex University. All rights reserved.
webmaster@globalcampus.com.eg
|