home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
com!online 2002 April
/
comcd0402.iso
/
homepage
/
javaspecial
/
03_01
/
romanus
/
CompvterRomanvs.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Java Source
|
1997-12-18
|
66.8 KB
|
1,636 lines
/*-**************************** *****************************-*/
/*-**************************** CompvterRomanvs *****************************-*/
/*-* *-*/
/*-* This applet is a calculator that works in Roman Numerals *-*/
/*-* Copyright ⌐ MCMXCVII Edward R. Hobbs *-*/
/*-* *-*/
/*-**************************************************************************-*/
/*-**************************************************************************-*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class CompvterRomanvs extends java.applet.Applet implements Runnable
{
/*-------------------------------------------------------------------------*/
/* These data items index the calculator keys and their associated images */
/*-------------------------------------------------------------------------*/
final int m_ndxI = 0;
final int m_ndxV = 1;
final int m_ndxX = 2;
final int m_ndxL = 3;
final int m_ndxC = 4;
final int m_ndxD = 5;
final int m_ndxM = 6;
final int m_ndxVbar = 7;
final int m_ndxXbar = 8;
final int m_ndxLbar = 9;
final int m_ndxCbar = 10;
final int m_ndxDbar = 11;
final int m_ndxMbar = 12;
final int m_ndxMaxNumeral = 12; /* Index of maximum numeral */
final int m_ndxAdd = 13;
final int m_ndxSubtract = 14;
final int m_ndxMultiply = 15;
final int m_ndxDivide = 16;
final int m_ndxEquals = 17;
final int m_ndxClear = 18;
final int m_ndxClearEntry = 19;
final int m_nCalcKeys = 20; /* Total number of calculator keys */
/*-------------------------------------------------------------------------*/
/* The following arrays hold the calculator key objects and their images. */
/*-------------------------------------------------------------------------*/
Image[] m_imageNumeral = new Image[m_nCalcKeys];
Image m_imageBackground;
Image m_imageCaesar;
MediaTracker m_mediaTracker = new MediaTracker ( this );
Image m_imageLoading = null;
CalcKey[] m_calckey = new CalcKey[m_nCalcKeys];
CalcKey m_calckeyPressed = null;
int m_ndxLastKey = m_ndxClear; /* Index of latest keypress */
int m_ndxPrevOperator = m_ndxAdd; /* Index of previous operator */
/*-------------------------------------------------------------------------*/
/* The following data items are used to keep track of the current display */
/*-------------------------------------------------------------------------*/
final int m_nDisplayX0 = 15;
final int m_nDisplayY0 = 15;
final int m_nDisplayWidth = 550;
final int m_nDisplayHeight = 35;
final int m_nDisplayGap = 1;
final int m_nDisplayMargin = 5;
final int m_nMaxNumerals = 27;
int m_nNumerals = 0;
int[] m_nNumeral = new int[m_nMaxNumerals];
final Color m_colorDisplay = new Color ( 192, 192, 192 );
Image m_imageDisplay;
final int m_nBezelWidth = 2;
final Color m_colorBezelLight = new Color ( 255, 255, 255 );
final Color m_colorBezelDark = new Color ( 80, 80, 80 );
/*-------------------------------------------------------------------------*/
/* Miscellaneous data items */
/*-------------------------------------------------------------------------*/
final int m_nMaxValue = 3999999; /* Range of values that can be... */
final int m_nMinValue = 1; /* ...expressed as Roman Numerals */
int m_nError = 0; /* See values below */
boolean m_bErrorPhase = true; /* Updated by background thread */
final int m_nOVERFLOW = -1; /* Value too big for Roman Numerals */
final int m_nUNDERFLOW = -2; /* Value too small for Roman Numerals */
final int m_nINVALID = -3; /* Invalid sequence of Numerals */
int m_nAccumulator ; /* Calculator logical accumulator */
Thread m_thread = null;
boolean m_bInitialized = false;
int[] m_nKeyBuffer = new int[4]; /* Buffer for "INFO" command */
final int m_nFrameWidth = 5;
Color m_colorFrameLight = new Color ( 255, 255, 255 );
Color m_colorFrameDark = new Color ( 80, 80, 80 );
/*-******************************** init *********************************-*/
/* */
/* This method is invoked exactly once, the first time the applet is */
/* started. */
/* */
/*-***********************************************************************-*/
public void init()
{
start();
}
/*-****************************** keyDown ********************************-*/
/* */
/* Intercept keystokes destined for the applet. When the "INFO" sting is */
/* typed (case sensitive!), repaint the window with a copyright notice. */
/* This allows the applet to be positively identified from a web browser. */
/* */
/*-***********************************************************************-*/
public boolean keyDown ( Event evt, int nKey )
{
m_nKeyBuffer[3] = m_nKeyBuffer[2]; /*---------------------------*/
m_nKeyBuffer[2] = m_nKeyBuffer[1]; /* As each key is received, */
m_nKeyBuffer[1] = m_nKeyBuffer[0]; /* shift it into the buffer. */
m_nKeyBuffer[0] = nKey; /*---------------------------*/
if ( ( m_nKeyBuffer[3] == (int) 'I' ) && /*---------------------------*/
( m_nKeyBuffer[2] == (int) 'N' ) && /* Check for the "INFO" */
( m_nKeyBuffer[1] == (int) 'F' ) && /* string. */
( m_nKeyBuffer[0] == (int) 'O' ) ) /*---------------------------*/
{
stop(); /* We don't want to collide with the background thread */
Graphics g = getGraphics();
FontMetrics fm = g.getFontMetrics();
Dimension dimWin = size();
g.setColor ( Color.white );
g.fillRect ( 0, 0, dimWin.width, dimWin.height );
g.setColor ( Color.black );
/*-------------------------------------------------------------------*/
/* This is the identifying info displayed in response to the "INFO" */
/* command. */
/*-------------------------------------------------------------------*/
g.drawString ( "COMPVTER ROMANVS", 20, fm.getHeight() );
g.drawString ( "(c) 1997 Edward R. Hobbs", 20, 3 * fm.getHeight() );
g.drawString ( " edhobbs@aol.com", 20, 4 * fm.getHeight() );
g.drawString ( " All rights reserved.", 20, 5 * fm.getHeight() );
}
return true;
}
/*-******************************** paint ********************************-*/
/* */
/*-***********************************************************************-*/
public void paint ( Graphics g )
{
Dimension dimWin = size();
if ( ! m_bInitialized )
{
/*-------------------------------------------------------------------*/
/* During initialization, the background thread paints the applet */
/* window with a bar graph showing progress in loading graphics. */
/*-------------------------------------------------------------------*/
return;
}
else
{
/*-*****************************************************************-*/
/* If the applet has been initialized, redraw the calculator. */
/*-*****************************************************************-*/
int nWidth = m_imageBackground.getWidth ( null );
int nHeight = m_imageBackground.getHeight ( null );
for ( int nX = 0; nX < dimWin.width; nX += nWidth )
for ( int nY = 0; nY < dimWin.height; nY += nHeight )
g.drawImage ( m_imageBackground, nX, nY, null );
g.drawImage ( m_imageCaesar, 90, 70, null );
/*-------------------------------------------------------------------*/
/* Draw the beveled frame around the edge of the calculator */
/*-------------------------------------------------------------------*/
g.setColor ( m_colorFrameLight );
for ( int i = 0; i < m_nFrameWidth; i++ )
{
g.drawLine ( i, i, dimWin.width - 1 - i, i );
g.drawLine ( i, i, i, dimWin.height - 1 - i );
}
g.setColor ( m_colorFrameDark );
for ( int i = 0; i < m_nFrameWidth; i++ )
{
g.drawLine ( dimWin.width - 1 - i, dimWin.height - 1 - i, i, dimWin.height - 1 - i );
g.drawLine ( dimWin.width - 1 - i, dimWin.height - 1 - i, dimWin.width - 1 - i, i );
}
/*-------------------------------------------------------------------*/
/* Draw the bezel around the edge of the display */
/*-------------------------------------------------------------------*/
g.setColor ( m_colorBezelDark );
for ( int i = 0; i < m_nBezelWidth; i++ )
{
g.drawLine ( m_nDisplayX0 - i - 1,
m_nDisplayY0 - i - 1,
m_nDisplayX0 + m_nDisplayWidth + i,
m_nDisplayY0 - i - 1 );
g.drawLine ( m_nDisplayX0 - i - 1,
m_nDisplayY0 - i - 1,
m_nDisplayX0 - i - 1,
m_nDisplayY0 + m_nDisplayHeight + i );
}
g.setColor ( m_colorBezelLight );
for ( int i = 0; i < m_nBezelWidth; i++ )
{
g.drawLine ( m_nDisplayX0 + m_nDisplayWidth + i,
m_nDisplayY0 + m_nDisplayHeight + i,
m_nDisplayX0 + m_nDisplayWidth + i,
m_nDisplayY0 - i - 1 );
g.drawLine ( m_nDisplayX0 + m_nDisplayWidth + i,
m_nDisplayY0 + m_nDisplayHeight + i,
m_nDisplayX0 - i - 1,
m_nDisplayY0 + m_nDisplayHeight + i );
}
for ( int i = 0; i < m_nCalcKeys; i++ )
m_calckey[i].Paint ( g, this );
drawDisplay ( g );
}
}
/*-****************************** mouseDown ******************************-*/
/* */
/* When the user presses the mouse button, check to see if the user clicked*/
/* on one of the calculator keys. */
/* */
/*-***********************************************************************-*/
public boolean mouseDown ( Event evt, int nX, int nY )
{
if ( ! m_bInitialized ) return true;
for ( int i = 0; i < m_nCalcKeys; i++ )
{
if ( m_calckey[i].testHit ( nX, nY ) )
if ( m_calckey[i] != m_calckeyPressed )
{
/*-------------------------------------------------------------*/
/* If one of the calculator keys is already pressed (and it is */
/* not this key), then un-press it. */
/*-------------------------------------------------------------*/
if ( m_calckeyPressed != null )
{
m_calckeyPressed.setPressed ( false, this );
m_calckeyPressed = null;
}
/*-------------------------------------------------------------*/
/* Redraw the newly-pressed calculator key. */
/*-------------------------------------------------------------*/
m_calckey[i].setPressed ( true, this );
m_calckeyPressed = m_calckey[i];
/*-------------------------*/
handleCalcKeyPress ( i ); /* Respond to the keypress */
/*-------------------------*/
break;
}
}
return true;
}
/*-******************************* Mouse Up ******************************-*/
/* */
/* When the user releases the mouse button, raise any calculator key that */
/* happens to be pressed. */
/* */
/*-***********************************************************************-*/
public boolean mouseUp ( Event evt, int nX, int nY )
{
if ( m_calckeyPressed != null )
{
m_calckeyPressed.setPressed ( false, this );
m_calckeyPressed = null;
}
return true;
}
/*-***************************** Mouse Drag ******************************-*/
/* */
/* Even if the user holds the mouse button down, raise the pressed key if */
/* he drags the mouse out of the area the key covers. */
/* */
/*-***********************************************************************-*/
public boolean mouseDrag ( Event evt, int nX, int nY )
{
if ( m_calckeyPressed != null )
if ( ! m_calckeyPressed.testHit ( nX, nY ) )
{
m_calckeyPressed.setPressed ( false, this );
m_calckeyPressed = null;
}
return true;
}
/*-************************** handleCalcKeyPress *************************-*/
/* */
/* This function performs the logical operation associated with the key */
/* which has just been pressed. */
/* */
/*-***********************************************************************-*/
private void handleCalcKeyPress ( int ndxCalcKey )
{
/*----------------------------------------------------------------------*/
/* If there is an outstanding error code, then ignore any keypress */
/* except Clear. */
/*----------------------------------------------------------------------*/
if ( m_nError != 0 )
{
if ( ndxCalcKey == m_ndxClear )
{
m_nError = 0;
m_nNumerals = 0;
m_nAccumulator = 0;
m_ndxLastKey = m_ndxClear;
m_ndxPrevOperator = m_ndxAdd;
updateDisplay();
}
return;
}
/*----------------------------------------------------------------------*/
/* The user pressed a numeral key */
/*----------------------------------------------------------------------*/
if ( ndxCalcKey <= m_ndxMaxNumeral )
{
if ( m_ndxLastKey <= m_ndxMaxNumeral )
{
/*----------------------------------------------------------------*/
/* If the previous key was also a numeral, then append this key */
/* to the string of numerals in the display. */
/*----------------------------------------------------------------*/
if ( m_nNumerals >= m_nMaxNumerals ) /*--------------------------*/
{ /* If the user put in too */
m_nAccumulator = 0; /* many numerals, set the */
m_nNumerals = 0; /* error code to INVALID. */
m_nError = m_nINVALID; /*--------------------------*/
m_ndxPrevOperator = m_ndxAdd;
}
else
{
m_nNumeral[m_nNumerals] = ndxCalcKey;
m_nNumerals++;
}
}
else
{
/*----------------------------------------------------------------*/
/* If this is the first numeral key after one or more non- */
/* numerals, start a new string of numerals in the display */
/*----------------------------------------------------------------*/
m_nNumeral[0] = ndxCalcKey;
m_nNumerals = 1;
/*----------------------------------------------------------------*/
/* If the user presses a numeral immediately following the "=" */
/* key, then we need to clear the accumulator. */
/*----------------------------------------------------------------*/
if ( m_ndxLastKey == m_ndxEquals )
{
m_nAccumulator = 0;
m_ndxPrevOperator = m_ndxAdd;
}
}
m_ndxLastKey = ndxCalcKey;
updateDisplay();
return;
}
/*----------------------------------------------------------------------*/
/* Handle the keys for mathematical operations. */
/*----------------------------------------------------------------------*/
if ( ( ndxCalcKey == m_ndxAdd ) ||
( ndxCalcKey == m_ndxSubtract ) ||
( ndxCalcKey == m_ndxMultiply ) ||
( ndxCalcKey == m_ndxDivide ) ||
( ndxCalcKey == m_ndxEquals ) )
{
/*-------------------------------------------------------------------*/
/* If the user types a +,-,*,/ directly after the "=" key, the */
/* operator becomes the previous operator. */
/*-------------------------------------------------------------------*/
if ( ( m_ndxLastKey == m_ndxEquals ) && ( ( ndxCalcKey == m_ndxAdd ) ||
( ndxCalcKey == m_ndxSubtract ) ||
( ndxCalcKey == m_ndxMultiply ) ||
( ndxCalcKey == m_ndxDivide ) ) )
{
m_ndxLastKey = ndxCalcKey;
m_ndxPrevOperator = ndxCalcKey;
return;
}
/*-------------------------------------------------------------------*/
/* Ignore consecutive keypresses of +,-,*,/ */
/*-------------------------------------------------------------------*/
if ( ( ndxCalcKey != m_ndxEquals ) && ( ( m_ndxLastKey == m_ndxAdd ) ||
( m_ndxLastKey == m_ndxSubtract ) ||
( m_ndxLastKey == m_ndxMultiply ) ||
( m_ndxLastKey == m_ndxDivide ) ) )
return;
m_ndxLastKey = ndxCalcKey;
if ( m_nNumerals > 0 ) /*--------------------------*/
{ /* Ignore the keypress if */
int nEntry = evaluateRomanNumeral(); /* the user has entered no */
/* numerals. */
if ( nEntry < 0 ) /*--------------------------*/
{
m_nAccumulator = 0; /*--------------------------*/
m_nNumerals = 0; /* Make sure the entry is */
updateDisplay(); /* a valid Roman Numeral */
m_nError = nEntry; /*--------------------------*/
m_ndxPrevOperator = m_ndxAdd;
}
else
{
/*-------------------------------------------------------------*/
/* Perform the requested operation, and do a range check on */
/* the result. */
/*-------------------------------------------------------------*/
switch ( m_ndxPrevOperator )
{
case m_ndxAdd: m_nAccumulator += nEntry; break;
case m_ndxSubtract: m_nAccumulator -= nEntry; break;
case m_ndxMultiply: m_nAccumulator *= nEntry; break;
case m_ndxDivide: m_nAccumulator /= nEntry;
}
if ( m_nAccumulator < m_nMinValue )
{
m_nAccumulator = 0;
m_nNumerals = 0;
updateDisplay();
m_nError = m_nUNDERFLOW;
m_ndxPrevOperator = m_ndxAdd;
return;
}
if ( m_nAccumulator > m_nMaxValue )
{
m_nAccumulator = 0;
m_nNumerals = 0;
updateDisplay();
m_nError = m_nOVERFLOW;
m_ndxPrevOperator = m_ndxAdd;
return;
}
if ( ndxCalcKey == m_ndxEquals )
{
convertToRomanNumerals ( m_nAccumulator );
updateDisplay();
}
m_ndxPrevOperator = ndxCalcKey;
}
}
return;
}
/*----------------------------------------------------------------------*/
/* Handle the "Clear Entry" key */
/*----------------------------------------------------------------------*/
if ( ndxCalcKey == m_ndxClearEntry )
{
m_nNumerals = 0;
m_ndxLastKey = ndxCalcKey;
updateDisplay();
return;
}
/*----------------------------------------------------------------------*/
/* Handle the "Clear" key */
/*----------------------------------------------------------------------*/
if ( ndxCalcKey == m_ndxClear )
{
m_nNumerals = 0;
m_nAccumulator = 0;
m_ndxLastKey = ndxCalcKey;
m_ndxPrevOperator = m_ndxAdd;
updateDisplay();
return;
}
}
/*-**************************** updateDisplay ****************************-*/
/* */
/* Redraw the numeric display. This function is called outside of the */
/* context of a repaint. */
/* */
/*-***********************************************************************-*/
synchronized private void updateDisplay()
{
Graphics g = getGraphics();
drawDisplay ( g );
}
/*-***************************** drawDisplay *****************************-*/
/* */
/* Draw the display of the calculator. */
/* */
/*-***********************************************************************-*/
synchronized private void drawDisplay ( Graphics gComponent )
{
/*----------------------------------------------------------------------*/
/* Fill the display buffer with the background color */
/*----------------------------------------------------------------------*/
Graphics gBuffer = m_imageDisplay.getGraphics();
gBuffer.setColor ( m_colorDisplay );
gBuffer.fillRect ( 0, 0, m_nDisplayWidth, m_nDisplayHeight );
/*----------------------------------------------------------------------*/
/* Calculate the total width of the string of numerals to be displayed. */
/* Use this width to center the string in the display. */
/*----------------------------------------------------------------------*/
int nWidth = 0;
if ( m_nNumerals > 1 )
nWidth += ( m_nNumerals - 1 ) * m_nDisplayGap;
for ( int i = 0; i < m_nNumerals; i++ )
nWidth += m_imageNumeral[m_nNumeral[i]].getWidth ( null );
/*----------------------------------------------------------------------*/
/* Now draw the digits in the off-screen display buffer */
/*----------------------------------------------------------------------*/
int nX = ( m_nDisplayWidth - nWidth ) / 2;
int nY;
Image imageNumeral;
for ( int i = 0; i < m_nNumerals ; i++ )
{
imageNumeral = m_imageNumeral[m_nNumeral[i]];
nY = m_nDisplayHeight - imageNumeral.getHeight ( null ) - m_nDisplayMargin;
gBuffer.drawImage ( imageNumeral, nX, nY, null );
nX += imageNumeral.getWidth ( null ) + m_nDisplayGap;
}
/*----------------------------------------------------------------------*/
/* The offscreen image buffer is now complete; draw it to the applet */
/* window. */
/*----------------------------------------------------------------------*/
gComponent.drawImage ( m_imageDisplay, m_nDisplayX0, m_nDisplayY0, null );
}
/*-************************ convertToRomanNumerals ***********************-*/
/* */
/* Convert the argument value to a string of Roman Numerals, and place */
/* them in the display array. */
/* */
/* Argument: int nValue -- Value to be converted */
/* */
/* Return: int -- m_nUNDERFLOW: Value is less than 1 */
/* ------- m_nOVERFLOW: Value is greater than 3999999 */
/* 0: Value converted successfully. */
/* */
/*-***********************************************************************-*/
synchronized private int convertToRomanNumerals ( int nValue )
{
m_nNumerals = 0;
if ( nValue < m_nMinValue ) return m_nUNDERFLOW;
if ( nValue > m_nMaxValue ) return m_nOVERFLOW;
/*----------------------------------------------------------------------*/
/* At this point, we know that the number can be represented in Roman */
/* Numerals. Go to it. */
/*----------------------------------------------------------------------*/
int nResidual = nValue;
while ( nResidual >= 1000000 )
{
m_nNumeral[m_nNumerals] = m_ndxMbar;
m_nNumerals++;
nResidual -= 1000000;
}
if ( nResidual >= 900000 )
{
m_nNumeral[m_nNumerals] = m_ndxCbar;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxMbar;
m_nNumerals++;
nResidual -= 900000;
}
if ( nResidual >= 500000 )
{
m_nNumeral[m_nNumerals] = m_ndxDbar;
m_nNumerals++;
nResidual -= 500000;
}
if ( nResidual >= 400000 )
{
m_nNumeral[m_nNumerals] = m_ndxCbar;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxDbar;
m_nNumerals++;
nResidual -= 400000;
}
while ( nResidual >= 100000 )
{
m_nNumeral[m_nNumerals] = m_ndxCbar;
m_nNumerals++;
nResidual -= 100000;
}
if ( nResidual >= 90000 )
{
m_nNumeral[m_nNumerals] = m_ndxXbar;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxCbar;
m_nNumerals++;
nResidual -= 90000;
}
if ( nResidual >= 50000 )
{
m_nNumeral[m_nNumerals] = m_ndxLbar;
m_nNumerals++;
nResidual -= 50000;
}
if ( nResidual >= 40000 )
{
m_nNumeral[m_nNumerals] = m_ndxXbar;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxLbar;
m_nNumerals++;
nResidual -= 40000;
}
while ( nResidual >= 10000 )
{
m_nNumeral[m_nNumerals] = m_ndxXbar;
m_nNumerals++;
nResidual -= 10000;
}
if ( nResidual >= 9000 )
{
m_nNumeral[m_nNumerals] = m_ndxM;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxXbar;
m_nNumerals++;
nResidual -= 9000;
}
if ( nResidual >= 5000 )
{
m_nNumeral[m_nNumerals] = m_ndxVbar;
m_nNumerals++;
nResidual -= 5000;
}
if ( nResidual >= 4000 )
{
m_nNumeral[m_nNumerals] = m_ndxM;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxVbar;
m_nNumerals++;
nResidual -= 4000;
}
while ( nResidual >= 1000 )
{
m_nNumeral[m_nNumerals] = m_ndxM;
m_nNumerals++;
nResidual -= 1000;
}
if ( nResidual >= 900 )
{
m_nNumeral[m_nNumerals] = m_ndxC;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxM;
m_nNumerals++;
nResidual -= 900;
}
if ( nResidual >= 500 )
{
m_nNumeral[m_nNumerals] = m_ndxD;
m_nNumerals++;
nResidual -= 500;
}
if ( nResidual >= 400 )
{
m_nNumeral[m_nNumerals] = m_ndxC;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxD;
m_nNumerals++;
nResidual -= 400;
}
while ( nResidual >= 100 )
{
m_nNumeral[m_nNumerals] = m_ndxC;
m_nNumerals++;
nResidual -= 100;
}
if ( nResidual >= 90 )
{
m_nNumeral[m_nNumerals] = m_ndxX;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxC;
m_nNumerals++;
nResidual -= 90;
}
if ( nResidual >= 50 )
{
m_nNumeral[m_nNumerals] = m_ndxL;
m_nNumerals++;
nResidual -= 50;
}
if ( nResidual >= 40 )
{
m_nNumeral[m_nNumerals] = m_ndxX;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxL;
m_nNumerals++;
nResidual -= 40;
}
while ( nResidual >= 10 )
{
m_nNumeral[m_nNumerals] = m_ndxX;
m_nNumerals++;
nResidual -= 10;
}
if ( nResidual >= 9 )
{
m_nNumeral[m_nNumerals] = m_ndxI;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxX;
m_nNumerals++;
nResidual -= 9;
}
if ( nResidual >= 5 )
{
m_nNumeral[m_nNumerals] = m_ndxV;
m_nNumerals++;
nResidual -= 5;
}
if ( nResidual >= 4 )
{
m_nNumeral[m_nNumerals] = m_ndxI;
m_nNumerals++;
m_nNumeral[m_nNumerals] = m_ndxV;
m_nNumerals++;
nResidual -= 4;
}
while ( nResidual >= 1 )
{
m_nNumeral[m_nNumerals] = m_ndxI;
m_nNumerals++;
nResidual -= 1;
}
return 0;
}
/*-************************ Evaluate Roman Numeral ***********************-*/
/* */
/* Convert a Roman Numeral string to an integer. This function returns */
/* the value of the Roman Numeral or m_nINVALID. */
/* */
/*-***********************************************************************-*/
private int evaluateRomanNumeral ( )
{
int nValue = 0;
int nRepeatCount = 0;
int[] nNumeralTable = { 1, /*-----------------------------*/
5, /* */
10, /* */
50, /* */
100, /* */
500, /* This lookup table provides */
1000, /* the value of the numeral */
5000, /* associated with each index. */
10000, /* */
50000, /* */
100000, /* */
500000, /* */
1000000 }; /*-----------------------------*/
/*----------------------------------------------------------------------*/
/* Perform one iteration of this loop for each numeral in the string. */
/*----------------------------------------------------------------------*/
for ( int i = 0; i < m_nNumerals; i++ )
{
/*-------------------------------------------------------------------*/
/* Handle the first numeral in the string. */
/*-------------------------------------------------------------------*/
if ( i == 0 )
{
nValue = nNumeralTable[m_nNumeral[0]];
nRepeatCount = 1;
}
else
/*-------------------------------------------------------------------*/
/* The current numeral is identical to the numeral which immediately */
/* preceeds it. */
/*-------------------------------------------------------------------*/
if ( m_nNumeral[i] == m_nNumeral[i-1] )
{ /*-----------------------*/
if ( ( m_nNumeral[i] == m_ndxV ) || /* The only numerals that*/
( m_nNumeral[i] == m_ndxL ) || /* can repeat are powers */
( m_nNumeral[i] == m_ndxD ) || /* of ten. */
( m_nNumeral[i] == m_ndxVbar ) || /*-----------------------*/
( m_nNumeral[i] == m_ndxLbar ) ||
( m_nNumeral[i] == m_ndxDbar ) ) return m_nINVALID;
else /*--------------------*/
/* No numeral can */
if ( nRepeatCount >= 3 ) return m_nINVALID; /* repeat more than */
/* three times in a */
else /* row. */
{ /*--------------------*/
nRepeatCount++;
nValue += nNumeralTable[m_nNumeral[i]];
}
}
else
/*-------------------------------------------------------------------*/
/* If the numeral is the smallest numeral so far, it is always valid */
/*-------------------------------------------------------------------*/
if ( ( i <= 1 ) && ( m_nNumeral[i] < m_nNumeral[i-1] ) )
{
nValue += nNumeralTable[m_nNumeral[i]];
nRepeatCount = 1;
}
else
if ( ( i > 1 ) && ( m_nNumeral[i] < m_nNumeral[i-1] ) && ( m_nNumeral[i] < m_nNumeral[i-2] ) )
{
nValue += nNumeralTable[m_nNumeral[i]];
nRepeatCount = 1;
}
else
/*-------------------------------------------------------------------*/
/* */
/* A larger numeral can follow a smaller numeral only in certain */
/* cases: */
/* */
/* (1) The larger numeral must be no larger than the numeral */
/* which precedes the smaller numeral. (This implies that */
/* the smaller numeral cannot be repeated.) */
/* */
/* (2) The smaller numeral must be a power of ten. */
/* */
/* (3) The smaller numeral must be either one tenth or one fifth */
/* of the value of the larger numeral */
/* */
/*-------------------------------------------------------------------*/
if ( m_nNumeral[i] > m_nNumeral[i-1] )
{
if ( ( i > 1 ) && /*---------------*/
( m_nNumeral[i] > m_nNumeral[i-2] ) ) /* (1) */
return m_nINVALID; /*---------------*/
else
if ( ( m_nNumeral[i-1] == m_ndxV ) || /*---------------*/
( m_nNumeral[i-1] == m_ndxL ) || /* (2) */
( m_nNumeral[i-1] == m_ndxD ) || /*---------------*/
( m_nNumeral[i-1] == m_ndxVbar ) ||
( m_nNumeral[i-1] == m_ndxLbar ) ||
( m_nNumeral[i-1] == m_ndxDbar ) ) return m_nINVALID;
else
/*---------------*/
if ( m_nNumeral[i] > ( m_nNumeral[i-1] + 2 ) ) /* (3) */
return m_nINVALID; /*---------------*/
else /*------------------------------------*/
/* This is a valid case of a larger */
/* numeral following a smaller one. */
/* Set the repeat count to 3 to force */
/* a smaller numeral to be next. */
{ /*------------------------------------*/
nRepeatCount = 3;
nValue += nNumeralTable[m_nNumeral[i]];
nValue -= 2 * nNumeralTable[m_nNumeral[i-1]];
}
}
/*-------------------------------------------------------------------*/
/* If the numeral falls into none of the above cases, it is invalid */
/*-------------------------------------------------------------------*/
else return m_nINVALID;
}
return nValue;
}
/*-******************************** start ********************************-*/
/* */
/* This method is invoked every time the applet becomes active. */
/* */
/*-***********************************************************************-*/
public void start()
{
if ( m_thread == null )
{
m_thread = new Thread ( this );
m_thread.start();
}
}
/*-******************************** stop *********************************-*/
/* */
/* This method is invoked every time the applet becomes inactive. */
/* */
/*-***********************************************************************-*/
public void stop()
{
if ( m_thread != null )
{
m_thread.stop();
m_thread = null;
}
}
/*-********************************* run *********************************-*/
/* */
/* The background thread loads graphics and displays error codes. */
/* */
/*-***********************************************************************-*/
public void run()
{
/*----------------------------------------------------------------------*/
/* Load the applet's graphics the first time the background thread is */
/* run. */
/*----------------------------------------------------------------------*/
if ( ! m_bInitialized )
{
m_imageNumeral[m_ndxI ] = getImage ( getDocumentBase(), "i.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxI ], 0 );
m_imageNumeral[m_ndxV ] = getImage ( getDocumentBase(), "v.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxV ], 1 );
m_imageNumeral[m_ndxX ] = getImage ( getDocumentBase(), "x.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxX ], 2 );
m_imageNumeral[m_ndxL ] = getImage ( getDocumentBase(), "l.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxL ], 3 );
m_imageNumeral[m_ndxC ] = getImage ( getDocumentBase(), "c.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxC ], 4 );
m_imageNumeral[m_ndxD ] = getImage ( getDocumentBase(), "d.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxD ], 5 );
m_imageNumeral[m_ndxM ] = getImage ( getDocumentBase(), "m.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxM ], 6 );
m_imageNumeral[m_ndxVbar ] = getImage ( getDocumentBase(), "vbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxVbar ], 7 );
m_imageNumeral[m_ndxXbar ] = getImage ( getDocumentBase(), "xbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxXbar ], 8 );
m_imageNumeral[m_ndxLbar ] = getImage ( getDocumentBase(), "lbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxLbar ], 9 );
m_imageNumeral[m_ndxCbar ] = getImage ( getDocumentBase(), "cbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxCbar ], 10 );
m_imageNumeral[m_ndxDbar ] = getImage ( getDocumentBase(), "dbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxDbar ], 11 );
m_imageNumeral[m_ndxMbar ] = getImage ( getDocumentBase(), "mbar.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxMbar ] , 12 );
m_imageNumeral[m_ndxAdd ] = getImage ( getDocumentBase(), "add.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxAdd ], 13 );
m_imageNumeral[m_ndxSubtract ] = getImage ( getDocumentBase(), "subtract.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxSubtract ], 14 );
m_imageNumeral[m_ndxMultiply ] = getImage ( getDocumentBase(), "multiply.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxMultiply ], 15 );
m_imageNumeral[m_ndxDivide ] = getImage ( getDocumentBase(), "divide.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxDivide ], 16 );
m_imageNumeral[m_ndxEquals ] = getImage ( getDocumentBase(), "equals.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxEquals ], 17 );
m_imageNumeral[m_ndxClear ] = getImage ( getDocumentBase(), "clearall.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxClear ], 18 );
m_imageNumeral[m_ndxClearEntry] = getImage ( getDocumentBase(), "clear.gif" );
m_mediaTracker.addImage ( m_imageNumeral[m_ndxClearEntry], 19 );
m_imageBackground = getImage ( getDocumentBase(), "calcbkgd.gif" );
m_mediaTracker.addImage ( m_imageBackground, 20 );
m_imageCaesar = getImage ( getDocumentBase(), "caesar.gif" );
m_mediaTracker.addImage ( m_imageCaesar, 21 );
/*-******************** Track graphics loading *********************-*/
/* */
/* Track the progress of loading the applet graphics, and display it */
/* as a bar graph */
/*-*****************************************************************-*/
Dimension dimWin = size();
m_imageLoading = createImage ( dimWin.width, dimWin.height );
Graphics gBuffer = m_imageLoading.getGraphics();
int nFrameWidth = 200;
int nFrameHeight = 30;
int nFrameThick = 2;
int nFrameX = ( dimWin.width - nFrameWidth ) / 2;
int nFrameY = ( dimWin.height * 2 / 3 ) - ( nFrameHeight / 2 );
int nGraphX = nFrameX + nFrameThick;
int nGraphY = nFrameY + nFrameThick;
int nGraphWidth = nFrameWidth - 2 * nFrameThick;
int nGraphHeight = nFrameHeight - 2 * nFrameThick;
int nTotalImages = 22;
int nGraphedImages = -1;
int nLoadedImages;
gBuffer.setColor ( new Color ( 255, 255, 255 ) );
gBuffer.fillRect ( 0, 0, dimWin.width, dimWin.height );
/*-------------------------------------------------------------------*/
/* Initialize the bar graph by drawing the name */
/*-------------------------------------------------------------------*/
gBuffer.setColor ( new Color ( 0, 0, 0 ) );
for ( int i = 0; i < nFrameThick; i++ )
gBuffer.drawRect ( nFrameX + i,
nFrameY + i,
nFrameWidth - 2 * i - 1,
nFrameHeight - 2 * i - 1 );
gBuffer.setFont ( new Font ( "Helvetica", Font.PLAIN, 14 ) );
FontMetrics fm = gBuffer.getFontMetrics();
String stringLoading = "Loading graphics...";
int nStringX = ( dimWin.width - fm.stringWidth ( stringLoading ) ) / 2;
int nStringY = nFrameY - fm.getDescent() - 2;
gBuffer.drawString ( stringLoading, nStringX, nStringY );
/*-------------------------------------------------------------------*/
/* Display the name of the applet */
/*-------------------------------------------------------------------*/
gBuffer.setFont ( new Font ( "Helvetica", Font.BOLD, 24 ) );
fm = gBuffer.getFontMetrics();
String stringApplet = "COMPVTER ROMANVS";
nStringX = ( dimWin.width - fm.stringWidth ( stringApplet ) ) / 2;
nStringY = dimWin.height / 3;
gBuffer.drawString ( stringApplet, nStringX, nStringY );
nStringY += fm.getDescent();
gBuffer.setColor ( Color.black );
gBuffer.setFont ( new Font ( "Helvetica", Font.PLAIN, 14 ) );
fm = gBuffer.getFontMetrics();
nStringY += fm.getAscent() + fm.getLeading();
String stringCopyright = "Copyright ⌐ MCMXCVII Edward R. Hobbs";
nStringX = ( dimWin.width - fm.stringWidth ( stringCopyright ) ) / 2;
gBuffer.drawString ( stringCopyright, nStringX, nStringY );
gBuffer.dispose();
/*-------------------------------------------------------------------*/
/* Loop ten times per second, checking to see how many graphics have */
/* been loaded. Redraw the graph every time progress is detected. */
/*-------------------------------------------------------------------*/
for(;;)
{
nLoadedImages = 0;
for ( int i = 0; i < nTotalImages; i++ )
if ( m_mediaTracker.checkID ( i, true ) ) nLoadedImages++;
if ( nLoadedImages > nGraphedImages )
{
nGraphedImages = nLoadedImages;
gBuffer= m_imageLoading.getGraphics();
gBuffer.setColor ( new Color ( 0, 0, 255 ) );
gBuffer.fillRect ( nGraphX,
nGraphY,
( nGraphWidth * nGraphedImages ) / nTotalImages,
nGraphHeight );
gBuffer.dispose();
Graphics gWindow = getGraphics();
gWindow.drawImage ( m_imageLoading, 0, 0, dimWin.width, dimWin.height, null );
gWindow.dispose();
}
if ( nGraphedImages >= nTotalImages ) break;
try
{
Thread.sleep ( 100 );
}
catch ( InterruptedException e )
{
}
}
m_imageLoading.flush();
/*-*****************************************************************-*/
/* At this point, all of the graphics have been loaded. */
/*-*****************************************************************-*/
/*-------------------------------------------------------------------*/
/* Create the Calculator Keys for the Numerals */
/*-------------------------------------------------------------------*/
int nNumPadX0 = 235;
int nNumPadY0 = 65;
m_calckey[m_ndxI] = new CalcKey ( (int) ( nNumPadX0 + 1.5 * CalcKey.m_nWidth ),
nNumPadY0 + 3 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxI], 0 );
m_calckey[m_ndxV] = new CalcKey ( nNumPadX0,
nNumPadY0 + 2 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxV], 0 );
m_calckey[m_ndxX] = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
nNumPadY0 + 2 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxX], 0 );
m_calckey[m_ndxL] = new CalcKey ( nNumPadX0 + 2 *CalcKey.m_nWidth,
nNumPadY0 + 2 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxL], 0 );
m_calckey[m_ndxC] = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
nNumPadY0 + 2 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxC], 0 );
m_calckey[m_ndxD] = new CalcKey ( nNumPadX0,
nNumPadY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxD], 0 );
m_calckey[m_ndxM] = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
nNumPadY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxM], 0 );
m_calckey[m_ndxVbar] = new CalcKey ( nNumPadX0 + 2 * CalcKey.m_nWidth,
nNumPadY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxVbar], 0 );
m_calckey[m_ndxXbar] = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
nNumPadY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxXbar], 0 );
m_calckey[m_ndxLbar] = new CalcKey ( nNumPadX0,
nNumPadY0,
m_imageNumeral[m_ndxLbar], 0 );
m_calckey[m_ndxCbar] = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
nNumPadY0,
m_imageNumeral[m_ndxCbar], 0 );
m_calckey[m_ndxDbar] = new CalcKey ( nNumPadX0 + 2 * CalcKey.m_nWidth,
nNumPadY0,
m_imageNumeral[m_ndxDbar], 0 );
m_calckey[m_ndxMbar] = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
nNumPadY0,
m_imageNumeral[m_ndxMbar], 0 );
/*-------------------------------------------------------------------*/
/* Create the Calculator Keys for arithmetic operators */
/*-------------------------------------------------------------------*/
int nOperatorX0 = 395;
int nOperatorY0 = 65;
m_calckey[m_ndxAdd] = new CalcKey ( nOperatorX0,
nOperatorY0,
m_imageNumeral[m_ndxAdd], 0 );
m_calckey[m_ndxSubtract] = new CalcKey ( nOperatorX0,
nOperatorY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxSubtract], 0 );
m_calckey[m_ndxMultiply] = new CalcKey ( nOperatorX0,
nOperatorY0 + 2 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxMultiply], 0 );
m_calckey[m_ndxDivide] = new CalcKey ( nOperatorX0,
nOperatorY0 + 3 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxDivide], 0 );
/*-------------------------------------------------------------------*/
/* Create the special purpose Calculator Keys */
/*-------------------------------------------------------------------*/
int nSpecialX0 = 450;
int nSpecialY0 = 65;
m_calckey[m_ndxClear] = new CalcKey ( nSpecialX0,
nSpecialY0,
m_imageNumeral[m_ndxClear], 0 );
m_calckey[m_ndxClearEntry] = new CalcKey ( nSpecialX0,
nSpecialY0 + CalcKey.m_nHeight,
m_imageNumeral[m_ndxClearEntry], 0 );
m_calckey[m_ndxEquals] = new CalcKey ( nSpecialX0,
nSpecialY0 + 3 * CalcKey.m_nHeight,
m_imageNumeral[m_ndxEquals], 0 );
/*-------------------------------------------------------------------*/
/* Create the offscreen buffer for the calculator display. */
/*-------------------------------------------------------------------*/
m_imageDisplay = createImage ( m_nDisplayWidth, m_nDisplayHeight );
m_bInitialized = true;
repaint();
}
/*------------------------- Display Error Code -------------------------*/
/* */
/* The background thread spends most of its time in the following loop. */
/* It wakes up at intervals to check for errors. If an error is */
/* indicated, it blinks the error code in the display. */
/*----------------------------------------------------------------------*/
for(;;)
{
m_bErrorPhase = ! m_bErrorPhase;
if ( m_nError < 0 )
{
if ( m_bErrorPhase )
convertToRomanNumerals ( -m_nError );
else
m_nNumerals = 0;
updateDisplay();
}
try
{
m_thread.sleep ( 500 );
}
catch ( InterruptedException e )
{
stop();
}
}
}
}
/*-******************************* ******************************-*/
/*-******************************* CalcKey ******************************-*/
/*-* *-*/
/*-* This class defines the behavior of the calculator keys *-*/
/*-* *-*/
/*-**************************************************************************-*/
/*-**************************************************************************-*/
class CalcKey
{
/*-------------------------------------------------------------------------*/
/* Constant data members of this class */
/*-------------------------------------------------------------------------*/
public final static int m_nWidth = 35;
public final static int m_nHeight = 35;
final int m_nFrameWidth = 2;
final Color m_colorBackground = new Color ( 192, 192, 192 );
final Color m_colorFrameLight = new Color ( 255, 255, 255 );
final Color m_colorFrameDark = new Color ( 80, 80, 80 );
/*-------------------------------------------------------------------------*/
/* Variable data members of this class */
/*-------------------------------------------------------------------------*/
int m_nX;
int m_nY;
Image m_image;
boolean m_bPressed = false;
public int m_nValue;
/*-***********************************************************************-*/
/* Class Constructor */
/*-***********************************************************************-*/
public CalcKey ( int nX, int nY, Image image, int nValue )
{
m_nX = nX;
m_nY = nY;
m_image = image;
m_nValue = nValue;
}
/*-****************************** testHit ********************************-*/
/* */
/* Return true if the coordinates lie within this calculator key; return */
/* false if they do not. */
/* */
/*-***********************************************************************-*/
public boolean testHit ( int nX, int nY )
{
if ( ( nX >= m_nX ) && ( nX < ( m_nX + m_nWidth ) ) &&
( nY >= m_nY ) && ( nY < ( m_nY + m_nHeight) ) )
return true;
return false;
}
/*-***************************** setPressed ******************************-*/
/* */
/* Compare the current state of the calculator key to the argument state. */
/* If the two states disagree, update the state of the calculator key, and */
/* redraw it. */
/* */
/*-***********************************************************************-*/
public boolean setPressed ( boolean bPressed, Component component )
{
if ( bPressed != m_bPressed )
{
m_bPressed = bPressed;
Draw ( component );
return m_bPressed;
}
return false;
}
/*-******************************** Draw *********************************-*/
/* */
/* Draw a calculator key into the given Component object */
/* */
/*-***********************************************************************-*/
public void Draw ( Component component )
{
Graphics g = component.getGraphics();
Paint ( g, component );
}
/*-******************************** Paint ********************************-*/
/* */
/* Paint a calculator key into the given Graphics object */
/* */
/*-***********************************************************************-*/
public void Paint ( Graphics g, Component component )
{
/*----------------------------------------------------------------------*/
/* Draw the top and left sides of the calculator key frame. */
/*----------------------------------------------------------------------*/
if ( m_bPressed )
g.setColor ( m_colorFrameDark );
else
g.setColor ( m_colorFrameLight );
for ( int i = 0; i < m_nFrameWidth; i++ )
{
g.drawLine ( m_nX + i, m_nY + i, m_nX + m_nWidth - 1 - i, m_nY + i );
g.drawLine ( m_nX + i, m_nY + i, m_nX + i, m_nY + m_nHeight - 1 - i );
}
/*----------------------------------------------------------------------*/
/* Draw the bottom and right sides of the calculator key frame. */
/*----------------------------------------------------------------------*/
if ( m_bPressed )
g.setColor ( m_colorFrameLight );
else
g.setColor ( m_colorFrameDark );
for ( int i = 0; i < m_nFrameWidth; i++ )
{
g.drawLine ( m_nX + m_nWidth - 1 - i, m_nY + m_nHeight - 1 - i,
m_nX + i, m_nY + m_nHeight - 1 - i );
g.drawLine ( m_nX + m_nWidth - 1 - i, m_nY + m_nHeight - 1 - i,
m_nX + m_nWidth - 1 - i, m_nY + i );
}
/*----------------------------------------------------------------------*/
/* Draw the surface of the calculator key within the frame */
/*----------------------------------------------------------------------*/
g.setColor ( m_colorBackground );
g.fillRect ( m_nX + m_nFrameWidth, m_nY + m_nFrameWidth,
m_nWidth - 2 * m_nFrameWidth, m_nHeight - 2 * m_nFrameWidth );
int nX = m_nX + ( m_nWidth - m_image.getWidth ( component ) ) / 2;
int nY = m_nY + ( m_nHeight - m_image.getHeight ( component ) ) / 2;
if ( m_bPressed )
{
nX++;
nY++;
}
g.drawImage ( m_image, nX, nY, null );
}
}