|
|
/****************************Module*Header***********************************\
* Module Name: SCICOMM.C * * Module Descripton: * * Warnings: * * Created: * * Author: \****************************************************************************/
#include "scicalc.h"
#include "calchelp.h"
#include "unifunc.h"
#include "input.h"
#include "strsafe.h"
extern HWND hStatBox; extern HNUMOBJ ghnoNum, ghnoLastNum, ghnoMem; extern HNUMOBJ ghnoParNum[25], ghnoPrecNum[25];
extern eNUMOBJ_FMT nFE; extern INT nTempCom, nParNum, nPrecNum, gcIntDigits, nOpCode, nOp[25], nPrecOp[25]; extern BOOL bError; extern TCHAR szBlank[6]; extern TCHAR *rgpsz[CSTRINGS];
int nLastCom; // Last command entered.
CALCINPUTOBJ gcio; // Global calc input object for decimal strings
BOOL gbRecord; // Global mode: recording or displaying
/* Puts up the wait cursor if the calc will take a long time */ HCURSOR ghcurOld = NULL;
BOOL SetWaitCursor( BOOL fOn ) { if (fOn && ghcurOld == NULL) { ghcurOld = SetCursor( LoadCursor(NULL, IDC_WAIT) ); } else if (!fOn && ghcurOld != NULL) { SetCursor( ghcurOld ); ghcurOld = NULL; }
return (fOn && ghcurOld != NULL); }
/* Process all keyclicks whether by mouse or accelerator. */ VOID NEAR RealProcessCommands(WPARAM wParam);
VOID NEAR ProcessCommands(WPARAM wParam) { if (wParam != IDM_ABOUT) { TimeCalc(TRUE); }
try { RealProcessCommands( wParam ); } catch( ... ) { // note: it should be impossible for a throw to reach this level, this is put here as an
// emergency backup only. Throws are normally caught at the boundry between calc and ratpak.
ASSERT( 0 ); MessageBox( g_hwndDlg, TEXT("An unknown error has occured."), TEXT("Error"), MB_OK ); }
if (wParam != IDM_ABOUT) { TimeCalc(FALSE); } }
VOID NEAR RealProcessCommands(WPARAM wParam) { static BOOL bNoPrevEqu=TRUE, /* Flag for previous equals. */ bChangeOp=FALSE; /* Flag for changing operation. */ INT nx, ni; TCHAR szJunk[50], szTemp[50]; static BYTE rgbPrec[24]={ 0,0, IDC_OR,0, IDC_XOR,0, IDC_AND,1, IDC_ADD,2, IDC_SUB,2, RSHF,3, IDC_LSHF,3, IDC_MOD,3, IDC_DIV,3, IDC_MUL,3, IDC_PWR,4};
// Make sure we're only getting commands we understand.
ASSERT( xwParam(IDC_FIRSTCONTROL, IDC_LASTCONTROL) || // Is it a button?
xwParam(IDM_FIRSTMENU, IDM_LASTMENU) ); // or a menu command?
// Save the last command. Some commands are not saved in this manor, these
// commands are:
// Inv, Hyp, Deg, Rad, Grad, Stat, FE, MClear, Back, and Exp. The excluded
// commands are not
// really mathematical operations, rather they are GUI mode settings.
if ( !xwParam(IDC_INV, IDC_HYP) && !xwParam(IDM_HEX, IDM_BIN) && !xwParam(IDM_QWORD, IDM_BYTE) && !xwParam(IDM_DEG, IDM_GRAD) && wParam!=IDC_STAT && wParam!=IDC_FE && wParam!=IDC_MCLEAR && wParam!=IDC_BACK && wParam!=IDC_EXP) { nLastCom=nTempCom; nTempCom=(INT)wParam; }
// If error and not a clear key or help key, BEEP.
if (bError && (wParam !=IDC_CLEAR) && (wParam !=IDC_CENTR) && (wParam != IDM_HELPTOPICS)) { MessageBeep(0); return; }
// Toggle Record/Display mode if appropriate.
if (gbRecord) { if (xwParam(IDC_AND, IDC_MPLUS) || xwParam(IDC_AVE, IDC_CLOSEP) || xwParam(IDC_INV, IDC_HYP) || xwParam(IDM_HEX, IDM_BIN) || xwParam(IDM_QWORD, IDM_BYTE) || xwParam(IDM_DEG, IDM_GRAD) || wParam == IDM_PASTE) { gbRecord = FALSE; SetWaitCursor(TRUE); CIO_vConvertToNumObj(&ghnoNum, &gcio); DisplayNum(); // Causes 3.000 to shrink to 3. on first op.
SetWaitCursor(FALSE); } } else { if ( xwParam(IDC_0, IDC_F) || wParam == IDC_PNT) { gbRecord = TRUE; CIO_vClear(&gcio); } }
// Interpret digit keys.
if (xwParam(IDC_0, IDC_F)) { int iValue = (int)(wParam-IDC_0);
// this is redundant, illegal keys are disabled
if (iValue >= nRadix) { //ASSERT( 0 );
MessageBeep(0); return; }
if (!CIO_bAddDigit(&gcio, iValue)) { MessageBeep(0); return; }
DisplayNum(); return; }
// STATISTICAL FUNCTIONS:
if (xwParam(IDC_AVE,IDC_DATA)) { /* Do statistics functions on data in fpStatNum array. */ if (hStatBox) { DisplayNum(); // Make sure gpszNum has the correct string
try { StatFunctions (wParam); } catch ( ... ) { ASSERT( 0 ); // the only thing stat box should be able to throw is out of memory
// which in previous versions of calc caused a program crash
} if (!bError) DisplayNum (); } else /* Beep if the stat box is not active. */ MessageBeep(0);
/* Reset the inverse flag since some functions use it. */ SetBox (IDC_INV, bInv=FALSE); return; }
// BINARY OPERATORS:
if (xwParam(IDC_AND,IDC_PWR)) { if (bInv && wParam==IDC_LSHF) { SetBox (IDC_INV, bInv=FALSE); wParam=RSHF; }
/* Change the operation if last input was operation. */ if (nLastCom >=IDC_AND && nLastCom <=IDC_PWR) { nOpCode=(INT)wParam; return; }
/* bChangeOp is true if there was an operation done and the */ /* current ghnoNum 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) { NumObjAssign( &ghnoPrecNum[nPrecNum], ghnoLastNum ); 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 */
SetWaitCursor(TRUE);
DoOperation(nOpCode, &ghnoNum, ghnoLastNum);
SetWaitCursor(FALSE);
if ((nPrecNum !=0) && (nPrecOp[nPrecNum-1])) { nPrecNum--; nOpCode=nPrecOp[nPrecNum] ; if (NumObjOK( ghnoPrecNum[nPrecNum] )) NumObjAssign(&ghnoLastNum , ghnoPrecNum[nPrecNum]); else NumObjAssign(&ghnoLastNum, HNO_ZERO);
goto DoPrecedenceCheckAgain ; }
if (!bError) DisplayNum (); } }
NumObjAssign(&ghnoLastNum, ghnoNum); NumObjAssign(&ghnoNum, HNO_ZERO); nOpCode=(INT)wParam; bNoPrevEqu=bChangeOp=TRUE; return; }
// UNARY OPERATORS:
if (xwParam(IDC_CHOP,IDC_PERCENT)) { /* Functions are unary operations. */
/* If the last thing done was an operator, ghnoNum 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 >= IDC_AND && nLastCom <= IDC_PWR) NumObjAssign( &ghnoNum, ghnoLastNum );
SetWaitCursor(TRUE); SciCalcFunctions ( &ghnoNum, (DWORD)wParam); SetWaitCursor(FALSE);
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 == IDC_CHOP || wParam == IDC_SIN || wParam == IDC_COS || wParam == IDC_TAN || wParam == IDC_SQR || wParam == IDC_CUB || wParam == IDC_LOG || wParam == IDC_LN || wParam == IDC_DMS)) { bInv=FALSE; SetBox (IDC_INV, FALSE); }
if (bHyp && (wParam == IDC_SIN || wParam == IDC_COS || wParam == IDC_TAN)) { bHyp = FALSE; SetBox (IDC_HYP, FALSE); } bNoPrevEqu=TRUE; return; }
// BASE CHANGES:
if (xwParam(IDM_HEX, IDM_BIN)) { // Change radix and update display.
if (nCalc==1) { wParam=IDM_DEC; }
SetRadix((DWORD)wParam); return; }
SetWaitCursor(TRUE);
/* 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_USE_SEPARATOR: case IDM_HELPTOPICS: // Jump to menu command handler in scimenu.c.
MenuFunctions((DWORD)wParam); DisplayNum(); break;
case IDC_CLEAR: /* Total clear. */ NumObjAssign( &ghnoLastNum, HNO_ZERO ); nPrecNum=nTempCom=nLastCom=nOpCode=nParNum=bChangeOp=FALSE; nFE = FMT_FLOAT; // back to the default number format
bNoPrevEqu=TRUE;
/* clear the paranthesis status box indicator, this will not be
cleared for CENTR */
SetDlgItemText(g_hwndDlg, IDC_PARTEXT, szBlank);
/* fall through */
case IDC_CENTR: /* Clear only temporary values. */ NumObjAssign( &ghnoNum, HNO_ZERO );
if (!nCalc) { // Clear the INV, HYP indicators & leave (=xx indicator active
SetBox (IDC_INV, bInv=FALSE); SetBox (IDC_HYP, bHyp=FALSE); }
bError=FALSE; CIO_vClear(&gcio); gbRecord = TRUE; DisplayNum (); break;
case IDC_STAT: /* Shift focus to Statistix Box if it's active. */ if (hStatBox) SetFocus(hStatBox); else SetStat (TRUE); break;
case IDC_BACK: // Divide number by the current radix and truncate.
// Only allow backspace if we're recording.
if (gbRecord) { if (!CIO_bBackspace(&gcio)) MessageBeep(0);
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 [censored] I did here... */ case IDC_EQU: do { // NOTE: the number pointed to by hnoHold won't get freed until process termination.
static HNUMOBJ hnoHold;
/* Last thing keyed in was an operator. Lets do the op on*/ /* a duplicate of the last entry. */ if ((nLastCom >= IDC_AND) && (nLastCom <= IDC_PWR)) NumObjAssign( &ghnoNum, ghnoLastNum );
if (nOpCode) /* Is there a valid operation around? */ { /* If this is the first EQU in a string, set hnoHold=ghnoNum */ /* Otherwise let ghnoNum=hnoTemp. This keeps ghnoNum constant */ /* through all EQUs in a row. */ if (bNoPrevEqu) NumObjAssign(&hnoHold, ghnoNum); else NumObjAssign(&ghnoNum, hnoHold);
/* Do the current or last operation. */ DoOperation (nOpCode, &ghnoNum, ghnoLastNum); NumObjAssign(&ghnoLastNum, ghnoNum );
/* 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]; if (NumObjOK( ghnoPrecNum[nPrecNum] )) NumObjAssign(&ghnoLastNum , ghnoPrecNum[nPrecNum]); else NumObjAssign(&ghnoLastNum, HNO_ZERO); bNoPrevEqu=TRUE; } while (nPrecNum >= 0);
bChangeOp=FALSE; break;
case IDC_OPENP: case IDC_CLOSEP: nx=0; if (wParam==IDC_OPENP) nx=1;
// -IF- the Paren holding array is full and we try to add a paren
// -OR- the paren holding array is empty and we try to remove a
// paren
// -OR- the the precidence holding array is full
if ((nParNum >= 25 && nx) || (!nParNum && !nx) || ( (nPrecNum >= 25 && nPrecOp[nPrecNum-1]!=0) ) ) { MessageBeep(0); break; }
if (nx) { /* Open level of parentheses, save number and operation. */ NumObjAssign( &ghnoParNum[nParNum], ghnoLastNum); nOp[nParNum++]=nOpCode;
/* save a special marker on the precedence array */ nPrecOp[nPrecNum++]=0 ;
NumObjAssign( &ghnoLastNum, HNO_ZERO ); nTempCom=0; nOpCode=IDC_ADD; } else { /* Get the operation and number and return result. */ DoOperation (nOpCode, &ghnoNum, ghnoLastNum);
/* now process the precedence stack till we get to an
opcode which is zero. */
while (nOpCode = nPrecOp[--nPrecNum]) { if (NumObjOK( ghnoPrecNum[nPrecNum] )) NumObjAssign(&ghnoLastNum , ghnoPrecNum[nPrecNum]); else NumObjAssign(&ghnoLastNum, HNO_ZERO);
DoOperation (nOpCode, &ghnoNum, ghnoLastNum); }
/* now get back the operation and opcode at the begining
of this paranthesis pair */
nParNum -= 1; NumObjAssign( &ghnoLastNum, ghnoParNum[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. */ StringCchCopy(szJunk, ARRAYSIZE(szJunk), TEXT("(=")); StringCchCat(szJunk, ARRAYSIZE(szJunk), UToDecT(nParNum, szTemp)); SetDlgItemText(g_hwndDlg, IDC_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. */ SetDisplayText(g_hwndDlg, szJunk); bChangeOp=FALSE; } else DisplayNum (); break;
case IDM_QWORD: case IDM_DWORD: case IDM_WORD: case IDM_BYTE: case IDM_DEG: case IDM_RAD: case IDM_GRAD:
if (!F_INTMATH()) { // in decimal mode, these buttons simply set a flag which is
// passed to the ratpak to handle angle conversions
if (xwParam(IDM_DEG, IDM_GRAD)) { nDecMode = (ANGLE_TYPE)(wParam - IDM_DEG);
CheckMenuRadioItem(GetSubMenu(GetMenu(g_hwndDlg), 1), IDM_DEG, IDM_GRAD, IDM_DEG+nDecMode, MF_BYCOMMAND); CheckRadioButton(g_hwndDlg, IDC_DEG, IDC_GRAD, IDC_DEG+nDecMode); } } else { if (xwParam(IDM_DEG, IDM_GRAD)) { // if in hex mode, but we got a decimal key press this
// likely is the accelorator. map this to the correct key
wParam=IDM_DWORD+(wParam-IDM_DEG); } if ( gbRecord ) { CIO_vConvertToNumObj(&ghnoNum, &gcio); gbRecord = FALSE; }
// Compat. mode BaseX: Qword, Dword, Word, Byte
nHexMode = (int)(wParam - IDM_QWORD); switch (nHexMode) { case 0: dwWordBitWidth = 64; break; case 1: dwWordBitWidth = 32; break; case 2: dwWordBitWidth = 16; break; case 3: dwWordBitWidth = 8; break; default: ASSERT( 0 ); // Invalid Word Size
break; }
// different wordsize means the new wordsize determines
// the precision
BaseOrPrecisionChanged();
CheckMenuRadioItem(GetSubMenu(GetMenu(g_hwndDlg), 1), IDM_QWORD, IDM_BYTE, IDM_QWORD+nHexMode, MF_BYCOMMAND);
CheckRadioButton(g_hwndDlg, IDC_QWORD, IDC_BYTE, IDC_QWORD+nHexMode); }
// REARCHITECT: the call to display number is what actually does the
// chop. it would make more sense to do the chop here when the
// wordsize changes. the chop must be done when a different
// wordsize is selected AND when the base is changed to non-decimal
DisplayNum(); break;
case IDC_SIGN: // Change the sign.
if (gbRecord) CIO_vToggleSign(&gcio); else { NumObjNegate( &ghnoNum ); }
DisplayNum(); break;
case IDC_RECALL: /* Recall immediate memory value. */ NumObjAssign( &ghnoNum, ghnoMem );
DisplayNum (); break;
case IDC_MPLUS: /* MPLUS adds ghnoNum to immediate memory and kills the "mem" */ /* indicator if the result is zero. */ addrat( &ghnoMem, ghnoNum); SetDlgItemText(g_hwndDlg,IDC_MEMTEXT, !NumObjIsZero(ghnoMem) ? (TEXT(" M")):(szBlank)); break;
case IDC_STORE: case IDC_MCLEAR: if (wParam==IDC_STORE) { NumObjAssign( &ghnoMem, ghnoNum ); } else { NumObjAssign( &ghnoMem, HNO_ZERO ); } SetDlgItemText(g_hwndDlg,IDC_MEMTEXT, !NumObjIsZero(ghnoMem) ? (TEXT(" M")):(szBlank)); break;
case IDC_PI: if (!F_INTMATH()) { /* Return PI if bInv==FALSE, or 2PI if bInv==TRUE. */ if (bInv) NumObjAssign( &ghnoNum, HNO_2PI ); else NumObjAssign( &ghnoNum, HNO_PI );
DisplayNum(); SetBox(IDC_INV, bInv=FALSE); } else MessageBeep(0); break;
case IDC_FE: // Toggle exponential notation display.
nFE = NUMOBJ_FMT(!(int)nFE); DisplayNum(); break;
case IDC_EXP: if (gbRecord && !F_INTMATH()) if (CIO_bExponent(&gcio)) { DisplayNum(); break; } MessageBeep(0); break;
case IDC_PNT: if (gbRecord && !F_INTMATH()) { if (CIO_bAddDecimalPt(&gcio)) {
DisplayNum(); break; } } MessageBeep(0); break;
case IDC_INV: SetBox((int)wParam, bInv=!bInv); break;
case IDC_HYP: SetBox((int)wParam, bHyp=!bHyp); break; }
SetWaitCursor(FALSE); }
// change the display area from a static text to an editbox, which has the focus can make
// Magnifer (Accessibility tool) work
BOOL SetDisplayText(HWND hDlg, LPCTSTR szText) { HWND hDispEdit = GetDlgItem(hDlg, IDC_DISPLAY); int nLen = lstrlen(szText);
SetWindowText(hDispEdit, szText); SetFocus(hDispEdit); // make sure the number just typed is shown at the center of Magnifier
SendMessage(hDispEdit, EM_SETSEL, nLen, nLen); return TRUE; }
|