home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-27 | 18.4 KB | 1,038 lines |
- //
- // Calculator.m
- // Copyright (c) 1990, 1991,1992 by Jiro Nakamura
- // All rights reserved
- //
- // Maintains a simple 4++ function calculator in Objective-C
- // Doesn't handle algebraic notation or simple ordering.
- //
- // by Jiro Nakamura (jiro@shaman.com)
- //
- // RCS Information
- // Revision Number-> $Revision: 1.8 $
- // Last Revised-> $Date: 92/02/02 18:24:36 $
- //
- static char rcsid[] = "$Id: Calculator.m,v 1.8 92/02/02 18:24:36 jiro Exp Locker: jiro $";
-
- #import <appkit/Application.h>
- #import <appkit/Button.h>
- #import <appkit/Matrix.h>
- #import <appkit/Panel.h>
- #import <appkit/Pasteboard.h>
- #import <appkit/publicWraps.h> /* for NXBeep( ) */
- #import <appkit/ScrollView.h>
- #import "appkit/TextField.h"
- #import <strings.h>
- #import "Calculator.h"
- #import <math.h>
- #import <signal.h>
- #import <libc.h>
-
- #define OP_NOOP 0
- #define OP_ADD 1
- #define OP_SUBTRACT 2
- #define OP_MULTIPLY 3
- #define OP_DIVIDE 4
- #define OP_LOGICAL_OR 5
- #define OP_LOGICAL_AND 6
- #define OP_LOGICAL_EOR 7
- #define OP_LOGICAL_NOR 8
- #define OP_POWER 9
-
- #define SHIFTMASK (NX_ALTERNATEMASK | NX_SHIFTMASK)
-
- char *OperatorDesc[10] =
- {
- "=",
- "+",
- "-",
- "x",
- "/",
- "|",
- "&",
- "eor",
- "nor",
- "^"};
-
-
- #define CALCULATOR_ICON "window.calculator.tiff"
- #define CALCULATOR_TITLE_SHORT "T.I. 68040"
- #define CALCULATOR_TITLE_LONG "Tennessee Instruments"
-
-
- #define DM_BINARY 2
- #define DM_OCTAL 8
- #define DM_DECIMAL 10
- #define DM_HEXADECIMAL 16
-
- // The minimum dimensions of the window
- #define MIN_WIDTH 210.0
- #define MIN_HEIGHT 350.0
-
-
- // Together, these two declarations form the floating point exception
- // part of Calculator
- id me; // Class global attachment to self set in +new
- BOOL fpError; // Global floating point error indicator
-
- void floatingError(int sig)
- {
- [me errorDisplay: "- Floating point error"];
- fpError = YES;
- return;
- }
-
- // Math operators
- double magic( double y, int op, double x)
- {
- switch( op)
- {
- case OP_ADD:
- return y + x;
- case OP_SUBTRACT:
- return y - x ;
- case OP_MULTIPLY:
- return y * x;
- case OP_DIVIDE:
- if( x == 0.0)
- fpError = YES;
- else
- return y / x;
- case OP_LOGICAL_OR:
- return (int) y | (int) x;
- case OP_LOGICAL_AND:
- return (int) y & (int) x;
- case OP_LOGICAL_EOR:
- return (int) y ^ (int) x;
- case OP_POWER:
- return pow( y, x);
- case OP_NOOP:
- return y;
- default:
- NXRunAlertPanel("Unrecognizable math.",
- "Contact Jiro: jiro@shaman.com",
- "OK",NULL, NULL);
- }
- return 0.0;
- }
-
- // Not the most efficient (timewise) but....
- // Note: x should be an integer.
- double factorial(double x)
- {
- double y;
-
- if( x <= 1.0)
- return 1.0;
-
- if( x >= 170) // Larger than this overflows a double
- {
- [me errorDisplay: "- Overflow"];
- return -1;
- }
-
- y = rint(x);
- x = 1;
-
- while( y )
- x *= y--;
-
- return x;
- }
-
- int readBinary( const char *buf )
- {
- int tmp = 0;
- const char *pt = buf;
-
- for( ; *pt != '\0' && (*pt == '0' || *pt == '1'); pt ++ )
- tmp = (tmp << 1) + (*pt - '0') ;
- return tmp;
- }
-
- char * writeBinary(char *buf, int val )
- {
- int tmp;
-
- for( tmp = 15; tmp >= 0; tmp -- )
- {
- buf[15 - tmp] = (val & (1 << tmp) ) ? '1' : '0';
- }
- buf[16] = '\0';
-
- return buf;
- }
-
-
- @implementation Calculator
-
- - initCalc
- {
- #ifdef DEBUG
- fprintf(stderr,"Initing calculator object\n");
- #endif
-
- if( calcDidInit == YES)
- return self;
-
- m = 0.0;
- x = 0.0;
- y = 0.0;
- x_isNew = YES;
- displayMode = DM_DECIMAL;
- x_hasDecimal = NO;
- currentOperation = OP_NOOP;
- [self updateOperationMarker];
- fpError = NO;
- signal( SIGFPE, floatingError);
- me = self;
- calcDidInit = YES;
- scrollText = [scrollDisplay docView];
- [scrollText setAlignment: NX_RIGHTALIGNED];
-
- return self;
- }
-
- - key_add:sender
- {
- [self processPrevious];
- currentOperation = OP_ADD;
- [self updateOperationMarker];
- return self;
- }
-
- - key_subtract:sender
- {
- [self processPrevious];
- currentOperation = OP_SUBTRACT;
- [self updateOperationMarker];
- return self;
- }
-
-
- - key_multiply:sender
- {
- [self processPrevious];
- currentOperation = OP_MULTIPLY;
- [self updateOperationMarker];
- return self;
- }
-
- - key_divide:sender
- {
- [self processPrevious];
- currentOperation = OP_DIVIDE;
- [self updateOperationMarker];
- return self;
- }
-
- - key_logicalOr: sender
- {
- [self processPrevious];
- currentOperation = OP_LOGICAL_OR;
- [self updateOperationMarker];
- return self;
- }
-
- - key_logicalAnd: sender
- {
- [self processPrevious];
- currentOperation = OP_LOGICAL_AND;
- [self updateOperationMarker];
- return self;
- }
-
- - key_logicalEor: sender
- {
- [self processPrevious];
- currentOperation = OP_LOGICAL_EOR;
- [self updateOperationMarker];
- return self;
- }
-
-
-
- - key_clear:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- x = 0;
- x_hasDecimal = NO;
- x_isNew = YES;
- [self setDisplay];
- return self;
- }
-
- - key_number:sender
- {
- char key;
- static char tmp[80];
-
- // We cannot do this from IB and we do not have a
- // appDidInit method
- [invisibleEnterKey setKeyEquivalent:3];
-
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- if( x_isNew)
- {
- [self clearDisplay];
- x_isNew = NO;
- }
-
- key = *[[sender selectedCell]title];
-
- #ifdef DEBUG
- fprintf(stderr,"Key = %c\n", key);
- #endif
-
- if( key == '.') // Decimal point
- if( x_hasDecimal || displayMode != DM_DECIMAL )
- {
- NXBeep();
- return self;
- }
- else
- x_hasDecimal = YES;
-
- if( key == '0' && *([display stringValue]) == '0'
- && x_hasDecimal == NO && displayMode == DM_DECIMAL)
- {
- NXBeep();
- return self;
- }
-
- strcpy( tmp, [display stringValue]);
- strcat( tmp, [[sender selectedCell]title]);
- [display setStringValue: tmp];
- [self getDisplay];
-
- return self;
- }
-
- - key_log:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- [self getDisplay];
-
- if( ([NXApp currentEvent]->flags) & SHIFTMASK)
- {
- [self setScrollOperation: "Ln" andNumber: x];
- x = log( x );
- }
- else
- {
- [self setScrollOperation: "Log" andNumber: x];
- x = log10( x );
- }
- [self setDisplay];
- [self setScrollOperation: "=" andNumber: x];
-
- return self;
- }
-
- - key_squareRoot:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- [self getDisplay];
-
- // alternate key pressed
- if( ([NXApp currentEvent]->flags) & SHIFTMASK)
- {
- [self setScrollOperation: "square" andNumber: x];
- x = pow( x, 2);
- }
- else
- {
- [self setScrollOperation: "root" andNumber: x];
- if( x >= 0.0)
- x = sqrt(x);
- else
- {
- [self errorDisplay: "- Root of negative"];
- return self;
- }
- }
-
- [self setDisplay];
- [self setScrollOperation: "=" andNumber: x];
-
- return self;
- }
-
- - key_memory:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- [self getDisplay];
- #ifdef DEBUG
- fprintf(stderr, "Memory: Tag = %d, m = %f, x = %f\n",
- [sender tag], m, x);
- #endif
-
- switch( [[sender selectedCell] tag])
- {
- case 0: // M+
- m += x;
- [self setScrollOperation: "M+" andNumber: x];
- break;
- case 1: // M-
- m -= x;
- [self setScrollOperation: "M-" andNumber: x];
- break;
- case 2: // M=
- m = x;
- [self setScrollOperation: "M=" andNumber: x];
- break;
- case 3: // MR
- x = m;
- x_isNew = YES;
- x_hasDecimal = NO;
- [self setDisplay];
- [self setScrollOperation: "MR" andNumber: x];
- break;
- case 4: // MC
- m = 0;
- break;
- default:
- NXRunAlertPanel("Dumb Error",
- "Key_memory returns false case. Why? "
- "Ask Jiro.", "OK", NULL, NULL);
- break;
- }
- [self updateMemoryMarker];
- return self;
- }
-
- - key_factorial:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- [self getDisplay];
-
- if( ([NXApp currentEvent]->flags) & SHIFTMASK)
- {
- [self setScrollOperation: "1/" andNumber: x];
-
- if( x != 0.0)
- x = 1/x;
- else
- {
- [self errorDisplay: "- Division by zero"];
- return self;
- }
- }
- else
- {
-
- [self setScrollOperation: "factorial" andNumber: x];
- if( x != rint(x) ) // if x is a non-integer
- {
- [self errorDisplay: "- Non-integer !"];
- return self;
- }
-
- if( x < 0.0 ) // if x is negative
- {
- [self errorDisplay: "- Negative !"];
- return self;
- }
- x = factorial(x);
- if( fpError )
- {
- [self errorDisplay];
- return nil;
- }
- }
-
- [self setDisplay];
- [self setScrollOperation: "=" andNumber: x];
-
- return self;
- }
-
-
-
- - key_enter:sender
- {
- #ifdef DEBUG
- fprintf(stderr, "Enter key hit. y = %f. Op = %d.\n", y,
- currentOperation);
- #endif
-
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- [self getDisplay];
-
-
- if( currentOperation != OP_NOOP)
- {
- [self setScrollOperation: OperatorDesc[currentOperation]
- andNumber: x];
- x = magic(y, currentOperation, x);
- if( fpError )
- {
- [self errorDisplay];
- return nil;
- }
- currentOperation = OP_NOOP;
- [self updateOperationMarker];
- }
- [self setDisplay];
-
- [self setScrollOperation: "="
- andNumber: x];
-
- return self;
- }
-
- - key_allClear:sender
- {
- #ifdef DEBUG
- fprintf(stderr,"All Clear\n");
- #endif
-
- x = y = 0.0;
- x_isNew = YES;
- x_hasDecimal = NO;
- currentOperation = OP_NOOP;
- [self updateOperationMarker];
- fpError = NO;
- [self setDisplay];
-
-
- [scrollText setSel: [scrollText textLength]
- : [scrollText textLength]];
- [scrollText replaceSel: "\nAC\n"];
- [scrollText scrollSelToVisible];
- [scrollText hideCaret];
- [self makeFirstResponder: self];
-
- return self;
- }
-
- - key_power:sender
- {
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- // alternate key pressed
- if( ([NXApp currentEvent]->flags) & SHIFTMASK)
- {
- [self setScrollOperation: "exp" andNumber: x];
- [self getDisplay];
- x = exp(x);
- [self setDisplay];
- [self setScrollOperation: "=" andNumber: x];
- }
- else
- {
- [self processPrevious];
- currentOperation = OP_POWER;
- [self updateOperationMarker];
- }
-
- return self;
- }
-
- - key_negate:sender
- {
- if( fpError || displayMode == DM_OCTAL || displayMode == DM_BINARY)
- {
- NXBeep();
- return self;
- }
-
-
- [self getDisplay];
-
- if( ([NXApp currentEvent]->flags) & SHIFTMASK)
- {
- [self setScrollOperation: "abs" andNumber: x];
- x = fabs(x);
- }
- else
- {
- [self setScrollOperation: "+/-" andNumber: x];
- x = -x;
- }
-
-
- [self setDisplay];
- [self setScrollOperation: "=" andNumber: x];
- return self;
- }
-
- - key_baseChanged: sender
- {
- static char buf[30];
-
- switch( atoi( [[sender selectedCell]title]) )
- {
- case 2: // Previous was base 2, so new is base 8
- displayMode = DM_OCTAL;
- [hexadecimalKeyMatrix setEnabled: NO];
- [[sender selectedCell] setTitle: "8"];
-
- [decimalKeyMatrix setEnabled: YES];
- [[decimalKeyMatrix findCellWithTag: 8] setEnabled: NO];
- [[decimalKeyMatrix findCellWithTag: 9] setEnabled: NO];
- [decimalPointKey setEnabled: NO];
- break;
- case 8: // Previous was base 8, so new is base 10
- displayMode = DM_DECIMAL;
- [[sender selectedCell] setTitle: "10"];
- [hexadecimalKeyMatrix setEnabled: NO];
- [[decimalKeyMatrix findCellWithTag: 8]
- setEnabled: YES];
- [[decimalKeyMatrix findCellWithTag: 9] setEnabled:
- YES];
- [decimalPointKey setEnabled: YES];
- break;
- case 10: // Previous was base 10, so new is base 16
- displayMode = DM_HEXADECIMAL;
- [hexadecimalKeyMatrix setEnabled: YES];
- [[sender selectedCell] setTitle: "16"];
- [decimalPointKey setEnabled: NO];
- break;
- case 16: // Previous was base 16, so new is base 2
- displayMode = DM_BINARY;
- [hexadecimalKeyMatrix setEnabled: NO];
- [[sender selectedCell] setTitle: "2"];
-
- [decimalKeyMatrix setEnabled: NO];
- [[decimalKeyMatrix findCellWithTag: 0]
- setEnabled: YES];
- [[decimalKeyMatrix findCellWithTag: 1]
- setEnabled: YES];
-
- [decimalPointKey setEnabled: NO];
- break;
- default:
- NXBeep();
- return nil;
- }
- [self setDisplay];
-
- sprintf(buf, "\nChange to base %d", displayMode );
- [scrollText setSel: [scrollText textLength]
- : [scrollText textLength]];
- [scrollText replaceSel: buf];
- [self setScrollOperation: "=" andNumber: x];
-
- [self getDisplay];
- return self;
- }
-
-
-
- - setDisplay
- {
- static char displayBuf[20];
- #ifdef DEBUG
- fprintf(stderr,"Display is set to %f.\n", x);
- #endif
-
- switch (displayMode )
- {
- case DM_BINARY:
- writeBinary(displayBuf, x);
- [display setStringValue: displayBuf];
- break;
- case DM_OCTAL:
- sprintf( displayBuf, "%o", (unsigned int) x);
- [display setStringValue: displayBuf];
- break;
- case DM_HEXADECIMAL:
- sprintf( displayBuf, "%x", (unsigned int) x);
- [display setStringValue: displayBuf];
- break;
- case DM_DECIMAL:
- default:
- [display setDoubleValue: x];
- break;
- }
-
- x_isNew = YES;
- [self updateMemoryMarker];
- [self makeFirstResponder: self];
- return self;
- }
-
- - clearDisplay
- {
- [display setStringValue: ""];
- x_hasDecimal = NO;
- x_isNew = YES;
- return self;
- }
-
- - errorDisplay
- {
- [display setStringValue: "Error"];
- fpError = YES;
- return self;
- }
-
- - errorDisplay: (char *) errorString
- {
- static char buf[80];
-
- fpError = YES;
- sprintf(buf, "Error %s", errorString);
- [display setStringValue: buf];
-
-
- sprintf(buf, "\nError %s", errorString);
- [scrollText setSel: [scrollText textLength]
- : [scrollText textLength]];
- [scrollText replaceSel: buf];
- [scrollText scrollSelToVisible];
- [scrollText hideCaret];
- [self makeFirstResponder: self];
-
- return self;
- }
-
- - (double) getDisplay
- {
- unsigned int utmp;
-
- if( !calcDidInit )
- return( x = 0.0);
-
- switch (displayMode)
- {
- case DM_BINARY:
- x = readBinary([display stringValue] );
- break;
- case DM_OCTAL:
- sscanf([display stringValue], "%o", &utmp);
- x = utmp;
- break;
- case DM_HEXADECIMAL:
- sscanf([display stringValue], "%x", &utmp);
- x = utmp;
- break;
- case DM_DECIMAL:
- default:
- x = [display doubleValue];
- break;
- }
-
- return( x );
- }
-
- - updateMemoryMarker
- {
- if( m == 0.0 )
- [memoryDisplay setStringValue: ""];
- else
- [memoryDisplay setStringValue: "M"];
- return self;
- }
-
- - updateOperationMarker
- {
- const char* operation;
-
- switch( currentOperation )
- {
- case OP_ADD:
- operation = "+";
- break;
- case OP_SUBTRACT:
- operation = "-";
- break;
- case OP_MULTIPLY:
- operation = "x";
- break;
- case OP_DIVIDE:
- operation = "/";
- break;
- case OP_LOGICAL_OR:
- operation = "OR";
- break;
- case OP_LOGICAL_AND:
- operation = "AND";
- break;
- case OP_LOGICAL_EOR:
- operation = "EOR";
- break;
- case OP_LOGICAL_NOR:
- operation = "NOR";
- break;
- case OP_NOOP:
- default:
- operation = " ";
- break;
- }
- [operationDisplay setStringValue: operation];
- return self;
- }
-
-
-
- // Delegate stuff to ensure proper miniaturization behaviour
- - windowWillMiniaturize: sender
- toMiniwindow: mini
- {
- [self setTitle: CALCULATOR_TITLE_SHORT];
- return self;
- }
-
- - windowDidUpdate: sender
- {
- if( calcDidInit != YES)
- [self initCalc];
- [self setMiniwindowIcon: CALCULATOR_ICON];
- [self makeFirstResponder: self];
- return self;
- }
-
- - windowDidDeminiaturize: sender
- {
- [self setTitle: CALCULATOR_TITLE_LONG];
- [self makeFirstResponder: self];
- return self;
- }
-
-
- - processPrevious
- {
-
-
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
-
- if( currentOperation != OP_NOOP)
- {
- [self getDisplay];
-
- [self setScrollOperation: OperatorDesc[currentOperation]
- andNumber: x];
-
- y = magic(y, currentOperation, x);
- if( fpError )
- {
- [self errorDisplay];
- return nil;
- }
-
- x = y;
- [self setDisplay];
- x_isNew = YES;
-
- }
- else
- {
- [self getDisplay];
- y = x;
- x_isNew = YES;
- }
-
- [self setScrollOperation: "=" andNumber: x];
-
- return self;
- }
-
- - copy: sender
- {
- static char pasteBuffer[80];
- const char *types[] = {NXAsciiPboardType,NULL};
- id pasteBoard;
-
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- strcpy( pasteBuffer, [display stringValue]);
-
- pasteBoard = [Pasteboard newName: NXGeneralPboard];
-
- [pasteBoard declareTypes: types
- num: 1
- owner: NULL];
-
- [pasteBoard writeType: NXAsciiPboardType
- data: pasteBuffer
- length: (int) strlen(pasteBuffer)];
-
- #ifdef DEBUG
- fprintf(stderr, "Copied <%s> to the Pasteboard.\n",
- pasteBuffer);
- #endif
-
- fprintf(stderr, "Copy!\n");
- return self;
- }
-
- - paste: sender
- {
- static char *bufPt;
- int len;
- int tmp;
- const NXAtom *types;
- id pasteBoard;
-
- if( fpError)
- {
- NXBeep();
- return self;
- }
-
- pasteBoard = [Pasteboard newName: NXGeneralPboard];
-
- types = [pasteBoard types];
- for( tmp = 0; types[tmp] != NULL; tmp ++ )
- if( strcmp( types[tmp], NXAsciiPboardType ) == 0 )
- {
- [pasteBoard readType: NXAsciiPboardType
- data: &bufPt
- length: &len];
-
- [display setStringValue: bufPt];
- [self getDisplay];
- [self setDisplay];
- [self setScrollOperation: "Paste: " andNumber: x];
-
- #ifdef DEBUG
- fprintf(stderr, "Pasted <%s> (%d) from "
- "the Pasteboard.\n", bufPt,
- len);
- #endif
-
- break;
- }
- return self;
- }
-
- - setScrollOperation: (char *) op andNumber: (double) val
- {
- static char buffer[30], buf2[30];
-
- switch (displayMode )
- {
- case DM_BINARY:
- writeBinary( buf2, val);
- sprintf(buffer, "\n%s %s", op, buf2);
- break;
-
- case DM_OCTAL:
- sprintf( buffer, "\n%s %o",
- op, (unsigned int) val);
- break;
- case DM_HEXADECIMAL:
- sprintf( buffer, "\n%s %x",
- op, (unsigned int) val);
- break;
- case DM_DECIMAL:
- default:
- sprintf( buffer, "\n%s %g",
- op, val);
- break;
- }
-
- [scrollText setSel: [scrollText textLength]
- : [scrollText textLength]];
- [scrollText replaceSel: buffer];
- [scrollText hideCaret];
- [scrollText scrollSelToVisible];
- [self makeFirstResponder: self];
-
- return self;
- }
-
-
- - windowWillResize: (id) sender toSize: (NXSize *) size
- {
- #ifdef DEBUG
- fprintf(stderr,"Window would have resized to %f x %f.\n",
- size->width, size->height);
- #endif
-
- if( size->width < MIN_WIDTH)
- size->width = MIN_WIDTH;
- if( size->height < MIN_HEIGHT)
- size->height = MIN_HEIGHT;
-
- #ifdef DEBUG
- fprintf(stderr,"Window will resize to %f x %f.\n",
- size->width, size->height);
- #endif
- return self;
- }
-
- - (BOOL) acceptsFirstResponder { return YES; }
-
- - printPSCode: sender { [scrollText printPSCode: sender]; return self;}
-
- // From CalculatorLab/MinusPanel.m
- // When the user presses the minus sign on the keypad, the resulting character
- // is number 45 from the Symbol character set, not a minus sign from the ASCII
- // character set (which you would get by pressing the key to the right of the
- // zero key). The Panel class ignores commandKeys from all sets except the
- // ASCII set, but we want the user to be able to depress the minus key on the
- // keypad and have it act like the real minus key. We can get around this
- // problem by checking for this special minus sign and converting it into an
- // ASCII minus sign before it reaches the regular commandKey: method
- - (BOOL)commandKey:(NXEvent *)theEvent
- {
- NXEvent localEvent;
- BOOL symbolSet, minusSign;
-
- symbolSet = theEvent->data.key.charSet == NX_SYMBOLSET;
- minusSign = theEvent->data.key.charCode == 45;
-
- if (symbolSet && minusSign) { /* check for minus */
- localEvent = *theEvent;
- localEvent.data.key.charSet = NX_ASCIISET;
- localEvent.data.key.charCode = '-';
- return [super commandKey:&localEvent];
- } else {
- return [super commandKey:theEvent];
- }
- }
-
-
- @end
-