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.