This is Calculator.m in view mode; [Download] [Up]
/***(Calculator.m)*************************************************************
*H* Primary input processing logic for SciCalc V1.1, 20-JUN-93 *
*C* V1.1 20-JUN-93 Version 1.1 update. --MDM *
*C* V0.0 09-FEB-91 Initial version --MDM *
******************************************************************************/
#import <strings.h> /* Standard C string operator function prototypes */
#import <ctype.h> /* Standard C ASCII character code macros */
#import <math.h> /* Standard C math library function prototypes */
#import <appkit/Application.h> /* Appkit Application Class Definition */
#import <appkit/Button.h> /* Appkit Button Class Definition */
#import <appkit/Matrix.h> /* Appkit Matrix Class Definition */
#import <appkit/MenuCell.h> /* Appkit MenuCell Class Definition */
#import <appkit/Slider.h> /* Appkit Slider Class Definition */
#import "Calculator.h" /* Interface for this object */
#import "LcdText.h" /* LcdText Class Definition */
#import "nxkeycodes.h" /* NeXT keyboard keycode values */
void STR_BinaryToHex (char*, char*);
void STR_HexToBinary (char*, char*);
#ifdef DEBUG
void UTL_NxDumpEvent (NXEvent*, int, int, int);
#endif
#define ACCEPT_Digit 0x00000001
#define ACCEPT_Operator 0x00000002
#define ACCEPT_OpenParen 0x00000004
#define ACCEPT_CloseParen 0x00000008
#define ACCEPT_Function 0x00000010
#define BASE_Decimal 1
#define BASE_Hex 2
#define BASE_Octal 3
#define BASE_Binary 4
#define FMODE_Evaluate 1
#define FMODE_Operate 2
#define MAXDGT_Decimal 22
#define MAXDGT_HexLong 8
#define MAXDGT_HexWord 4
#define MAXDGT_HexByte 2
#define MAXDGT_OctalLong 11
#define MAXDGT_OctalWord 6
#define MAXDGT_OctalByte 3
#define MAXDGT_BinaryLong 32
#define MAXDGT_BinaryWord 16
#define MAXDGT_BinaryByte 8
#define MCELL_minimum 0
#define MCELL_maximum 25
#define METRIC_Degrees 1
#define METRIC_Radians 2
#define METRIC_Grads 3
#define UNIT_Byte 1
#define UNIT_Word 2
#define UNIT_Longword 3
#define UNIT_Quadword 4
static Calculator *CVmyOnlyChild; /* ID of Classes one object instance */
@implementation Calculator:Object
/******************************************************************************
*** F A C T O R Y M E T H O D S ***
******************************************************************************/
/******************************************************************************
* METHOD:- init *
* Create and initialize a new instance of the Calculator class. *
******************************************************************************/
- init
{ /* Local Variables */
int ndex;
/* BEGIN:-init */
self = [super init]; /* create new instance of calculator */
/* Set the Class Variable to the ID of its only object */
CVmyOnlyChild = self;
/* Create Value & Operator stacks */
OVvalStack = [[ValueStack alloc] init];
OVopStack = [[OperatorStack alloc] init];
/* Initialize Display & Value strings to be null terminated */
OVcalcDspStr[0] = OVcalcDspStr[0] = 0;
OVcalcMemDspStr[0] = 0;
for (ndex = MCELL_minimum; ndex <= MCELL_maximum; ndex++)
OVcalcMemoryVal[ndex] = 0.0;
/* Initialize state variables */
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
OVcalcBase = BASE_Decimal;
OVcalcMaxDigits = MAXDGT_Decimal;
OVcalcCEIndex = 0;
OVcalcEqualized = NO;
OVcalcFrozen = NO;
OVcalcFunctionMode = FMODE_Evaluate;
OVcalcHyperbolic = NO;
OVcalcInverse = NO;
OVcalcMetric = METRIC_Degrees;
OVcalcMemoryCell = MCELL_minimum;
OVcalcParenBalance = 0;
OVcalcPrecsnVal = 6;
return(self);
}/* END:-init */
/******************************************************************************
* METHOD:+ KeyBoardEquivalent *
* Receives messages from the Main Window object for each keyDown event it *
* receives. This routine determines which key was pressed and decides if a *
* message should be sent to the Calculator object itself. *
******************************************************************************/
+ KeyBoardEquivalent:(NXEvent *)event;
{ /* Local Variables */
char chrval;
/* BEGIN KeyBoardEquivalent */
#if 0
UTL_NxDumpEvent (event, 1, 1, 1);
#endif
if ( index("01234567890ABCDEFabcdef.",event->data.key.charCode) )
{/* This keydown event corresponds to a digit button */
chrval = event->data.key.charCode;
if (islower(chrval))
chrval = toupper(chrval);
[CVmyOnlyChild ProcessDigit:chrval];
}
else if ( event->data.key.charCode == '+' )
[CVmyOnlyChild ProcessOperator: OPCODE_Plus: "+"];
else if ( event->data.key.charCode == '-' )
[CVmyOnlyChild ProcessOperator: OPCODE_Minus: "-"];
else if ( event->data.key.charCode == '/' )
[CVmyOnlyChild ProcessOperator: OPCODE_Divide: "/"];
else if ( event->data.key.charCode == '*' )
[CVmyOnlyChild ProcessOperator: OPCODE_Multiply: "*"];
else if ( event->data.key.charCode == '=' ||
event->data.key.charCode == 13 ||
((event->flags & NX_NUMERICPADMASK) && event->data.key.charCode == 3) )
[CVmyOnlyChild Equals:CVmyOnlyChild];
else if ( event->data.key.charCode == '~' ||
event->data.key.charCode == '`' )
[CVmyOnlyChild ChangeSign:CVmyOnlyChild];
else if ( event->data.key.charCode == '(' )
[CVmyOnlyChild OpenParen:CVmyOnlyChild];
else if ( event->data.key.charCode == ')' )
[CVmyOnlyChild CloseParen:CVmyOnlyChild];
else if ( event->data.key.charCode == 'P' || event->data.key.charCode == 'p' )
[CVmyOnlyChild EnterPi:CVmyOnlyChild];
else if ( event->data.key.charCode == '[' )
[CVmyOnlyChild MemoryStore:CVmyOnlyChild];
else if ( event->data.key.charCode == ']' )
[CVmyOnlyChild MemoryRecall:CVmyOnlyChild];
else if ( event->data.key.charCode == '{' )
[[CVmyOnlyChild setTag:-1] MemorySelect:CVmyOnlyChild];
else if ( event->data.key.charCode == '}' )
[[CVmyOnlyChild setTag:1] MemorySelect:CVmyOnlyChild];
else if ( event->data.key.charCode == '\\' )
[CVmyOnlyChild MemoryClear:CVmyOnlyChild];
else if ( event->data.key.charCode == '\033' ) /* ESC */
[CVmyOnlyChild AllClear:CVmyOnlyChild];
else if ( event->data.key.charCode == '\177' ) /* Delete */
[CVmyOnlyChild ClearEntry:CVmyOnlyChild];
else if ( event->data.key.charCode == '\005' ) /* Control-E */
[CVmyOnlyChild ProcessFunction:OPCODE_Exp :"Exp:"];
else if ( (event->flags & NX_ALTERNATEMASK) && /* Alternate-E */
(event->data.key.charCode == NX_CHRCODE_ALT_e ||
event->data.key.charCode == NX_CHRCODE_ALT_E) )
[CVmyOnlyChild ProcessFunction:OPCODE_Ln :"Ln:"];
else if ( (event->flags & NX_ALTERNATEMASK) && /* Alternate-F */
(event->data.key.charCode == NX_CHRCODE_ALT_f ||
event->data.key.charCode == NX_CHRCODE_ALT_F) )
[CVmyOnlyChild ProcessFunction:OPCODE_Factorial :"X!:"];
else if ( event->data.key.charCode == '\014' ) /* Control-L */
[CVmyOnlyChild ProcessFunction:OPCODE_TenToX :"10^X:"];
else if ( (event->flags & NX_ALTERNATEMASK) && /* Alternate-L */
(event->data.key.charCode == NX_CHRCODE_ALT_l ||
event->data.key.charCode == NX_CHRCODE_ALT_L) )
[CVmyOnlyChild ProcessFunction:OPCODE_Log :"LOG:"];
else if ( event->data.key.charCode == '\022' ) /* Control-R */
[CVmyOnlyChild ProcessFunction:OPCODE_Recip :"1/X:"];
else if ( event->data.key.charCode == '\030' ) /* Control-X */
[CVmyOnlyChild ProcessFunction:OPCODE_XSquared :"X^2:"];
else if ( (event->flags & NX_ALTERNATEMASK) && /* Alternate-X */
(event->data.key.charCode == NX_CHRCODE_ALT_x ||
event->data.key.charCode == NX_CHRCODE_ALT_X) )
[CVmyOnlyChild ProcessFunction:OPCODE_SqrRoot :"SQRT:"];
else if ( event->data.key.charCode == '\031' ) /* Control-Y */
[CVmyOnlyChild ProcessOperator:OPCODE_XToY :"X^Y"];
else if ( (event->flags & NX_ALTERNATEMASK) && /* Alternate-Y */
(event->data.key.charCode == NX_CHRCODE_ALT_y ||
event->data.key.charCode == NX_CHRCODE_ALT_Y) )
[CVmyOnlyChild ProcessOperator:OPCODE_XrootY :"XrootY"];
else if ( (event->flags & NX_CONTROLMASK) && !(event->flags & NX_SHIFTMASK) )
{/* Control down without shift key ::= Inverse:Off, Hyperbolic:Off */
if ( event->data.key.charCode == '\003' ) /* Ctl-c */
[CVmyOnlyChild ProcessFunction:OPCODE_Cos :"COS:"];
else if ( event->data.key.charCode == '\023' ) /* Ctl-s */
[CVmyOnlyChild ProcessFunction:OPCODE_Sin :"SIN:"];
else if ( event->data.key.charCode == '\024' ) /* Ctl-t */
[CVmyOnlyChild ProcessFunction:OPCODE_Tan :"TAN:"];
}
else if ( (event->flags & NX_CONTROLMASK) && (event->flags & NX_SHIFTMASK) )
{/* Control down with shift key ::= Inverse:Off, Hyperbolic:On */
if ( event->data.key.charCode == '\003' ) /* Ctl-C */
[CVmyOnlyChild ProcessFunction:OPCODE_CosH :"COSH:"];
else if ( event->data.key.charCode == '\023' ) /* Ctl-S */
[CVmyOnlyChild ProcessFunction:OPCODE_SinH :"SINH:"];
else if ( event->data.key.charCode == '\024' ) /* Ctl-T */
[CVmyOnlyChild ProcessFunction:OPCODE_TanH :"TANH:"];
}
else if ( (event->flags & NX_ALTERNATEMASK) && !(event->flags & NX_SHIFTMASK) )
{/* Alternate down without shift key ::= Inverse:Off, Hyperbolic:On */
if ( event->data.key.charCode == NX_CHRCODE_ALT_c ) /* Alt-c */
[CVmyOnlyChild ProcessFunction:OPCODE_ACos :"ACOS:"];
else if ( event->data.key.charCode == NX_CHRCODE_ALT_s ) /* Alt-s */
[CVmyOnlyChild ProcessFunction:OPCODE_ASin :"ASIN:"];
else if ( event->data.key.charCode == NX_CHRCODE_ALT_t ) /* Alt-t */
[CVmyOnlyChild ProcessFunction:OPCODE_ATan :"ATAN:"];
}
else if ( (event->flags & NX_ALTERNATEMASK) && (event->flags & NX_SHIFTMASK) )
{/* Alternate down with shift key ::= Inverse:On, Hyperbolic:On */
if ( event->data.key.charCode == NX_CHRCODE_ALT_C ) /* Alt-C */
[CVmyOnlyChild ProcessFunction:OPCODE_ACosH :"ACOSH:"];
else if ( event->data.key.charCode == NX_CHRCODE_ALT_S ) /* Alt-S */
[CVmyOnlyChild ProcessFunction:OPCODE_ASinH :"ASINH:"];
else if ( event->data.key.charCode == NX_CHRCODE_ALT_T ) /* Alt-T */
[CVmyOnlyChild ProcessFunction:OPCODE_ATanH :"ATANH:"];
}
else
return(NULL);
return(CVmyOnlyChild);
}/* END KeyBoardEquivalent */
/******************************************************************************
* METHOD:+ PasteExpression:Length: *
* This method accepts a string and attempts to process each character as if *
* were entered from the keyboard. *
******************************************************************************/
+ PasteExpression
/* Arguments */
:(char*) expString
Length:(int) expLength
{ /* Local Variables */
char chrval;
int indx;
/* BEGIN+PasteExpression:Length: */
for ( indx = 0; indx < expLength; indx++ )
{
chrval = toupper(expString[indx]);
if ( strchr("01234567890ABCDEF.", chrval) )
[CVmyOnlyChild ProcessDigit:chrval];
else if ( chrval == '+' )
[CVmyOnlyChild ProcessOperator: OPCODE_Plus: "+"];
else if ( chrval == '-' )
[CVmyOnlyChild ProcessOperator: OPCODE_Minus: "-"];
else if ( chrval == '/' )
[CVmyOnlyChild ProcessOperator: OPCODE_Divide: "/"];
else if ( chrval == '*' )
[CVmyOnlyChild ProcessOperator: OPCODE_Multiply: "*"];
else if ( chrval == '=' )
[CVmyOnlyChild Equals:CVmyOnlyChild];
else if ( chrval == '~' )
[CVmyOnlyChild ChangeSign:CVmyOnlyChild];
else if ( chrval == '(' )
[CVmyOnlyChild OpenParen:CVmyOnlyChild];
else if ( chrval == ')' )
[CVmyOnlyChild CloseParen:CVmyOnlyChild];
else if ( chrval == 'P' )
[CVmyOnlyChild EnterPi:CVmyOnlyChild];
else if ( chrval == '[' )
[CVmyOnlyChild MemoryStore:CVmyOnlyChild];
else if ( chrval == ']' )
[CVmyOnlyChild MemoryRecall:CVmyOnlyChild];
else if ( chrval == '{' )
[[CVmyOnlyChild setTag:-1] MemorySelect:CVmyOnlyChild];
else if ( chrval == '}' )
[[CVmyOnlyChild setTag:1] MemorySelect:CVmyOnlyChild];
else if ( chrval == '\\' )
[CVmyOnlyChild MemoryClear:CVmyOnlyChild];
else if ( chrval == '\033' )
[CVmyOnlyChild AllClear:CVmyOnlyChild];
else if ( chrval == '\177' )
[CVmyOnlyChild ClearEntry:CVmyOnlyChild];
}/*for*/
return(CVmyOnlyChild);
}/* END+PasteExpression:Length: */
/******************************************************************************
*** I N S T A N C E M E T H O D S ***
******************************************************************************/
/******************************************************************************
* INSTANCE METHOD:- AdjustPrecision *
* This method adjusts the precision display text and the slider limits *
* according to the current Base and Unit settings. *
******************************************************************************/
- AdjustPrecision
{ /* Local Variables */
char fmtstr[16]; /* Space to format precision label text */
int maxprc=0; /* maximum precision for current Base/Unit choice */
/* BEGIN AdjustPrecision */
switch (OVcalcBase)
{
case BASE_Decimal:
maxprc = MAXDGT_Decimal - 2;
break;
case BASE_Hex:
if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_HexLong;
if (OVcalcUnit == UNIT_Word) maxprc = MAXDGT_HexWord;
if (OVcalcUnit == UNIT_Byte) maxprc = MAXDGT_HexByte;
break;
case BASE_Octal:
if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_OctalLong;
if (OVcalcUnit == UNIT_Word) maxprc = MAXDGT_OctalWord;
if (OVcalcUnit == UNIT_Byte) maxprc = MAXDGT_OctalByte;
break;
case BASE_Binary:
if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_BinaryLong;
if (OVcalcUnit == UNIT_Word) maxprc = MAXDGT_BinaryWord;
if (OVcalcUnit == UNIT_Byte) maxprc = MAXDGT_BinaryByte;
break;
}/*switch*/
if ( maxprc < OVcalcPrecsnVal )
{/* Reset precision to fit within limits, modify label and slider */
OVcalcPrecsnVal = maxprc;
sprintf(fmtstr, "Precision: %2d", OVcalcPrecsnVal);
[OVcalcPrecsnDsp setStringValue:fmtstr];
[OVcalcPrecsnSldr setIntValue:OVcalcPrecsnVal];
}
[OVcalcPrecsnSldr setMaxValue:(double)maxprc];
return self;
}/* END AdjustPrecision */
/******************************************************************************
* INSTANCE METHOD:- AllClear *
* This method handles the actions required when the user presses the "AC" *
* button. *
******************************************************************************/
- AllClear:sender
{/* BEGIN AllClear */
/* Empty Operator and Value Stacks */
[OVopStack ClearStack];
[OVvalStack ClearStack];
/* Clear Display and Value Strings */
OVcalcDspStr[0] = OVcalcValStr[0] = 0;
OVcalcCEIndex = 0;
/* Reset state variables to normalized values */
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
OVcalcFrozen = NO;
OVcalcEqualized = NO;
OVcalcParenBalance = 0;
/* Set current display value to "0.0" */
[OVcalcDisplay setCurrentLine:"0.0"];
return self;
}/* END AllClear */
/******************************************************************************
* INSTANCE METHOD:- BitwiseAnd *
* Process the bitwise And operator button event. *
******************************************************************************/
- BitwiseAnd:sender
{/* BEGIN BitwiseAnd */
[self ProcessOperator: OPCODE_And: "&"];
return self;
}/* END BitwiseAnd */
/******************************************************************************
* INSTANCE METHOD:- BitwiseLsh *
* Process the bitwise arithmetic Left-Shift function button event *
******************************************************************************/
- BitwiseLsh:sender
{/* BEGIN BitwiseLsh */
[self ProcessFunction: OPCODE_LShift: "<<:"];
return self;
}/* END BitwiseLsh */
/******************************************************************************
* INSTANCE METHOD:- BitwiseNot *
* Process the bitwise Not function button event. *
******************************************************************************/
- BitwiseNot:sender
{/* BEGIN BitwiseNot */
[self ProcessFunction: OPCODE_Not: "Not:"];
return self;
}/* END BitwiseNot */
/******************************************************************************
* INSTANCE METHOD:- BitwiseOr *
* Process the bitwise Or operator button event. *
******************************************************************************/
- BitwiseOr:sender
{/* BEGIN BitwiseOr */
[self ProcessOperator: OPCODE_Or: "|"];
return self;
}/* END BitwiseOr */
/******************************************************************************
* INSTANCE METHOD:- BitwiseRsh *
* Process the bitwise arithmetic Right-Shift function button event *
******************************************************************************/
- BitwiseRsh:sender
{/* BEGIN BitwiseRsh */
[self ProcessFunction: OPCODE_RShift: ">>:"];
return self;
}/* END BitwiseRsh */
/******************************************************************************
* INSTANCE METHOD:- BitwiseXor *
* Process the bitwise exclusive or operator button event. *
******************************************************************************/
- BitwiseXor:sender
{/* BEGIN BitwiseXor */
[self ProcessOperator: OPCODE_Xor: "^"];
return self;
}/* END BitwiseXor */
/******************************************************************************
* INSTANCE METHOD:- ChangeBase *
* Handle the actions needed when one of numeric base radio buttons are *
* selected. This function is responsible enabling and disabling the digit *
* buttons appropriate for the current numeric base. It is also responsible *
* for managing the UNIT radio box according to base. In Decimal mode the *
* only Unit allowed is Quadword. If the numeric base is Hex/Octal/Binary the *
* Unit may be Longword/Word/Byte. When transitioning from Decimal to Hex/ *
* Octal/Binary the Longword/Word/Byte buttons are enabled and longword is set *
* as the default Unit size. This routine also performs the task of changing *
* the currently entered value from the current Base/Unit to the new Base/Unit *
* selection. *
******************************************************************************/
- ChangeBase:sender
{ /* Local Variables */
int col;
double curval;
int oldbase;
int row;
int translate=0; /* Translate current value to new base? */
/* BEGIN ChangeBase */
if ( [self ValueEntered] )
{/* A value has been entered, scan it under the current Base setting */
translate = 1;
[self ScanValue:OVcalcValStr :&curval];
}
/* Save the current Base and extract the new base from the button tag */
oldbase = OVcalcBase;
OVcalcBase = [sender selectedTag]; /* Get tag value from sending button */
/* Convert Unit radio box according to Base transition */
if ( oldbase != BASE_Decimal && OVcalcBase == BASE_Decimal )
{/* Modify the unit radio box, allow only Quadword unit selection */
[[OVcalcUnitMatrix cellAt:0 :0] setEnabled:NO]; /* Disable byte */
[[OVcalcUnitMatrix cellAt:1 :0] setEnabled:NO]; /* Disable word */
[[OVcalcUnitMatrix cellAt:0 :1] setEnabled:NO]; /* Disable longword */
[[OVcalcUnitMatrix cellAt:1 :1] setEnabled:YES]; /* Enable quadword */
[OVcalcUnitMatrix selectCellAt:1 :1]; /* Select quadword */
/* Modify bitwise function button matrix: Disable [And], [Or], [Not] */
/* [Xor], [Lsh], [Rsh] */
for (row = 0; row < 3; row++)
for (col = 0; col < 2; col++)
[[OVcalcBitwiseMtx cellAt:row :col] setEnabled:NO];
OVcalcUnit = UNIT_Quadword;
}
else if ( oldbase == BASE_Decimal && OVcalcBase != BASE_Decimal )
{/* Modify unit radio box, allow byte/word/longword select longword */
[[OVcalcUnitMatrix cellAt:0 :0] setEnabled:YES]; /* Enable byte */
[[OVcalcUnitMatrix cellAt:1 :0] setEnabled:YES]; /* Enable word */
[[OVcalcUnitMatrix cellAt:0 :1] setEnabled:YES]; /* Enable longword */
[[OVcalcUnitMatrix cellAt:1 :1] setEnabled:NO]; /* Disable quadword */
[OVcalcUnitMatrix selectCellAt:0 :1]; /* Select Longword */
/* Modify bitwise function button matrix: Enable [And], [Or], [Not] */
/* [Xor], [Lsh], [Rsh] */
for (row = 0; row < 3; row++)
for (col = 0; col < 2; col++)
[[OVcalcBitwiseMtx cellAt:row :col] setEnabled:YES];
OVcalcUnit = UNIT_Longword;
}
switch ( OVcalcBase )
{
case BASE_Decimal:
/* Disable buttons [A]-[F] */
for (row = 0; row < 2; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
/* Enable buttons [0]-[9] & [.] */
for (row = 2; row < 5; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];
[OVcalcDigitPBtn setEnabled:YES];
OVcalcMaxDigits = MAXDGT_Decimal;
break;
case BASE_Hex:
/* Enable buttons [0]-[F] and disable [.] button */
for (row = 0; row < 5; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];
[OVcalcDigitPBtn setEnabled:NO];
if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_HexLong;
if (OVcalcUnit == UNIT_Word) OVcalcMaxDigits = MAXDGT_HexWord;
if (OVcalcUnit == UNIT_Byte) OVcalcMaxDigits = MAXDGT_HexByte;
break;
case BASE_Octal:
/* Disable buttons [8]-[F] & [.] */
for (row = 0; row < 2; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
[[OVcalcDigitMtx cellAt:2 :1] setEnabled:NO];
[[OVcalcDigitMtx cellAt:2 :2] setEnabled:NO];
[OVcalcDigitPBtn setEnabled:NO];
/* Enable buttons [0]-[7] ] */
[[OVcalcDigitMtx cellAt:2 :0] setEnabled:YES];
for (row = 3; row < 5; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];
if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_OctalLong;
if (OVcalcUnit == UNIT_Word) OVcalcMaxDigits = MAXDGT_OctalWord;
if (OVcalcUnit == UNIT_Byte) OVcalcMaxDigits = MAXDGT_OctalByte;
break;
case BASE_Binary:
/* Disable buttons [2]-[F] & [.] */
for (row = 0; row < 4; row++)
for (col = 0; col < 3; col++)
[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
[[OVcalcDigitMtx cellAt:4 :1] setEnabled:NO];
[[OVcalcDigitMtx cellAt:4 :2] setEnabled:NO];
[OVcalcDigitPBtn setEnabled:NO];
/* Enable buttons [0]-[1] */
[[OVcalcDigitMtx cellAt:4 :0] setEnabled:YES];
if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_BinaryLong;
if (OVcalcUnit == UNIT_Word) OVcalcMaxDigits = MAXDGT_BinaryWord;
if (OVcalcUnit == UNIT_Byte) OVcalcMaxDigits = MAXDGT_BinaryByte;
break;
}/*switch*/
/* Adjust precision slider for selected base */
[self AdjustPrecision];
if ( translate )
{/* Clear current entry strings, reformat and redisplay */
OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
[self FormatValue:curval :OVcalcValStr];
strcat (OVcalcDspStr, OVcalcValStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
}
/* Adjust memory display if need be */
if ( OVcalcMemDspStr[0] != 0 )
{/* Memory is not empty, so reformat cell to new numeric base */
[self FormatValue:OVcalcMemoryVal[OVcalcMemoryCell] :OVcalcMemDspStr];
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
}
return self;
}/* END ChangeBase */
/******************************************************************************
* INSTANCE METHOD:- ChangeFunctionMode *
* Handle the actions needed when Function Mode modifier is toggled. *
******************************************************************************/
- ChangeFunctionMode:sender
{/* BEGIN ChangeFunctionMode */
if ( OVcalcFunctionMode == FMODE_Evaluate )
{/* Change current function mode to Operate, replace menu button text */
OVcalcFunctionMode = FMODE_Operate;
[[sender selectedCell] setTitle:"Operate"];
}
else if ( OVcalcFunctionMode == FMODE_Operate )
{/* Change current function mode to Evaluate, replace menu button text */
OVcalcFunctionMode = FMODE_Evaluate;
[[sender selectedCell] setTitle:"Evaluate"];
}
return(self);
}/* END ChangeFunctionMode */
/******************************************************************************
* METHOD:- ChangeHyperbolic *
* Handle the actions needed when Hyperbolic modifier is toggled. *
******************************************************************************/
- ChangeHyperbolic:sender
{/* BEGIN ChangeHyperbolic */
if ( [sender intValue] == 0 )
/* Toggle button is in disabled state, turn off Hyperbolic modifier */
OVcalcHyperbolic = NO;
else
/* Toggle button is in enabled state, turn on Hyperbolic modifier */
OVcalcHyperbolic = YES;
return(self);
}/* END ChangeHyperbolic */
/******************************************************************************
* METHOD:- ChangeInverse *
* Handle the actions needed when Inverse modifier is toggled. *
******************************************************************************/
- ChangeInverse:sender
{/* BEGIN ChangeInverse */
if ( [sender intValue] == 0 )
/* Toggle button is in disabled state, turn off Inverse modifier */
OVcalcInverse = NO;
else
/* Toggle button is in enabled state, turn on Inverse modifier */
OVcalcInverse = YES;
return(self);
}/* END ChangeInverse */
/******************************************************************************
* METHOD:- ChangeMetric *
* Handle the actions needed when one of Metric radio buttons are selected. *
******************************************************************************/
- ChangeMetric:sender
{/* BEGIN ChangeMetric */
OVcalcMetric = [sender selectedTag]; /* Get tag value from sending button */
return(self);
}/* END ChangeMetric */
/******************************************************************************
* METHOD:- ChangePrecison *
* This method handles messages from the output precision slider. It is *
* responsible for updating precision display and current precision instance *
* variable. *
******************************************************************************/
- ChangePrecision:sender;
{ /* Local Variables */
char fmtstr[16];
/* BEGIN ChangePrecision */
OVcalcPrecsnVal = [sender intValue];
sprintf(fmtstr, "Precision: %2d", OVcalcPrecsnVal);
[OVcalcPrecsnDsp setStringValue:fmtstr];
return(self);
}/* END ChangePrecision */
/******************************************************************************
* METHOD:- ChangeSign *
* Handle the actions needed when the [+-] button is pressed. If the first *
* character in the current value string is '-' the minus sign is stripped off *
* the value and entry portion of the display string. Otherwise a minus sign *
* is added to the value and entry portion of the display string. The display *
* string is then redrawn. *
******************************************************************************/
- ChangeSign:sender
{/* BEGIN ChangeSign */
if ( OVcalcValStr[0] == '-' )
{/* Change negative value to positive value, copy over - sign */
strcpy(OVcalcValStr,&OVcalcValStr[1]);
strcpy(&OVcalcDspStr[OVcalcCEIndex], &OVcalcDspStr[OVcalcCEIndex+1]);
}
else
{/* Change positive value to negative, add - then append current value */
strcpy(&OVcalcDspStr[OVcalcCEIndex],"-");
strcpy(&OVcalcDspStr[OVcalcCEIndex+1],OVcalcValStr);
strcpy(OVcalcValStr,&OVcalcDspStr[OVcalcCEIndex]);
}
/* Redraw the current display string */
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
return(self);
}/* END ChangeSign */
/******************************************************************************
* METHOD:- ChangeUnit *
* Handle the actions needed when one of Unit radio buttons are selected. *
******************************************************************************/
- ChangeUnit:sender
{/* BEGIN ChangeUnit */
OVcalcUnit = [sender selectedTag]; /* Get tag value from sending button */
if (OVcalcUnit == UNIT_Quadword)
OVcalcMaxDigits = MAXDGT_Decimal;
else if (OVcalcUnit == UNIT_Longword)
{
if (OVcalcBase == BASE_Hex) OVcalcMaxDigits = MAXDGT_HexLong;
if (OVcalcBase == BASE_Octal) OVcalcMaxDigits = MAXDGT_OctalLong;
if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryLong;
}
else if (OVcalcUnit == UNIT_Word)
{
if (OVcalcBase == BASE_Hex) OVcalcMaxDigits = MAXDGT_HexWord;
if (OVcalcBase == BASE_Octal) OVcalcMaxDigits = MAXDGT_OctalWord;
if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryWord;
}
else if (OVcalcUnit == UNIT_Byte)
{
if (OVcalcBase == BASE_Hex) OVcalcMaxDigits = MAXDGT_HexByte;
if (OVcalcBase == BASE_Octal) OVcalcMaxDigits = MAXDGT_OctalByte;
if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryByte;
}
/* Adjust precision slider for selected base */
[self AdjustPrecision];
return(self);
}/* END ChangeUnit */
/******************************************************************************
* METHOD:- ClearEntry *
* Handle the actions needed when the "CE" button is pressed. If a partial *
* value has been entered then clear the Value string and delete the value off *
* the end of the display string and redisplay. *
******************************************************************************/
- ClearEntry:sender;
{/* BEGIN ClearEntry */
if ( [self ValueEntered] )
{/* A partial value has been entered, erase it and redisplay */
OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
}
return(self);
}/* END ClearEntry */
/******************************************************************************
* METHOD:- CloseParen *
* Handle the actions needed when the ")" button is pressed. *
******************************************************************************/
- CloseParen:sender
{ /* Local Variables */
double curval;
int opcode;
/* BEGIN CloseParen */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_CloseParen) != 0 )
{/* We are expecting a close paren, so attempt to reduce to open paren */
/* With a valid Close Paren set new acceptance state */
if (--OVcalcParenBalance > 0)
/* Open parens not balanced, accept Operator or CloseParen state */
OVcalcAcceptMode = ACCEPT_Operator | ACCEPT_CloseParen;
else
/* Open Parens are balanced, accept Operator only */
OVcalcAcceptMode = ACCEPT_Operator;
/* Add operator symbol to display string and redisplay */
strcat (OVcalcDspStr, ")");
OVcalcCEIndex = strlen(OVcalcDspStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
if ( [self ValueEntered] )
{/* Close paren just after an operand, push onto value stack */
[self ScanValue:OVcalcValStr :&curval];
[OVvalStack Push:curval];
OVcalcValStr[0] = 0;
}
/* Reduce expression between parens and remove open paren operator */
[self ReduceExpression:OPCODE_CloseParen];
[OVopStack Pop:&opcode];
#ifdef DEBUG
if ( opcode != OPCODE_OpenParen )
{
printf("Paren Match Failure");
[OVopStack PrintStack];
[OVvalStack PrintStack];
}
#endif
}
else
/* Enter error state and display syntax error */
[self SyntaxError:OPCODE_CloseParen :")"];
return self;
}/* END CloseParen */
/******************************************************************************
* METHOD:- Cosine *
* Perform actions in response to the COS button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- Cosine:sender;
{/* BEGIN Cosine */
if ( OVcalcInverse )
{/* The Inverse modifier is on, Opcode will be an ACOSx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be ACOSH */
[self ProcessFunction:OPCODE_ACosH :"ACOSH:"];
else
/* Hyperbolic modifier off, Opcode will be ACOS */
[self ProcessFunction:OPCODE_ACos :"ACOS:"];
}
else
{/* The Inverse modifier is off, Opcode will be an COSx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be COSH */
[self ProcessFunction:OPCODE_CosH :"COSH:"];
else
/* Hyperbolic modifier off, Opcode will be COS */
[self ProcessFunction:OPCODE_Cos :"COS:"];
}
return(self);
}/* END Cosine */
/******************************************************************************
* METHOD:- Digit *
* This method handles the button press events for all the numeric buttons *
* plus the period button. The button tag value is used to identify the *
* corresponding character for the button. The ProcessDigit method is then *
* called with the corresponding character to parse the input. *
******************************************************************************/
- Digit:sender
{ /* Local Variables */
static char tag2chr[] = "0123456789ABCDEF.";
int tagval;
/* BEGIN Digit */
/* Get the tag value from the sending button object */
tagval = [sender selectedTag];
/* Process the corresponding character value for the button pressed */
[self ProcessDigit:tag2chr[tagval]];
return self;
}/* END Digit */
/******************************************************************************
* METHOD:- Divide *
* Process the Divide operator button event. *
******************************************************************************/
- Divide:sender
{/* BEGIN Divide */
[self ProcessOperator: OPCODE_Divide: "/"];
return self;
}/* END Divide */
/******************************************************************************
* METHOD:- EnterPi *
* This method handles the actions required when the user presses the [PI] *
* button. The Pi opcode is sent to the Function Processor routine. *
******************************************************************************/
- EnterPi:sender
{/* BEGIN EnterPi */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 && OVcalcBase == BASE_Decimal)
{/* We are expecting a digit to be entered and base is decimal */
/* Replace current entry and value string with Pi approximation */
strcpy(OVcalcValStr, "3.14159265358979323846");
strcpy(&OVcalcDspStr[OVcalcCEIndex], OVcalcValStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
if ( OVcalcParenBalance > 0 )
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
else
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
}/*if*/
return self;
}/* END EnterPi */
/******************************************************************************
* METHOD:- Equals *
* This method handles the actions required when the user presses the "=" *
* button. *
******************************************************************************/
- Equals:sender
{ /* Local Variables */
double curval;
/* BEGIN Equals */
if ( (OVcalcAcceptMode & ACCEPT_Operator) != 0 &&
(OVcalcAcceptMode & ACCEPT_CloseParen) == 0 )
{/* We are expecting an operator and parens are balanced, so the */
/* = operator is valid at this time. */
/* With a valid equal operator set new acceptance state */
OVcalcAcceptMode = ACCEPT_Digit | ACCEPT_Operator | ACCEPT_Function |
ACCEPT_OpenParen;
if ( [self ValueEntered] )
{/* We have an Equals operator after an unprocessed value */
/* Translate value string to binary and push onto value stack */
[self ScanValue:OVcalcValStr :&curval];
[OVvalStack Push:curval];
/* Clear value string for next entered value */
OVcalcValStr[0] = 0;
}
/* "Equals" has lowest precedence, reduce expresion */
[self ReduceExpression:OPCODE_Equals];
/* Top of value stack now has results of expression, display it and */
/* preload value string with result, and set state to Equalized */
[OVvalStack Pop:&curval];
[self FormatValue:curval :OVcalcDspStr];
[self FormatValue:curval :OVcalcValStr];
[OVcalcDisplay newLine:self];
[OVcalcDisplay setCurrentLine:" = "];
[OVcalcDisplay appendCurrentLine:OVcalcValStr];
[OVcalcDisplay newLine:self];
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
OVcalcCEIndex = 0;
OVcalcEqualized = YES;
}
else
[self SyntaxError: OPCODE_Equals: "="];
return self;
}/* END Equals */
/******************************************************************************
* METHOD:- Exponential *
* Perform actions in response to the E^X button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- Exponential:sender;
{/* BEGIN Exponential */
if ( OVcalcInverse )
/* The Inverse modifier is on, Opcode will be Ln */
[self ProcessFunction:OPCODE_Ln :"Ln:"];
else
/* The Inverse modifier is off, Opcode will be an E^X */
[self ProcessFunction:OPCODE_Exp :"Exp:"];
return(self);
}/* END Exponential */
/******************************************************************************
* METHOD:- Factorial *
* Perform actions in response to the X! button being pressed. Send opcode *
* to the Function Processor routine. *
******************************************************************************/
- Factorial:sender;
{/* BEGIN Factorial */
[self ProcessFunction:OPCODE_Factorial :"X!:"];
return self;
}/* END Factorial */
/******************************************************************************
* METHOD:- Integer *
* Perform actions in response to the [Int] button being pressed. Send the *
* opcode to the Function Processor routine. *
******************************************************************************/
- Integer:sender;
{/* BEGIN Integer */
[self ProcessFunction:OPCODE_Integer :"Int:"];
return self;
}/* END Integer */
/******************************************************************************
* METHOD:- FormatValue *
* This method accepts a single operand value and formats into the supplied *
* string according to the current numeric base and operand unit size. *
******************************************************************************/
- FormatValue: (double)operand : (char*) fmtstr
{ /* Local Variables */
char byte; /* Space to convert double operand to byte value */
long longword; /* Space to convert double operand to longword value */
char hexstr[16]; /* Input string for binary conversion */
short word; /* Space to convert double operand to word value */
/* BEGIN FormatValue */
if ( OVcalcBase == BASE_Decimal )
sprintf(fmtstr, "%.*f", OVcalcPrecsnVal, operand);
/*sprintf(fmtstr, "%.*g", OVcalcPrecsnVal,operand);*/
else if ( OVcalcBase == BASE_Hex )
switch ( OVcalcUnit )
{
case UNIT_Longword:
longword = operand;
sprintf(fmtstr, "%*.*lX", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned long)longword);
break;
case UNIT_Word:
word = operand;
sprintf(fmtstr, "%*.*hX", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned short)word);
break;
case UNIT_Byte:
byte = operand;
sprintf(fmtstr, "%*.*X", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned int)byte);
break;
}/*switch*/
else if ( OVcalcBase == BASE_Octal )
switch ( OVcalcUnit )
{
case UNIT_Longword:
longword = operand;
sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned long)longword);
break;
case UNIT_Word:
word = operand;
sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned short)word);
break;
case UNIT_Byte:
byte = operand;
sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
(unsigned char)byte);
break;
}/*switch*/
else if ( OVcalcBase == BASE_Binary )
{
switch ( OVcalcUnit )
{
case UNIT_Longword:
longword = operand;
sprintf(hexstr, "%8.8X", (unsigned long)longword);
break;
case UNIT_Word:
word = operand;
sprintf(hexstr, "%4.4X", (unsigned short)word);
break;
case UNIT_Byte:
byte = operand;
sprintf(hexstr, "%2.2X", (unsigned char)byte);
break;
}/*switch*/
STR_HexToBinary (hexstr,fmtstr);
/* Adjust resultant value to current precision setting */
longword = strlen(fmtstr);
if ( index(fmtstr,'1') == NULL )
/* The string contains all zeroes, display precision length zero */
word = longword - OVcalcPrecsnVal;
else
{/* Adjust value string for non-zero case */
word = strlen(index(fmtstr, '1'));
if ( word < OVcalcPrecsnVal )
/* Value shorter than precision, trim leading zeroes for proper pad */
word = longword - OVcalcPrecsnVal;
else
/* Value longer than precision, trim all leading zeroes */
word = longword - word;
}
strcpy(fmtstr,fmtstr+word);
}
return(self);
}/* END FormatValue */
/******************************************************************************
* METHOD:- MemoryClear *
* Process the [MC] button event. This routine clears the contents of the *
* current memory value string and its associated text window. *
******************************************************************************/
- MemoryClear:sender
{/* BEGIN MemoryClear */
OVcalcMemDspStr[0] = 0;
OVcalcMemoryVal[OVcalcMemoryCell] = 0.0;
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
return(self);
}/* END MemoryClear */
/******************************************************************************
* METHOD:- MemoryRecall *
* Process the [MR] button event. This routine checks to see if the *
* Calculator is in a state to accept an operand. If it is the value in the *
* Memory string is entered as the new operand. *
******************************************************************************/
- MemoryRecall:sender
{/* BEGIN MemoryRecall */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 )
{/* We are expecting a digit to be entered */
/* Copy memory value string to current & display strings, redisplay */
strcpy(OVcalcValStr, OVcalcMemDspStr);
strcpy(&OVcalcDspStr[OVcalcCEIndex], OVcalcMemDspStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
if ( OVcalcParenBalance > 0 )
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
else
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
}/*if*/
return self;
}/* END MemoryRecall */
/******************************************************************************
* METHOD:- MemorySelect *
* This method handles messages from the memory cell selection buttons. It *
* is responsible for modifying the current memory cell index and updating the *
* memory cell label and display string. *
******************************************************************************/
- MemorySelect:sender;
{ /* Local Variables */
char fmtstr[2];
/* BEGIN MemorySelect */
/* increment/decrement memory cell index according to selection button */
OVcalcMemoryCell += [sender selectedTag];
if ( OVcalcMemoryCell < MCELL_minimum ) OVcalcMemoryCell = MCELL_minimum;
if ( OVcalcMemoryCell > MCELL_maximum ) OVcalcMemoryCell = MCELL_maximum;
/* Set the memory cell label to correspond to the current cell index */
fmtstr[0] = 'A' + OVcalcMemoryCell;
fmtstr[1] = 0;
[OVcalcMemCellLabel setStringValue:fmtstr];
/* Set the memory value display to the cell string */
if ( OVcalcMemoryVal[OVcalcMemoryCell] == 0.0 )
/* Use a null string instead of a formatted 0 */
OVcalcMemDspStr[0] = 0;
else
/* Format the value in current base and unit selection */
[self FormatValue:OVcalcMemoryVal[OVcalcMemoryCell] :OVcalcMemDspStr];
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
return(self);
}/* END MemorySelect */
/******************************************************************************
* METHOD:- MemoryStore *
* Process the [MS] button event. A check is made to see if a valid value *
* currently exists in the current value string. If no valid value has been *
* entered the routine returns without taking any action. Otherwise the *
* current value string is copied to memory value string, and the memory text *
* window is then updated with the new value. *
******************************************************************************/
- MemoryStore:sender
{/* BEGIN MemoryStore */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( [self ValueEntered] )
{/* A viable operand value has been entered or is available */
/* Copy current value string & display Memory string in text window */
strcpy (OVcalcMemDspStr, OVcalcValStr);
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
[self ScanValue:OVcalcMemDspStr :&OVcalcMemoryVal[OVcalcMemoryCell]];
}
return self;
}/* END MemoryStore */
/******************************************************************************
* METHOD:- Minus *
* Process the Minus operator button event. *
******************************************************************************/
- Minus:sender
{/* BEGIN Minus */
[self ProcessOperator: OPCODE_Minus: "-"];
return self;
}/* END Minus */
/******************************************************************************
* METHOD:- Modify *
* Process the Modify operator button event. *
******************************************************************************/
- Modify:sender
{/* BEGIN Modify */
[self ProcessOperator: OPCODE_Modify: "%"];
return self;
}/* END Modify */
/******************************************************************************
* METHOD:- Multiply *
* Process the Multiply operator button event. *
******************************************************************************/
- Multiply:sender
{/* BEGIN Multiply */
[self ProcessOperator: OPCODE_Multiply: "*"];
return self;
}/* END Multiply */
/******************************************************************************
* METHOD:- OpenParen *
* Handle the actions needed when the "(" button is pressed. *
******************************************************************************/
- OpenParen:sender
{/* BEGIN OpenParen */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_OpenParen) != 0 )
{/* We are expecting an open paren */
/* With a valid Open Paren set new acceptance state */
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
OVcalcParenBalance++; /* increment open paren count */
if (OVcalcEqualized == YES)
{/* We need to wipeout preloaded equalized value and start a new one */
OVcalcDspStr[0] = OVcalcValStr[0] = 0;
OVcalcEqualized = NO;
}
/* Add operator symbol to display string and redisplay */
strcat (OVcalcDspStr, "(");
OVcalcCEIndex = strlen(OVcalcDspStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
/* Push the new opcode on the operator stack */
[OVopStack Push:OPCODE_OpenParen];
}
else
/* Enter error state and display syntax error */
[self SyntaxError:OPCODE_OpenParen :"("];
return self;
}/* END OpenParen */
/******************************************************************************
* METHOD:- Plus *
* Process the Plus operator button event. *
******************************************************************************/
- Plus:sender
{/* BEGIN Plus */
[self ProcessOperator: OPCODE_Plus: "+"];
return self;
}/* END Plus */
/******************************************************************************
* METHOD:- ProcessDigit *
* This method check the validity of each digit of an operand as it is *
* entered. The character value of the new digit is supplied by the caller *
* and if acceptable is appended to the current Display and Value strings. *
******************************************************************************/
- ProcessDigit:(char) dgtchr
{ /* Local Variables */
char *inset; /* Char pointer to locate dgtchr in base digit set */
/* BEGIN ProcessDigit */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 )
{/* We are expecting a digit to be entered */
/* Determine if digit valid for current number Base */
if (OVcalcBase == BASE_Decimal)
inset = index("0123456789.",dgtchr);
else if (OVcalcBase == BASE_Hex)
inset = index("0123456789ABCDEF",dgtchr);
else if (OVcalcBase == BASE_Octal)
inset = index("01234567",dgtchr);
else /* OVcalcBase == BASE_Binary */
inset = index("01",dgtchr);
if (inset == NULL)
/* Entered digit is not proper set for base, ignore it */
return self;
if (OVcalcEqualized == YES)
{/* We need to wipeout preloaded equalized value and start a new one */
OVcalcDspStr[0] = OVcalcValStr[0] = 0;
OVcalcEqualized = NO;
}
/* Determine if we have exceeded maximum digit limit */
if (OVcalcValStr[0] == '-' && strlen(OVcalcValStr) == OVcalcMaxDigits+1)
return self;
else if (strlen(OVcalcValStr) == OVcalcMaxDigits)
return self;
/* Append the digit character to the Value and Display strings */
strncat (OVcalcValStr, &dgtchr, 1);
strncat (OVcalcDspStr, &dgtchr, 1);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
if ( [self ValueEntered] )
{/* Viable operand has been formed, so an operator is now acceptable */
if ( OVcalcParenBalance > 0 )
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
else
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
}
}
return self;
}/* END ProcessDigit */
/******************************************************************************
* METHOD:- ProcessFunction *
* Perform all common processing Function call type operator codes. *
******************************************************************************/
- ProcessFunction: (int) opcode: (char *)symbol
{ /* Local Variables */
double curval;
int preced;
/* BEGIN ProcessFunction */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( OVcalcFunctionMode == FMODE_Evaluate )
{/* Process the function within the context of the current expression */
if ( (OVcalcAcceptMode & ACCEPT_Function) != 0 )
{/* We are expecting a function call opcode */
/* Set new acceptance mode after a valid operator */
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen;
if (OVcalcEqualized == YES)
{/* We need to wipeout preloaded equalized value and start a new one */
OVcalcDspStr[0] = OVcalcValStr[0] = 0;
OVcalcEqualized = NO;
}
/* Add operator symbol to display string and redisplay */
strcat (OVcalcDspStr, symbol);
OVcalcCEIndex = strlen(OVcalcDspStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
[OVopStack ComparePrecedence: opcode: &preced];
if ( preced <= 0 )
/* OpCode has a lower precedence then the operator on the top of */
/* operator stack, reduce expresion until lower opcode is reached */
[self ReduceExpression:opcode];
/* Push the new opcode on the operator stack */
[OVopStack Push:opcode];
}
else
/* Enter error state and display syntax error */
[self SyntaxError:opcode :symbol];
}
else if ( OVcalcFunctionMode == FMODE_Operate )
{/* Try to operate the function on the currently entered value */
if ( [self ValueEntered] )
{/* Then there is a value to operate the function on */
/* Read the currently entered value and perform function on value */
[self ScanValue:OVcalcValStr :&curval];
curval = [self ProcessOpcode:opcode :curval :curval];
/* Clear current entry strings, reformat and redisplay */
OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
[self FormatValue:curval :OVcalcValStr];
strcat (OVcalcDspStr, OVcalcValStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
}
}
return self;
}/* END ProcessFunction */
/******************************************************************************
* INSTANCE METHOD:- ProcessOpcode *
* This method executes the operation indicated by the supplied opcode using *
* the supplied operands. The result is returned to the caller via the retrun *
* value. *
******************************************************************************/
- (double) ProcessOpcode :(int)opcode :(double)operand1 :(double)operand2
{ /* Local Variables */
double result; /* Temporary workspace for opcode evaluation */
long longword1; /* Bitwise Longword operand1 */
long longword2; /* Bitwise Longword operand2 */
short word1; /* Bitwise Word operand1 */
short word2; /* Bitwise Word operand2 */
char byte1; /* Bitwise Byte operand1 */
char byte2; /* Bitwise Byte operand2 */
/* BEGIN PerformOpcode */
switch (opcode)
{
case OPCODE_ACos:
result = acos(operand1);
if ( OVcalcMetric == METRIC_Degrees )
result *= (180.0/M_PI);
if ( OVcalcMetric == METRIC_Grads )
result *= (200.0/M_PI);
return result;
case OPCODE_ACosH:
return acosh(operand1);
case OPCODE_And:
byte1 = word1 = longword1 = operand1;
byte2 = word2 = longword2 = operand2;
if ( OVcalcUnit == UNIT_Longword )
result = (longword1 & longword2);
else if ( OVcalcUnit == UNIT_Word )
result = (word1 & word2);
else if ( OVcalcUnit == UNIT_Byte )
result = (byte1 & byte2);
else
result = NAN;
return result;
case OPCODE_ASin:
result = asin(operand1);
if ( OVcalcMetric == METRIC_Degrees )
result *= (180.0/M_PI);
if ( OVcalcMetric == METRIC_Grads )
result *= (200.0/M_PI);
return result;
case OPCODE_ASinH:
return asinh(operand1);
case OPCODE_ATan:
result = atan(operand1);
if ( OVcalcMetric == METRIC_Degrees )
result *= (180.0/M_PI);
if ( OVcalcMetric == METRIC_Grads )
result *= (200.0/M_PI);
return result;
case OPCODE_ATanH:
return atanh(operand1);
case OPCODE_Cos:
if ( OVcalcMetric == METRIC_Degrees )
operand1 *= (M_PI/180.0);
if ( OVcalcMetric == METRIC_Grads )
operand1 *= (M_PI/200.0);
return cos(operand1);
case OPCODE_CosH:
return cosh(operand1);
case OPCODE_Divide:
return (operand1 / operand2);
case OPCODE_Exp:
return exp(operand1);
case OPCODE_Factorial:
if (operand1 < 0.0)
result = NAN;
else
{/* Round to nearest integer */
operand1 = rint(operand1);
result = operand2 = 1.0;
while (operand2 <= operand1)
{
result *= operand2;
operand2 += 1.0;
}
}
return result;
case OPCODE_Integer:
return rint(operand1);
case OPCODE_Ln:
return log(operand1);
case OPCODE_Log:
return log10(operand1);
case OPCODE_LShift:
byte1 = word1 = longword1 = operand1;
if ( OVcalcUnit == UNIT_Longword )
result = (longword1 << 1);
else if ( OVcalcUnit == UNIT_Word )
result = (word1 << 1);
else if ( OVcalcUnit == UNIT_Byte )
result = (byte1 << 1);
else
result = NAN;
return result;
case OPCODE_Minus:
return (operand1 - operand2);
case OPCODE_Modify:
result = floor(operand1/operand2);
result = operand1 / operand2 - result;
result = result * operand2;
return rint(result);
case OPCODE_Multiply:
return (operand1 * operand2);
case OPCODE_Not:
byte1 = word1 = longword1 = operand1;
if ( OVcalcUnit == UNIT_Longword )
result = ~longword1;
else if ( OVcalcUnit == UNIT_Word )
result = ~word1;
else if ( OVcalcUnit == UNIT_Byte )
result = ~byte1;
else
result = NAN;
return result;
case OPCODE_Or:
byte1 = word1 = longword1 = operand1;
byte2 = word2 = longword2 = operand2;
if ( OVcalcUnit == UNIT_Longword )
result = (longword1 | longword2);
else if ( OVcalcUnit == UNIT_Word )
result = (word1 | word2);
else if ( OVcalcUnit == UNIT_Byte )
result = (byte1 | byte2);
else
result = NAN;
return result;
case OPCODE_Pi:
return M_PI;
case OPCODE_Plus:
return (operand1 + operand2);
case OPCODE_Recip:
return ((double)1.0 / operand1);
case OPCODE_RShift:
byte1 = word1 = longword1 = operand1;
if ( OVcalcUnit == UNIT_Longword )
result = (longword1 >> 1);
else if ( OVcalcUnit == UNIT_Word )
result = (word1 >> 1);
else if ( OVcalcUnit == UNIT_Byte )
result = (byte1 >> 1);
else
result = NAN;
return result;
case OPCODE_Sin:
if ( OVcalcMetric == METRIC_Degrees )
operand1 *= (M_PI/180.0);
if ( OVcalcMetric == METRIC_Grads )
operand1 *= (M_PI/200.0);
return sin(operand1);
case OPCODE_SinH:
return sinh(operand1);
case OPCODE_SqrRoot:
return sqrt(operand1);
case OPCODE_Tan:
if ( OVcalcMetric == METRIC_Degrees )
operand1 *= (M_PI/180.0);
if ( OVcalcMetric == METRIC_Grads )
operand1 *= (M_PI/200.0);
return tan(operand1);
case OPCODE_TanH:
return tanh(operand1);
case OPCODE_TenToX:
return pow((double) 10.0,operand1);
case OPCODE_Xor:
byte1 = word1 = longword1 = operand1;
byte2 = word2 = longword2 = operand2;
if ( OVcalcUnit == UNIT_Longword )
result = (longword1 ^ longword2);
else if ( OVcalcUnit == UNIT_Word )
result = (word1 ^ word2);
else if ( OVcalcUnit == UNIT_Byte )
result = (byte1 ^ byte2);
else
result = NAN;
return result;
case OPCODE_XrootY:
return pow(operand2,1.0/operand1);
case OPCODE_XSquared:
return (operand1*operand1);
case OPCODE_XToY:
return pow(operand1,operand2);
}/*switch*/
return NAN;
}/* END PerformOpcode */
/******************************************************************************
* METHOD:- ProcessOperator *
* Perform all common processing for all normal arithmetic operators. *
******************************************************************************/
- ProcessOperator: (int) opcode: (char *)symbol
{ /* Local Variables */
double curval;
int preced;
/* BEGIN ProcessOperator */
if ( OVcalcFrozen == YES )
/* Calculator is in error state, ignore input until cleared */
return self;
if ( (OVcalcAcceptMode & ACCEPT_Operator) != 0 )
{/* We are expecting an operator */
/* Set new acceptance mode after a valid operator */
OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
/* Add operator symbol to display string and redisplay */
strcat (OVcalcDspStr, symbol);
OVcalcCEIndex = strlen(OVcalcDspStr);
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
if ( [self ValueEntered] )
{/* We have an operator after an unprocessed value */
OVcalcEqualized = NO; /* Cancel expression equalization state */
/* Translate value string to binary and push onto value stack */
[self ScanValue:OVcalcValStr :&curval];
[OVvalStack Push:curval];
/* Clear value string for next entered value */
OVcalcValStr[0] = 0;
}
[OVopStack ComparePrecedence: opcode: &preced];
if ( preced <= 0 )
/* OpCode has a lower precedence then the operator on the top of */
/* operator stack, reduce expresion until lower opcode is reached */
[self ReduceExpression:opcode];
/* Push the new opcode on the operator stack */
[OVopStack Push:opcode];
}
else
/* Enter error state and display syntax error */
[self SyntaxError:opcode :symbol];
return self;
}/* END ProcessOperator */
/******************************************************************************
* METHOD:- ReduceExpression *
* This method performs each of the operations on the Operator stack until *
* an operator of lesser precedence then the supplied operator is found on the *
* top of the stack. *
******************************************************************************/
- ReduceExpression: (int) curopr
{ /* Local Variables */
int noperands;
double operand1;
double operand2;
int preced;
int topopr;
/* BEGIN ReduceExpression */
[OVopStack ComparePrecedence: curopr: &preced];
while ( preced <= 0 )
{/* The stack top opcode still has higher precedence then current opcode */
/* Pop the opcode of the top of the stack and process it */
[OVopStack Pop:&topopr];
/* Retrieve the proper number of operands for this opcode */
[OVopStack NumberOfOperands:topopr :&noperands];
if ( noperands == 1 )
{
[OVvalStack Pop:&operand1];
operand2 = operand1;
}
else if ( noperands == 2 )
{
[OVvalStack Pop:&operand2];
[OVvalStack Pop:&operand1];
}
/* Execute the opcode on the operands and stack result */
[OVvalStack Push:[self ProcessOpcode:topopr :operand1 :operand2]];
/* Compare current opcode against new Operator stack top */
[OVopStack ComparePrecedence: curopr: &preced];
}/*while*/
return self;
}/* END ReduceExpression */
/******************************************************************************
* METHOD:- Reciprocal *
* Perform actions in response to the X^-1 button being pressed. *
******************************************************************************/
- Reciprocal:sender;
{/* BEGIN Reciprocal */
[self ProcessFunction:OPCODE_Recip :"1/X:"];
return(self);
}/* END Reciprocal */
/******************************************************************************
* METHOD:- ScanValue *
* This method accepts a string representing a formated value in the current *
* base and unit size. This routine converts the formated string to its *
* appropriate binary value. *
******************************************************************************/
- ScanValue: (char*) fmtstr :(double*)operand
{ /* Local Variables */
char byte; /* Space to convert double operand to byte value */
long longword; /* Space to convert double operand to longword value */
char hexstr[16]; /* Input string for binary conversion */
short word; /* Space to convert double operand to word value */
/* BEGIN ScanValue */
if ( OVcalcBase == BASE_Decimal )
sscanf(fmtstr, "%lf", operand);
else if ( OVcalcBase == BASE_Hex )
{
sscanf(fmtstr, "%lx", (unsigned long*)&longword);
switch ( OVcalcUnit )
{
case UNIT_Longword:
*operand = longword;
break;
case UNIT_Word:
*operand = word = longword;
break;
case UNIT_Byte:
*operand = byte = word = longword;
break;
}/*switch*/
}
else if ( OVcalcBase == BASE_Octal )
{
sscanf(fmtstr, "%o", (unsigned long*)&longword);
switch ( OVcalcUnit )
{
case UNIT_Longword:
*operand = longword;
break;
case UNIT_Word:
*operand = word = longword;
break;
case UNIT_Byte:
*operand = byte = word = longword;
break;
}/*switch*/
}
else if ( OVcalcBase == BASE_Binary )
{
STR_BinaryToHex (fmtstr, hexstr);
sscanf(hexstr, "%lX", (unsigned long*)&longword);
switch ( OVcalcUnit )
{
case UNIT_Longword:
*operand = longword;
break;
case UNIT_Word:
*operand = word = longword;
break;
case UNIT_Byte:
*operand = byte = word = longword;
break;
}/*switch*/
}
return(self);
}/* END ScanValue */
/******************************************************************************
* METHOD:- Sine *
* Perform actions in response to the SIN button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- Sine:sender;
{/* BEGIN Sine */
if ( OVcalcInverse )
{/* The Inverse modifier is on, Opcode will be an ASINx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be ASINH */
[self ProcessFunction:OPCODE_ASinH :"ASINH:"];
else
/* Hyperbolic modifier off, Opcode will be ASIN */
[self ProcessFunction:OPCODE_ASin :"ASIN:"];
}
else
{/* The Inverse modifier is off, Opcode will be an SINx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be SINH */
[self ProcessFunction:OPCODE_SinH :"SINH:"];
else
/* Hyperbolic modifier off, Opcode will be SIN */
[self ProcessFunction:OPCODE_Sin :"SIN:"];
}
return(self);
}/* END Sine */
/******************************************************************************
* METHOD:- SyntaxError *
* Display a syntax error message and set the Frozen state to true. The *
* Frozen state will block further input until a Clear operation is performed. *
******************************************************************************/
- SyntaxError: (int) opcode: (char *) symbol;
{/* BEGIN SyntaxError */
/* Append syntax error message to display string and redisplay */
strcat (OVcalcDspStr, "<Syntax Error>");
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
/* Set Frozen state to block further input procedures, until clear */
OVcalcFrozen = YES;
#ifdef DEBUG
/* Dump stack states to standard output */
printf("Syntax Error due to [%s] operator\n",symbol);
printf("Value Stack:\n");
[OVvalStack PrintStack];
printf("\nOperator Stack:\n");
[OVopStack PrintStack];
#endif
return self;
}/* END SyntaxError */
/******************************************************************************
* METHOD:- Tangent *
* Perform actions in response to the Tan button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- Tangent:sender;
{/* BEGIN Tangent */
if ( OVcalcInverse )
{/* The Inverse modifier is on, Opcode will be an ATANx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be ATANH */
[self ProcessFunction:OPCODE_ATanH :"ATANH:"];
else
/* Hyperbolic modifier off, Opcode will be ATAN */
[self ProcessFunction:OPCODE_ATan :"ATAN:"];
}
else
{/* The Inverse modifier is off, Opcode will be an TANx */
if ( OVcalcHyperbolic )
/* Hyperbolic modifier on, Opcode will be TANH */
[self ProcessFunction:OPCODE_TanH :"TANH:"];
else
/* Hyperbolic modifier off, Opcode will be TAN */
[self ProcessFunction:OPCODE_Tan :"TAN:"];
}
return(self);
}/* END Tangent */
/******************************************************************************
* METHOD:- TenToTheX *
* Perform actions in response to the 10^X button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- TenToTheX:sender;
{/* BEGIN TenToTheX */
if ( OVcalcInverse )
/* The Inverse modifier is on, Opcode will be Log */
[self ProcessFunction:OPCODE_Log :"LOG:"];
else
/* The Inverse modifier is off, Opcode will be an 10^X */
[self ProcessFunction:OPCODE_TenToX :"10^X:"];
return(self);
}/* END TenToTheX */
/******************************************************************************
* METHOD: ValueEntered *
* Determine if the current value string has enough characters in it to *
* constitute an operand. *
******************************************************************************/
- ValueEntered
{/* BEGIN ValueEntered */
if ( strlen(OVcalcValStr) == 0 )
return nil;
if ( OVcalcValStr[0] == '.' && strlen(OVcalcValStr) == 1 )
return nil;
if ( OVcalcValStr[0] == '-')
{
if ( strlen(OVcalcValStr) == 1 )
return nil;
if ( OVcalcValStr[1] == '.' && strlen(OVcalcValStr) == 2 )
return nil;
}
return self;
}/* END ValueEntered */
/******************************************************************************
* METHOD:- XSquared *
* Perform actions in response to the X^2 button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Function *
* Processor routine. *
******************************************************************************/
- XSquared:sender;
{/* BEGIN XSquared */
if ( OVcalcInverse )
/* The Inverse modifier is on, Opcode will be Square Root */
[self ProcessFunction:OPCODE_SqrRoot :"SQRT:"];
else
/* The Inverse modifier is off, Opcode will be an X^2 */
[self ProcessFunction:OPCODE_XSquared :"X^2:"];
return(self);
}/* END XSquared */
/******************************************************************************
* METHOD:- XToTheY *
* Perform actions in response to the X^Y button being pressed. Based on *
* the current modifier flags send the appropriate opcode to the Operator *
* Processor routine. *
******************************************************************************/
- XToTheY:sender;
{/* BEGIN XToTheY */
if ( OVcalcInverse )
/* The Inverse modifier is on, Opcode will be XrootY */
[self ProcessOperator:OPCODE_XrootY :"XrootY"];
else
/* The Inverse modifier is off, Opcode will be an X^Y */
[self ProcessOperator:OPCODE_XToY :"X^Y"];
return(self);
}/* END XToTheY */
/******************************************************************************
* METHOD:- setTag *
* This method is used so that this object can pose as a Control object. *
******************************************************************************/
- setTag:(int)anInt;
{/* BEGIN setTag */
OVtag = anInt;
return self;
}/* END setTag */
/******************************************************************************
* METHOD:- selectedTag *
* This method is used so that this object can pose as a Control object. *
******************************************************************************/
- (int) selectedTag
{/* BEGIN selectedTag */
return OVtag;
}/* END selectedTag */
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.