This is Calculator.m in view mode; [Download] [Up]
// // 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.