mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
19 KiB
637 lines
19 KiB
/****************************Module*Header***********************************\
|
|
* Module Name: SCICOMM.C
|
|
*
|
|
* Module Descripton:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* Created:
|
|
*
|
|
* Author:
|
|
\****************************************************************************/
|
|
|
|
#include "scicalc.h"
|
|
#include "calchelp.h"
|
|
#include "unifunc.h"
|
|
#include "input.h"
|
|
|
|
extern HWND hgWnd, hStatBox;
|
|
extern double fpNum, fpLastNum, fpParNum[25], fpPrecNum[25], fpMem;
|
|
extern INT nCalc, nRadix, nTempCom, nParNum, nPrecNum,
|
|
nOpCode, nTrig, nOp[25], nPrecOp[25], nDecMode, nHexMode;
|
|
extern BOOL bHyp, bInv, bError, bFE;
|
|
extern TCHAR szBlank[6];
|
|
extern DWORD dwChop;
|
|
extern DWORD aIds[];
|
|
|
|
int nLastCom; // Last command entered.
|
|
CALCINPUTOBJ gcio; // Global calc input object for decimal strings
|
|
BOOL gbRecord = TRUE; // Global mode: recording or displaying
|
|
|
|
/* Process all keyclicks whether by mouse or accelerator. */
|
|
VOID NEAR ProcessCommands(WPARAM wParam)
|
|
{
|
|
static BOOL bNoPrevEqu=TRUE, /* Flag for previous equals. */
|
|
bChangeOp=FALSE; /* Flag for changing operation. */
|
|
static INT nNeg=1; /* Current sign either 1 or -1. */
|
|
double fpSec, fpSave;
|
|
static double fpHold;
|
|
double fpTemp;
|
|
INT nx, ni;
|
|
TCHAR szJunk[50], szTemp[50];
|
|
static DWORD dwLens[3]={(DWORD)-1, 0xFFFF, 0xFF};
|
|
static BYTE rgbPrec[24]={0,0, OR,0, XOR,0, AND,1, ADD,2, SUB,2,
|
|
RSHF, 3, LSHF,3, MOD,3, DIV,3, MUL, 3, PWR, 4};
|
|
|
|
// Make sure we're only getting commands we understand.
|
|
|
|
ASSERT( xwParam('0', '9') ||
|
|
xwParam('A', 'F') ||
|
|
xwParam('(', ')') ||
|
|
xwParam(SIGN, PNT) ||
|
|
xwParam(AND, PWR) ||
|
|
xwParam(CHOP, EQU) ||
|
|
xwParam(MCLEAR, MPLUS) ||
|
|
xwParam(EXP, EXP) ||
|
|
xwParam(AVE, DATA) ||
|
|
xwParam(BIN, HEX) ||
|
|
xwParam(INV, HYP) ||
|
|
xwParam(DEG, GRAD) ||
|
|
xwParam(IDM_COPY, IDM_PASTE) ||
|
|
xwParam(IDM_ABOUT, IDM_ABOUT) ||
|
|
xwParam(IDM_SC, IDM_SSC) ||
|
|
xwParam(IDM_HELPTOPICS, IDM_HELPTOPICS) ||
|
|
xwParam(ID_ED, ID_ED)
|
|
);
|
|
|
|
// Save the last command.
|
|
|
|
if (wParam!=INV && wParam!=HYP && wParam!=STAT && wParam!=FE
|
|
&& wParam!=MCLEAR && wParam!=BACK && wParam!=DEG && wParam!=RAD
|
|
&& wParam !=GRAD && wParam<256 && wParam >=32 && wParam!=EXP)
|
|
{
|
|
nLastCom=nTempCom;
|
|
nTempCom=wParam;
|
|
}
|
|
|
|
// If error and not a clear key or help key, BEEP.
|
|
|
|
if (bError && (wParam !=CLEAR) && (wParam !=CENTR) && (wParam != IDM_HELPTOPICS))
|
|
{
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
|
|
// Toggle Record/Display mode if appropriate.
|
|
|
|
if (gbRecord)
|
|
{
|
|
if (wParam == TEXT(')') ||
|
|
xwParam(AND, MPLUS) ||
|
|
xwParam(AVE, HEX) ||
|
|
wParam == IDM_PASTE)
|
|
{
|
|
gbRecord = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isxu(wParam) || wParam == PNT)
|
|
{
|
|
gbRecord = TRUE;
|
|
CIO_vClear(&gcio);
|
|
}
|
|
}
|
|
|
|
/* If last command was a function and new key is a digit, then kill */
|
|
/* the environment/reset the numbers. This is so digits entered */
|
|
/* after a function (without and operator in between) do not get */
|
|
/* added on to the display. */
|
|
if (isxu(wParam) || wParam==PNT || wParam == TEXT('('))
|
|
if ((nLastCom >=CHOP && nLastCom<=HEX)
|
|
|| (nLastCom==TEXT(')') && nParNum==0)
|
|
|| wParam==IDM_PASTE)
|
|
{
|
|
fpNum=fpTemp=fpLastNum=0.0;
|
|
nOpCode=nLastCom=FALSE;
|
|
nNeg=bNoPrevEqu=1;
|
|
|
|
#ifdef DBCS //KKBUGFIX
|
|
// #1307: 11/16/1992: added displays ZERO if it is cleared
|
|
DisplayNum() ;
|
|
#endif
|
|
}
|
|
|
|
// Interpret hexidecimal keys.
|
|
|
|
if (isxu(wParam))
|
|
{
|
|
int iValue = CONV(wParam);
|
|
|
|
if (iValue >= nRadix)
|
|
{
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
|
|
if (nRadix == 10)
|
|
{
|
|
if (!CIO_bAddDigit(&gcio, iValue))
|
|
{
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fpNum >= (1e+308/(double)nRadix) || fpNum <= (-1e+308/(double)nRadix))
|
|
{
|
|
DisplayError (SCERR_OVERFLOW);
|
|
return;
|
|
}
|
|
fpNum = fpNum * (double)nRadix + (double)nNeg * (double)iValue;
|
|
}
|
|
|
|
DisplayNum();
|
|
return;
|
|
}
|
|
|
|
if (xwParam(AVE,DATA))
|
|
{
|
|
/* Do statistics functions on data in fpStatNum array. */
|
|
if (hStatBox)
|
|
{
|
|
DisplayNum(); // Make sure szfpNum has the correct string
|
|
StatFunctions (wParam);
|
|
if (!bError)
|
|
DisplayNum ();
|
|
}
|
|
else
|
|
/* Beep if the stat box is not active. */
|
|
MessageBeep(0);
|
|
|
|
/* Reset the inverse flag since some functions use it. */
|
|
SetBox (INV, bInv=FALSE);
|
|
return;
|
|
}
|
|
|
|
if (xwParam(AND,PWR))
|
|
{
|
|
if (bInv && wParam==LSHF)
|
|
{
|
|
SetBox (INV, bInv=FALSE);
|
|
wParam=RSHF;
|
|
}
|
|
|
|
/* Change the operation if last input was operation. */
|
|
if (nLastCom >=AND && nLastCom <=PWR)
|
|
{
|
|
nOpCode=wParam;
|
|
return;
|
|
}
|
|
|
|
/* bChangeOp is true if there was an operation done and the */
|
|
/* current fpNum is the result of that operation. This is so */
|
|
/* entering 3+4+5= gives 7 after the first + and 12 after the */
|
|
/* the =. The rest of this stuff attempts to do precedence in*/
|
|
/* Scientific mode. */
|
|
if (bChangeOp)
|
|
{
|
|
DoPrecedenceCheckAgain:
|
|
|
|
nx=0;
|
|
while (wParam!=rgbPrec[nx*2] && nx <12)
|
|
nx++;
|
|
|
|
ni=0;
|
|
while (nOpCode!=rgbPrec[ni*2] && ni <12)
|
|
ni++;
|
|
|
|
if (nx==12) nx=0;
|
|
if (ni==12) ni=0;
|
|
|
|
if (rgbPrec[nx*2+1] > rgbPrec[ni*2+1] && nCalc==0)
|
|
{
|
|
if (nPrecNum <25)
|
|
{
|
|
fpPrecNum[nPrecNum]=fpLastNum;
|
|
nPrecOp[nPrecNum]=nOpCode;
|
|
}
|
|
else
|
|
{
|
|
nPrecNum=24;
|
|
MessageBeep(0);
|
|
}
|
|
nPrecNum++;
|
|
}
|
|
else
|
|
{
|
|
/* do the last operation and then if the precedence array is not
|
|
* empty or the top is not the '(' demarcator then pop the top
|
|
* of the array and recheck precedence against the new operator
|
|
*/
|
|
|
|
fpNum=DoOperation(nOpCode, fpLastNum);
|
|
if ((nPrecNum !=0) && (nPrecOp[nPrecNum-1]))
|
|
{
|
|
nPrecNum--;
|
|
nOpCode=nPrecOp[nPrecNum] ;
|
|
fpLastNum=fpPrecNum[nPrecNum];
|
|
goto DoPrecedenceCheckAgain ;
|
|
}
|
|
|
|
if (!bError)
|
|
DisplayNum ();
|
|
}
|
|
}
|
|
else
|
|
DisplayNum(); // Causes 3.000 to shrink to 3. on first op.
|
|
|
|
fpLastNum=fpNum;
|
|
fpNum=0.0;
|
|
nNeg=1;
|
|
nOpCode=wParam;
|
|
bNoPrevEqu=bChangeOp=TRUE;
|
|
return;
|
|
}
|
|
|
|
if (xwParam(CHOP,PERCENT))
|
|
{
|
|
/* Functions are unary operations. */
|
|
|
|
/* If the last thing done was an operator, fpNum was cleared. */
|
|
/* In that case we better use the number before the operator */
|
|
/* was entered, otherwise, things like 5+ 1/x give Divide By */
|
|
/* zero. This way 5+=gives 10 like most calculators do. */
|
|
if (nLastCom >=AND && nLastCom <=PWR)
|
|
fpNum=fpLastNum;
|
|
|
|
SciCalcFunctions (wParam);
|
|
|
|
if (bError)
|
|
return;
|
|
|
|
/* Display the result, reset flags, and reset indicators. */
|
|
DisplayNum ();
|
|
|
|
/* reset the bInv and bHyp flags and indicators if they are set
|
|
and have been used */
|
|
|
|
if (bInv && (wParam == CHOP || wParam == SIN || wParam == COS ||
|
|
wParam == TAN || wParam == SQR || wParam == CUB ||
|
|
wParam == LOG || wParam == LN || wParam == DMS))
|
|
{
|
|
bInv=FALSE;
|
|
SetBox (INV, FALSE);
|
|
}
|
|
|
|
if (bHyp && (wParam == SIN || wParam == COS || wParam == TAN))
|
|
{
|
|
bHyp = FALSE ;
|
|
SetBox (HYP, FALSE);
|
|
}
|
|
bNoPrevEqu=TRUE;
|
|
nNeg=1;
|
|
return;
|
|
}
|
|
|
|
if (xwParam(BIN,HEX))
|
|
{
|
|
// Change radix and update display.
|
|
if (nCalc==1)
|
|
wParam=DEC;
|
|
|
|
// Play with the Help Array
|
|
// Since we're changing the Radix
|
|
if ( wParam != DEC )
|
|
{
|
|
|
|
aIds[1] = CALC_SCI_DWORD;
|
|
aIds[3] = CALC_SCI_WORD;
|
|
aIds[5] = CALC_SCI_BYTE;
|
|
}
|
|
else
|
|
{
|
|
aIds[1] = CALC_SCI_DEG;
|
|
aIds[3] = CALC_SCI_RAD;
|
|
aIds[5] = CALC_SCI_GRAD;
|
|
}
|
|
|
|
SetRadix(wParam);
|
|
return;
|
|
}
|
|
|
|
/* Now branch off to do other commands and functions. */
|
|
switch(wParam)
|
|
{
|
|
case IDM_COPY:
|
|
case IDM_PASTE:
|
|
case IDM_ABOUT:
|
|
case IDM_SC:
|
|
case IDM_SSC:
|
|
case IDM_HELPTOPICS:
|
|
// Jump to menu command handler in scimenu.c.
|
|
MenuFunctions(wParam);
|
|
DisplayNum();
|
|
break;
|
|
|
|
case CLEAR: /* Total clear. */
|
|
fpTemp=fpLastNum=0.0;
|
|
nPrecNum=nTempCom=nLastCom=nOpCode=nParNum=bFE=bChangeOp=FALSE;
|
|
bNoPrevEqu=TRUE;
|
|
|
|
/* clear the paranthesis status box indicator, this will not be
|
|
cleared for CENTR */
|
|
|
|
SetDlgItemText(hgWnd, PARTEXT, szBlank);
|
|
|
|
/* fall through */
|
|
|
|
case CENTR: /* Clear only temporary values. */
|
|
fpNum=0.0;
|
|
nNeg=1;
|
|
|
|
if (!nCalc)
|
|
{
|
|
EnableToggles (TRUE);
|
|
/* Clear the INV, HYP indicators & leave (=xx indicator active */
|
|
SetBox (INV, bInv=FALSE);
|
|
SetBox (HYP, bHyp=FALSE);
|
|
}
|
|
|
|
bError=FALSE;
|
|
CIO_vClear(&gcio);
|
|
gbRecord = TRUE;
|
|
DisplayNum ();
|
|
break;
|
|
|
|
case STAT: /* Shift focus to Statistix Box if it's active. */
|
|
if (hStatBox)
|
|
SetFocus(hStatBox);
|
|
else
|
|
SetStat (TRUE);
|
|
break;
|
|
|
|
case BACK:
|
|
// Divide number by the current radix and truncate.
|
|
// Only allow backspace if we're recording.
|
|
if (gbRecord)
|
|
{
|
|
if (nRadix == 10)
|
|
{
|
|
if (!CIO_bBackspace(&gcio))
|
|
MessageBeep(0);
|
|
}
|
|
else
|
|
{
|
|
// Nuke the last whole digit by simple division.
|
|
CalcModF(fpNum / (double) nRadix, &fpNum);
|
|
if (fpNum == 0.0)
|
|
nNeg = 1;
|
|
}
|
|
DisplayNum();
|
|
}
|
|
else
|
|
MessageBeep(0);
|
|
break;
|
|
|
|
/* EQU enables the user to press it multiple times after and */
|
|
/* operation to enable repeats of the last operation. I don't */
|
|
/* know if I can explain what the hell I did here... */
|
|
case EQU:
|
|
do {
|
|
/* Last thing keyed in was an operator. Lets do the op on*/
|
|
/* a duplicate of the last entry. */
|
|
if (nLastCom >=AND && nLastCom <=PWR)
|
|
fpNum=fpLastNum;
|
|
|
|
if (nOpCode) /* Is there a valid operation around? */
|
|
{
|
|
/* If this is the first EQU in a string, set fpHold=fpNum */
|
|
/* Otherwise let fpNum=fpTemp. This keeps fpNum constant */
|
|
/* through all EQUs in a row. */
|
|
(bNoPrevEqu) ? (fpHold=fpNum):(fpNum=fpHold);
|
|
|
|
/* Do the current or last operation. */
|
|
fpNum=fpLastNum=DoOperation (nOpCode,fpLastNum);
|
|
|
|
/* Check for errors. If this wasn't done, DisplayNum */
|
|
/* would immediately overwrite any error message. */
|
|
if (!bError)
|
|
DisplayNum ();
|
|
|
|
/* No longer the first EQU. */
|
|
bNoPrevEqu=FALSE;
|
|
}
|
|
else if (!bError)
|
|
DisplayNum();
|
|
|
|
if (nPrecNum==0 || nCalc==1)
|
|
break;
|
|
|
|
nOpCode=nPrecOp[--nPrecNum];
|
|
fpLastNum=fpPrecNum[nPrecNum];
|
|
bNoPrevEqu=TRUE;
|
|
} while (nPrecNum >= 0);
|
|
|
|
bChangeOp=FALSE;
|
|
break;
|
|
|
|
|
|
case TEXT('('):
|
|
case TEXT(')'):
|
|
nx=0;
|
|
if (wParam==TEXT('('))
|
|
nx=1;
|
|
|
|
#ifdef DBCS //KKBUGFIX /*sync ver3.0a t-Yoshio*/
|
|
if ((nParNum >= 25 && nx) || (!nParNum && !nx)
|
|
|| (nPrecNum >= 25 && nPrecOp[nPrecNum-1]!=0))
|
|
#else
|
|
if ((nParNum >= 25 && nx) || (!nParNum && !nx))
|
|
#endif
|
|
{
|
|
MessageBeep(0);
|
|
return;
|
|
}
|
|
|
|
if (nx)
|
|
{
|
|
/* Open level of parentheses, save number and operation. */
|
|
fpParNum[nParNum]=fpLastNum;
|
|
nOp[nParNum++]=nOpCode;
|
|
|
|
/* save a special marker on the precedence array */
|
|
nPrecOp[nPrecNum++]=0 ;
|
|
|
|
fpLastNum=0.0; /* Reset number and operation. */
|
|
nTempCom=0;
|
|
nOpCode=ADD;
|
|
}
|
|
else
|
|
{
|
|
/* Get the operation and number and return result. */
|
|
fpNum=DoOperation (nOpCode, fpLastNum);
|
|
|
|
/* now process the precedence stack till we get to an
|
|
opcode which is zero. */
|
|
|
|
while (nOpCode = nPrecOp[--nPrecNum])
|
|
{
|
|
fpLastNum=fpPrecNum[nPrecNum];
|
|
fpNum=DoOperation (nOpCode,fpLastNum);
|
|
}
|
|
|
|
/* now get back the operation and opcode at the beigining
|
|
of this paranthesis pair */
|
|
|
|
fpLastNum=fpParNum[--nParNum];
|
|
nOpCode=nOp[nParNum];
|
|
|
|
/* if nOpCode is a valid operator then set bChangeOp to
|
|
be true else set it false */
|
|
|
|
if (nOpCode)
|
|
bChangeOp=TRUE;
|
|
else
|
|
bChangeOp=FALSE ;
|
|
}
|
|
|
|
/* Set the "(=xx" indicator. */
|
|
lstrcpy(szJunk, TEXT("(="));
|
|
lstrcat(szJunk, MyItoa(nParNum, szTemp, 10));
|
|
SetDlgItemText(hgWnd, PARTEXT, (nParNum) ? (szJunk) : (szBlank));
|
|
|
|
if (bError)
|
|
break;
|
|
|
|
if (nx)
|
|
{
|
|
/* Build a display string of nParNum "("'s. */
|
|
for (nx=0; nx < nParNum; nx++)
|
|
szJunk[nx]=TEXT('(');
|
|
|
|
szJunk[nx]=0; /* Null-terminate. */
|
|
SetDlgItemText(hgWnd, DISPLAY+nCalc, szJunk);
|
|
bChangeOp=FALSE;
|
|
}
|
|
else
|
|
DisplayNum ();
|
|
break;
|
|
|
|
case DEG:
|
|
case RAD:
|
|
case GRAD:
|
|
nTrig = wParam-DEG;
|
|
|
|
if (nRadix==10)
|
|
nDecMode=nTrig;
|
|
else
|
|
{
|
|
dwChop=dwLens[nTrig];
|
|
nHexMode=nTrig;
|
|
}
|
|
|
|
CheckRadioButton(hgWnd, DEG, GRAD, nTrig+DEG);
|
|
DisplayNum ();
|
|
break;
|
|
|
|
case SIGN:
|
|
// Change the sign.
|
|
if (gbRecord && nRadix == 10)
|
|
CIO_vToggleSign(&gcio);
|
|
else
|
|
fpNum = -fpNum;
|
|
nNeg = -nNeg;
|
|
DisplayNum();
|
|
break;
|
|
|
|
case RECALL:
|
|
/* Recall immediate memory value. */
|
|
fpNum=fpMem;
|
|
|
|
if (fpNum <0.0)
|
|
nNeg=-1;
|
|
else
|
|
nNeg=1;
|
|
|
|
DisplayNum ();
|
|
break;
|
|
|
|
case MPLUS:
|
|
/* MPLUS adds fpNum to immediate memory and kills the "mem" */
|
|
/* indicator if the result is zero. */
|
|
|
|
if (fpNum > 0.0 && fpMem > 0.0)
|
|
{
|
|
fpSave=fpNum/10.0;
|
|
fpSec=fpMem/10.0;
|
|
|
|
if (fpSave+fpSec > 1e+307)
|
|
{
|
|
DisplayError (SCERR_OVERFLOW);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fpMem+=fpNum;
|
|
SetDlgItemText(hgWnd,MEMTEXT+nCalc, (fpMem) ? (TEXT(" M")):(szBlank));
|
|
break;
|
|
|
|
case STORE:
|
|
case MCLEAR:
|
|
if (wParam==STORE)
|
|
{
|
|
fpMem=fpNum;
|
|
}
|
|
else
|
|
{
|
|
fpMem=0.0;
|
|
}
|
|
SetDlgItemText(hgWnd,MEMTEXT+nCalc,(fpMem) ? (TEXT(" M")):(szBlank));
|
|
break;
|
|
|
|
case PI:
|
|
if (nRadix==10)
|
|
{
|
|
/* Return PI if bInv==FALSE, or 2PI if bInv==TRUE. */
|
|
fpNum = (bInv) ? (2.0 * PI_VAL) : (PI_VAL);
|
|
DisplayNum();
|
|
SetBox(INV, bInv=FALSE);
|
|
}
|
|
else
|
|
MessageBeep(0);
|
|
break;
|
|
|
|
case FE:
|
|
// Toggle exponential notation display.
|
|
bFE=!bFE;
|
|
DisplayNum();
|
|
break;
|
|
|
|
case EXP:
|
|
if (gbRecord && nRadix == 10)
|
|
if (CIO_bExponent(&gcio))
|
|
{
|
|
DisplayNum();
|
|
return;
|
|
}
|
|
MessageBeep(0);
|
|
break;
|
|
|
|
case PNT:
|
|
if (gbRecord && nRadix == 10)
|
|
if (CIO_bAddDecimalPt(&gcio))
|
|
return;
|
|
MessageBeep(0);
|
|
break;
|
|
|
|
case INV:
|
|
SetBox(wParam, bInv=!bInv);
|
|
break;
|
|
|
|
case HYP:
|
|
SetBox(wParam, bHyp=!bHyp);
|
|
break;
|
|
}
|
|
}
|