package polarDust.calculator;
/*
* Copyright (c) 1996 Sorin Lazareanu, All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies.
*
*/
import java.awt.*;
/**
* NumDisplay01 is a component capable of displaying a 12 digit
* decimal number in a floating point calculator specific format.
* If the number overflows the limit of 10e13 - 1, a flashing error message is
* displayed. On the left side it has letter M as a memory content flag.
*
*
* The source code.
*
* @version 0.9, 1996.06.04
* @author Sorin Lazareanu
*/
public class NumDisplay01
extends Canvas
implements
CalcDisplay,
Runnable,
CalcConstants {
static final long lMaxDigitsFactor = 100000000000L; // 10e11d
static final int iMaxDigits = 12;
boolean bM = false;
boolean bNewThread = true, bSuspended = true;
boolean bTick = true;
boolean bError = false;
int iLastOp = 0;
Thread thisThread = new Thread(this, "Blinking Display");
String text= " 0.";
Dimension bufferDimension = new Dimension(0, 0);
Image bufferImage;
Graphics bufferGraphics;
PrinterPanel01 printer;
/**
* Constructs a new display.
*/
public NumDisplay01() {
super();
}
/**
* Constructs a new display, driver for a printer.
* @param aPrinter the calculator printer attached.
* @see PrinterPanel01
*/
public NumDisplay01(PrinterPanel01 aPrinter) {
super();
printer = aPrinter;
}
/**
* @return the factor for a 12 digits display is 10e11
*/
public long maxDigitsFactor() {
return lMaxDigitsFactor;
}
/**
* Displays the parameter. No output to calculator printer.
* @param dReg1 the number to be displayed.
* @return the number as represented on the display component.
*/
public double show(double dReg1) {
double dResult;
dResult = showNumber(dReg1, -1);
return dResult;
}
/**
* Displays the parameter with output to calculator printer if exists.
* @param dReg1 the number to be displayed.
* @return the number as represented on the display component.
*/
public double assign(double dReg1) {
double dResult;
if (printer != null)
printer.showResult(text);
dResult = showNumber(dReg1, -1);
return dResult;
}
/**
* Oputputs the operator to the calculator printer.
* @param oper the representation of the operator to be printed.
*/
public void showOp(String oper) {
if (printer != null)
printer.showOp(oper);
}
/**
* Displays the parameter. No output to calculator printer.
* @param dReg1 the number to be displayed.
* @param iDecimals the number of digits after the decimal point.
* @return the number as represented on the display component.
*/
public double showWithDecimals(double dReg1, int iDecimals) {
double dResult;
dResult = showNumber(dReg1, iDecimals);
return dResult;
}
/**
* Displays the parameter with output to calculator printer.
* @param dReg1 the number to be displayed.
* @param iDecimals the number of digits after the decimal point.
* @return the number as represented on the display component.
*/
public double assignWithDecimals(double dReg1, int iDecimals) {
double dResult;
if (printer != null)
printer.showResult(text);
dResult = showNumber(dReg1, iDecimals);
return dResult;
}
/**
* Converts a double to a string format suitable for a NumDisplay
* @param dReg1 the number to be displayed.
* @param iDecimals the number of digits after the decimal point.
* @return the representation.
*/
protected synchronized String toFloatString(double dReg1, int iDecimals){
int i;
char cDec[];
double dAbs = Math.abs(dReg1);
long lInt, lDec, lI = 1;
StringBuffer num = new StringBuffer(""), dec;
StringBuffer paddedNum = new StringBuffer("");
iDecimals = Math.min(iMaxDigits, iDecimals);
if (dAbs >= lMaxDigitsFactor * 10) {
num.append("Error");
}
else {
num.append(dReg1 >= 0?"":"-");
lInt = (long)dAbs;
num.append(lInt + ".");
while (dAbs > lI) lI *= 10L;
lDec = Math.round((dAbs - lInt + 1) * lMaxDigitsFactor * 100 / lI);
// * 1000 - 10 from (... + 1)
// - 10 increase the internal precision by 1 digit ( not finished)
lDec = lDec / 10 + ((lDec - lDec / 10 * 10 > 5)?1:0);
dec = new StringBuffer(new Long(lDec).toString());
while (
dec.length() > 1 &&
dec.charAt( dec.length() - 1 ) == '0' &&
(dec.length() - 1) > iDecimals
) {
dec.setLength( Math.max(1, dec.length() - 1 ));
}
if (iDecimals != 0 && dec.length() > 1) {
cDec = new char[ dec.length() - 1 ];
dec.getChars(1, dec.length(), cDec, 0);
num.append(cDec);
}
}
i = iMaxDigits + 2 - num.length();
while (i-- > 0) {
paddedNum.append(' ');
};
paddedNum.append(num);
return new String(paddedNum);
}
protected double showNumber(double dReg1, int iDecimals){
String num = toFloatString(dReg1, iDecimals);
text = num;
if (num.endsWith("Error")) {
bError = true;
if (printer != null) printer.showResult(text);
if (bNewThread) {
start();
bNewThread = false;
}
resume();
} else {
bError = false;
if (!bNewThread) suspend();
}
repaint();
try {
dReg1 = new Double(num).doubleValue();
} catch (NumberFormatException e){ dReg1 = 0d; }
return dReg1;
}
/**
* @return the last operation generated an error (overflow).
*/
public boolean isError() {
return bError;
}
/**
* @param a number
* @return the parameter has a zero value representation on display.
*/
public boolean isNotZero(double dReg) {;
String num = toFloatString(dReg, -1);
try {
dReg = new Double(num).doubleValue();
} catch (NumberFormatException e) { dReg = 0d; };
return dReg == 0d;
}
/**
* Shows the letter M if the parameter is not represented as a zero value.
*/
public double showM(double dRegM) {
String num = toFloatString(dRegM, -1);
try {
dRegM = new Double(num).doubleValue();
} catch (NumberFormatException e) {
text = num;
dRegM = 0d;
}
bM = dRegM != 0d;
repaint();
return dRegM;
}
/**
* Sends a string to be displayed.
*/
public void setNumber( String aString ) {
text = aString;
repaint();
}
public Dimension preferredSize() {
return new Dimension(155, 32);
}
public Dimension minimumSize() {
return preferredSize();
}
/**
* Start the thread for blinking.
*/
public synchronized void start() {
if (!isAlive()) {
thisThread.setDaemon(true);
thisThread.start();
bSuspended = false;
}
}
/**
* Stops the thread for blinking.
*/
public void stop() {
if (isAlive()) {
thisThread.stop();
bNewThread = false;
};
}
/**
* Resume or suspends the thread for blinking.
*/
public void toggleRunnable() {
if (isAlive()) {
try {
if (bSuspended) resume();
else suspend();
} catch (IllegalThreadStateException e) { }
}
}
public void suspend() {
if (isAlive()) {
try { //catch does'n work - no exception thrown ver1.0
thisThread.suspend();
bSuspended = true;
bTick = true;
repaint();
} catch (IllegalThreadStateException e) { }
}
}
public boolean isAlive() {
return thisThread.isAlive();
}
public void resume() {
if (isAlive()) {
try { //catch does'n work - no exception thrown ver1.0
thisThread.resume();
bSuspended = false;
} catch (IllegalThreadStateException e) { }
}
}
public void run() {
long lTickDuration = 250L;
long lLastmS = System.currentTimeMillis();
while (thisThread != null) {
try {
if (bufferGraphics != null) {
bTick = !bTick;
}
thisThread.sleep(
lTickDuration -
Math.max(
0L,
Math.min(
lTickDuration,
System.currentTimeMillis() - lLastmS
)
)
);
lLastmS = System.currentTimeMillis();
// Should repaint() be called only when Screen Updater thread is running ?
// No specs. on Screen Updater.
// The thread must be stopped before System.exit(...).
repaint();
} catch (InterruptedException e) {
break;
}
};
}
public void paint(Graphics aGraphics) {
update(aGraphics);
}
public synchronized void update(Graphics aGraphics) {
int iWidth = size().width;
int iHeight = size().height;
int i, iBeginX, iBeginY;
int iStringWidth, iFontHeight;
int iFontNewSize;
int iMaxScreenWidth, iMaxScreenHeight;
int iScreenWidth, iScreenHeight;
StringBuffer aStringBuffer = new StringBuffer("");
String aString;
FontMetrics currentFontMetrics;
aStringBuffer.append(bM?'M':' ');
i = iMaxDigits + 2 - (bTick?text.length():0);
while (i-- > 0)
aStringBuffer.append(' ');
aStringBuffer.append(bTick?text:"");
aString = new String(aStringBuffer);
iMaxScreenWidth = iWidth - 4;
iMaxScreenHeight = iHeight - 4;
if (
bufferDimension.width != iWidth ||
bufferDimension.height != iHeight
) {
bufferDimension = size();
bufferImage = createImage(iWidth, iHeight);
bufferGraphics = bufferImage.getGraphics();
// min. font size = 15, min. string width = 136 (15chars)
iStringWidth = bufferGraphics.getFontMetrics().stringWidth(aString);
iFontHeight = bufferGraphics.getFontMetrics().getAscent();
iFontNewSize = bufferGraphics.getFont().getSize() *
Math.min(iMaxScreenWidth / iStringWidth, iMaxScreenHeight / iFontHeight) + 5;
do { // we must loop here since the font size is not a precise defined measure.
iFontNewSize--;
bufferGraphics.setFont(new Font("Courier", Font.BOLD, iFontNewSize));
currentFontMetrics = bufferGraphics.getFontMetrics();
iStringWidth = currentFontMetrics.stringWidth(aString); //15chars
iFontHeight = currentFontMetrics.getAscent();
} while (iStringWidth > iMaxScreenWidth || iFontHeight > iMaxScreenHeight);
setFont(new Font("Courier", Font.BOLD, iFontNewSize));
aGraphics.setFont(new Font("Courier", Font.BOLD, iFontNewSize));
}
currentFontMetrics = bufferGraphics.getFontMetrics();
bufferGraphics.setColor(getBackground());
bufferGraphics.fillRect(0, 0, iWidth, iHeight);
bufferGraphics.setColor(Color.gray);
bufferGraphics.fillRoundRect(0, 0, iWidth - 1, iHeight - 1, 15, 15);
bufferGraphics.setColor(Color.black);
bufferGraphics.drawRoundRect(0, 0, iWidth - 1, iHeight - 1, 15, 15);
bufferGraphics.drawRoundRect(1, 1, iWidth - 3, iHeight - 3, 14, 14);
iScreenWidth = currentFontMetrics.stringWidth(aString) + 2;
iScreenHeight = currentFontMetrics.getAscent()+ 2;
iBeginX = (iWidth + 1 - iScreenWidth) / 2; // + 1 for rounding
iBeginY = (iHeight + 1 - iScreenHeight ) / 2; // + 1 for rounding
bufferGraphics.setColor(Color.black);
bufferGraphics.fillRoundRect(
iBeginX,
iBeginY,
iScreenWidth,
iScreenHeight,
13,
13
);
bufferGraphics.setColor(Color.green.brighter());
bufferGraphics.drawString(
aString,
iBeginX + 3,
(iHeight + getFontMetrics(getFont()).getAscent() + 1) / 2 - 2
);
aGraphics.drawImage(bufferImage, 0, 0, this);
}
}