Archived
1

Added Lots of Documentation

Added some nice to haves
Improved and Unified General Code Layout
This commit is contained in:
Kim Wittenburg
2015-01-04 02:54:27 +01:00
parent 152b981e24
commit 7438fd1f95
83 changed files with 2282 additions and 416 deletions

View File

@@ -9,15 +9,63 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPElementaryFunction</code> class.
*/
@class MPElementaryFunctionTerm; @class MPElementaryFunctionTerm;
/*!
@class MPElementaryFunctionTerm
@abstract A <code>MPElementaryFunction</code> implements various
mathematical functions that have a valid text representation.
*/
@interface MPElementaryFunctionTerm : MPTerm @interface MPElementaryFunctionTerm : MPTerm
/*!
@method initWithFunctionIdentifier:term:
@abstract Initializes a function term using the specified textual
representation of the function and a term as its value.
@param function
The textual representation of the function to use. Valid values
are <code>"sin"</code>, <code>"cos"</code>, <code>"tan"</code>,
<code>"asin"</code>, <code>"arcsin"</code>, <code>"acos"</code>,
<code>"arccos"</code>, <code>"atan"</code>,
<code>"arctan"</code>, <code>"log"</code>, <code>"lg"</code> and
<code>"ln"</code>.
@param term
The term that should be evaluated using <code>function</code>.
@return A new <code>MPElementaryFunctionTerm</code> instance.
*/
- (instancetype)initWithFunctionIdentifier:(NSString *)function - (instancetype)initWithFunctionIdentifier:(NSString *)function
term:(MPTerm *)term; /* designated initializer */ term:(MPTerm *)term; /* designated initializer */
/*!
@property functionIdentifier
@abstract The identifier of the receiver.
@discussion The identifier of a function is the string of letters it is
mathematically defined of (e.g. <code>"sin"</code> for the sine
function).
*/
@property (readonly, nonatomic, copy) NSString *functionIdentifier;
/*!
@property term
@abstract The receiver's term.
@discussion Depending on the actual function the value of the
<code>term</code> may be converted from radians into degrees.
*/
@property (readonly, nonatomic, strong) MPTerm *term; @property (readonly, nonatomic, strong) MPTerm *term;
@end @end

View File

@@ -9,13 +9,10 @@
#import "MPElementaryFunctionTerm.h" #import "MPElementaryFunctionTerm.h"
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
#import "MPPowerFunction.h"
#import "MPProductTerm.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPMathRules.h" #import "MPMathRules.h"
@implementation MPElementaryFunctionTerm { @implementation MPElementaryFunctionTerm {
NSDecimalNumber *(^_function)(NSDecimalNumber *); NSDecimalNumber *(^_function)(NSDecimalNumber *);
} }
@@ -53,16 +50,18 @@
} else if ([function isEqualToString:@"ln"]) { } else if ([function isEqualToString:@"ln"]) {
func = &log; func = &log;
} else { } else {
NSAssert(true, @"function must be one of (sin, cos, tan, asin, acos, atan, lg, log, ln)."); NSAssert(true, @"function must be one of (sin, cos, tan, asin, arcsin, acos, arccos, atan, arctan, lg, log, ln).");
} }
[self setFunction:func [self setFunction:func
takesArcValue:takesArc takesArcValue:takesArc
returnsArcValue:returnsArc]; returnsArcValue:returnsArc];
_term = term; _term = term;
_functionIdentifier = function.copy;
} }
return self; return self;
} }
- (void)setFunction:(double (*)(double))function - (void)setFunction:(double (*)(double))function
takesArcValue:(BOOL)takesArc takesArcValue:(BOOL)takesArc
returnsArcValue:(BOOL)returnsArc returnsArcValue:(BOOL)returnsArc
@@ -80,6 +79,7 @@
}; };
} }
- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees - (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees
{ {
if ([MPMathRules sharedRules].isUsingDegrees) { if ([MPMathRules sharedRules].isUsingDegrees) {
@@ -90,6 +90,7 @@
} }
} }
- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants - (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants
{ {
if ([MPMathRules sharedRules].isUsingDegrees) { if ([MPMathRules sharedRules].isUsingDegrees) {
@@ -100,13 +101,24 @@
} }
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
NSDecimalNumber *value = [self.term evaluate:error]; NSDecimalNumber *value = [self.term evaluate:error];
if (!value) { if (!value) {
return nil; return nil;
} }
return _function(value); NSDecimalNumber *result = _function(value);
if ([result isEqualToNumber:[NSDecimalNumber notANumber]]) {
result = nil;
if (error) {
NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"%@ is not defined for the value %@.", nil), self.functionIdentifier, [value descriptionWithLocale:[NSLocale currentLocale]]];
*error = [NSError errorWithDomain:MPMathKitErrorDomain
code:102
userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
}
}
return result;
} }
@end @end

View File

@@ -7,21 +7,117 @@
// //
/*!
@header
This file contains the <code>MPEvaluationContext</code> class.
The evaluation context is organized in so called levels. Every level of a
context contains a unique set of variables. Only the highest level is
accessible. Before any lower level can be accessed the higher ones have to be
discarded. A higher level can not define variables that are already defined in
a lower level. Similarly variables can only be undefined if they are defined at
the current level. Discarding (popping) a level always undefines all variables
defined on that level.
*/
@class MPEvaluationContext; @class MPEvaluationContext;
/*!
@class MPEvaluationContext
@abstract The evaluation context maintains a map of variables and
associates them with a certain level.
@discussion Different levels are nested using <code>@link
//apple_ref/occ/instm/MPEvaluationContext/push@/link</code> and
<code>@link
//apple_ref/occ/instm/MPEvaluationContext/pop@/link</code>
messages.
<code>MPEvaluationContext</code> is a singletone class. There can
only exist one instance (the shared instance) at any time.
*/
@interface MPEvaluationContext : NSObject @interface MPEvaluationContext : NSObject
/*!
@method sharedContext
@abstract Returns the shared evaluation context.
*/
+ (MPEvaluationContext *)sharedContext; + (MPEvaluationContext *)sharedContext;
/*!
@method push
@abstract Pushes a new level on context.
*/
- (void)push; - (void)push;
/*!
@method pop
@abstract Pops a level off the context.
@discussion Popping a level also undefines all variables that were defined in
that level.
*/
- (void)pop; - (void)pop;
/*!
@method defineVariable:value:
@abstract Defines a variable with the specified value at the current level.
@discussion The variable can only be defined if it has not been defined
previously in a lower level. If the variable has been defined at
the current level its value will be overwritten to the specified
one.
@param variable
The name of the variable. Typically this is a one-letter string.
@param value
The value of the variable.
@return <code>YES</code> if the variable was defined successfully,
<code>NO</code> if it is already defined in a lower level.
*/
- (BOOL)defineVariable:(NSString *)variable - (BOOL)defineVariable:(NSString *)variable
value:(NSDecimalNumber *)value; value:(NSDecimalNumber *)value;
/*!
@method undefineVariable:
@abstract Undefines a variable that has previously been defined.
@discussion Variables can only be undefined if they have been defined at the
same level. If the variable to be undefined was defined at a
different level this method does nothing.
Normally there is very little reason to call this method since
all variables defined on one level are automatically undefined
when the level is popped.
@param variable
The variable to be undefined.
*/
- (void)undefineVariable:(NSString *)variable; - (void)undefineVariable:(NSString *)variable;
/*!
@method valueForVariable:
@abstract Returns the value for the specified variable.
@discussion If the <code>variable</code> has not been defined previously this
method returns <code>nil</code>.
@param variable
The variable whose value is to be retrieved.
@return The value <code>variable</code> was defined with or
<code>nil</code> if it was not defined.
*/
- (NSDecimalNumber *)valueForVariable:(NSString *)variable; - (NSDecimalNumber *)valueForVariable:(NSString *)variable;
@end @end

View File

@@ -8,13 +8,21 @@
#import "MPEvaluationContext.h" #import "MPEvaluationContext.h"
@interface MPEvaluationContext () @interface MPEvaluationContext ()
@property (nonatomic, strong) NSMutableArray *stack; @property (nonatomic, strong) NSMutableArray *stack;
@end @end
@implementation MPEvaluationContext @implementation MPEvaluationContext
static MPEvaluationContext *sharedContext; static MPEvaluationContext *sharedContext;
+ (MPEvaluationContext *)sharedContext + (MPEvaluationContext *)sharedContext
{ {
if (!sharedContext) { if (!sharedContext) {
@@ -23,6 +31,7 @@ static MPEvaluationContext *sharedContext;
return sharedContext; return sharedContext;
} }
- (instancetype)init - (instancetype)init
{ {
if (sharedContext) { if (sharedContext) {
@@ -40,16 +49,19 @@ static MPEvaluationContext *sharedContext;
return self; return self;
} }
- (void)push - (void)push
{ {
[self.stack addObject:[[NSMutableDictionary alloc] init]]; [self.stack addObject:[[NSMutableDictionary alloc] init]];
} }
- (void)pop - (void)pop
{ {
[self.stack removeLastObject]; [self.stack removeLastObject];
} }
- (BOOL)defineVariable:(NSString *)variable - (BOOL)defineVariable:(NSString *)variable
value:(NSDecimalNumber *)value value:(NSDecimalNumber *)value
{ {
@@ -62,17 +74,20 @@ static MPEvaluationContext *sharedContext;
return YES; return YES;
} }
- (void)undefineVariable:(NSString *)variable - (void)undefineVariable:(NSString *)variable
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSMutableDictionary *currentBindings = self.stack.lastObject;
[currentBindings removeObjectForKey:variable]; [currentBindings removeObjectForKey:variable];
} }
- (BOOL)isVariableDefined:(NSString *)variable - (BOOL)isVariableDefined:(NSString *)variable
{ {
return [self valueForVariable:variable] != nil; return [self valueForVariable:variable] != nil;
} }
- (NSDecimalNumber *)valueForVariable:(NSString *)variable - (NSDecimalNumber *)valueForVariable:(NSString *)variable
{ {
NSUInteger currentIndex = self.stack.count; NSUInteger currentIndex = self.stack.count;

View File

@@ -10,6 +10,9 @@
/*! /*!
@header @header
This file contains the <code>MPExpression</code> class and the
<code>MPExpressionElement</code> protocol.
The <code>MPExpression</code> class is used to represent a mathematical The <code>MPExpression</code> class is used to represent a mathematical
expression. It is used in the storage layer of the MathKit Expression System. An expression. It is used in the storage layer of the MathKit Expression System. An
instance of the <code>MPExpression</code> class stores all contents related to instance of the <code>MPExpression</code> class stores all contents related to
@@ -108,6 +111,58 @@
*/ */
/*!
@const MPIllegalElementException
@abstract Name for an exception that is raised if an invalid element is
added to an expression.
@discussion This exception may be raised during initialization of an
expression or when the expression is mutated. This exception
contains the invalid element in its <code>userInfo</code>
dictionary. You can query it using the
<code>MPIllegalElementExceptionElementKey</code> key.
*/
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
/*!
@const MPIllegalElementExceptionElementKey
@abstract Predefined key for an invalid element that caused a
<code>MPIllegalElementException</code> to be raised.
@discussion The invalid element can be of any type. Numbers and structs are
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
instance respectively.
*/
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
/*!
@typedef MPReferenceFrame
@abstract A reference frame specifies the way an <i>item</i> is to be
interpreted.
@constant MPElementReferenceFrame
Specifies that items should be interpreted as elements.
@constant MPSymbolReferenceFrame
Specifies that items should be interpreted as symbols.
@constant MPTokenReferenceFrame
Specifies that items should be interpreted as tokens.
*/
typedef enum {
MPElementReferenceFrame,
MPSymbolReferenceFrame,
MPTokenReferenceFrame
} MPReferenceFrame;
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
@protocol MPExpressionElement, MPToken;
/*! /*!
@protocol MPExpressionElement @protocol MPExpressionElement
@abstract This protocol defines the functionality an element in an @abstract This protocol defines the functionality an element in an
@@ -161,58 +216,6 @@
/*!
@const MPIllegalElementException
@abstract Name for an exception that is raised if an invalid element is
added to an expression.
@discussion This exception may be raised during initialization of an
expression or when the expression is mutated. This exception
contains the invalid element in its <code>userInfo</code>
dictionary. You can query it using the
<code>MPIllegalElementExceptionElementKey</code> key.
*/
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
/*!
@const MPIllegalElementExceptionElementKey
@abstract Predefined key for an invalid element that caused a
<code>MPIllegalElementException</code> to be raised.
@discussion The invalid element can be of any type. Numbers and structs are
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
instance respectively.
*/
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
/*!
@typedef MPReferenceFrame
@abstract A reference frame specifies the way an <i>item</i> is to be
interpreted.
@constant MPElementReferenceFrame
Specifies that items should be interpreted as elements.
@constant MPSymbolReferenceFrame
Specifies that items should be interpreted as symbols.
@constant MPTokenReferenceFrame
Specifies that items should be interpreted as tokens.
*/
typedef enum {
MPElementReferenceFrame,
MPSymbolReferenceFrame,
MPTokenReferenceFrame
} MPReferenceFrame;
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
@protocol MPExpressionElement, MPToken;
/*! /*!
@class MPExpression @class MPExpression
@abstract A <code>MPExpression</code> instance represents a mathematical @abstract A <code>MPExpression</code> instance represents a mathematical

View File

@@ -21,11 +21,11 @@
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
NSString *const MPIllegalElementException = @"Illegal Element Exception"; NSString *const MPIllegalElementException = @"Illegal Element Exception";
NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey"; NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey";
@interface MPExpression () { @interface MPExpression () {
NSMutableArray * _elements; NSMutableArray * _elements;
} }
@@ -299,7 +299,6 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
} }
#warning If multiple equal expressions exist errors may occur...
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element - (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
{ {
return [_elements indexOfObject:element]; return [_elements indexOfObject:element];
@@ -656,7 +655,7 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
- (NSString *)description - (NSString *)description
{ {
#warning Bad Implementation #warning Incomplete Implementation
NSMutableString *description = [[NSMutableString alloc] init]; NSMutableString *description = [[NSMutableString alloc] init];
NSUInteger index = 0; NSUInteger index = 0;
for (id element in _elements) { for (id element in _elements) {

View File

@@ -9,14 +9,53 @@
#import "MPLayout.h" #import "MPLayout.h"
/*!
@header
This file contains the <code>MPExpressionLayout</code> class.
*/
@class MPExpressionLayout, MPExpression, MPFunctionLayout; @class MPExpressionLayout, MPExpression, MPFunctionLayout;
/*!
@class MPExpressionLayout
@abstract An expression layout draws an <code>@link
//apple_ref/occ/cl/MPExpression@/link</code>.
@discussion For more information on the layout system see the documentation
on the <code>@link //apple_ref/occ/cl/MPLayout@/link</code>
class.
*/
@interface MPExpressionLayout : MPLayout @interface MPExpressionLayout : MPLayout
/*!
@method initWithExpression:parent:
@abstract Initializes an expression layout with the specified expression
and parent layout.
@discussion To initialize a layout for the root expression of an expression
tree specify <code>nil</code> as the <code>parent</code>.
@param expression
The expression that sould be represented by the receiver. Must
not be <code>ni</code>.
@param parent
The parent layout of the receiver. Specify <code>nil</code> to
initalize a root expression layout.
@return A newly initialized expression layout.
*/
- (instancetype)initWithExpression:(MPExpression *)expression - (instancetype)initWithExpression:(MPExpression *)expression
parent:(MPFunctionLayout *)parent; parent:(MPFunctionLayout *)parent;
/*!
@property expression
@abstract The expression represented by the receiver.
*/
@property (readonly, nonatomic, weak) MPExpression *expression; @property (readonly, nonatomic, weak) MPExpression *expression;
@end @end

View File

@@ -9,21 +9,23 @@
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPExpression.h" #import "MPExpression.h"
#import "MPExpression.h" #import "MPFunction.h"
#import "MPPowerFunction.h"
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPPowerFunctionLayout.h" #import "MPPowerFunctionLayout.h"
#import "MPToken.h" #import "MPToken.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@interface MPExpressionLayout (MPLineGeneration) @interface MPExpressionLayout (MPLineGeneration)
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index; - (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
@end @end
@implementation MPExpressionLayout (MPLineGeneration) @implementation MPExpressionLayout (MPLineGeneration)
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index - (CTLineRef)lineForElementAtIndex:(NSUInteger)index
@@ -60,9 +62,13 @@
@end @end
@implementation MPExpressionLayout @implementation MPExpressionLayout
# pragma mark Creation Methods # pragma mark Creation Methods
- (instancetype)initWithExpression:(MPExpression *)expression - (instancetype)initWithExpression:(MPExpression *)expression
parent:(MPFunctionLayout *)parent parent:(MPFunctionLayout *)parent
{ {
@@ -73,12 +79,16 @@
return self; return self;
} }
#pragma mark Cache Methods #pragma mark Cache Methods
- (NSUInteger)numberOfChildren - (NSUInteger)numberOfChildren
{ {
return self.expression.countElements; return self.expression.countElements;
} }
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index - (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{ {
id cachedObject = [self cachableObjectForIndex:index generator:^id{ id cachedObject = [self cachableObjectForIndex:index generator:^id{
@@ -95,6 +105,7 @@
return nil; return nil;
} }
- (NSRect)boundsOfElementAtIndex:(NSUInteger)index - (NSRect)boundsOfElementAtIndex:(NSUInteger)index
{ {
id symbol = [self.expression elementAtIndex:index]; id symbol = [self.expression elementAtIndex:index];
@@ -109,7 +120,10 @@
} }
} }
#pragma mark Drawing Methods #pragma mark Drawing Methods
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
if (self.expression.countElements == 0) { if (self.expression.countElements == 0) {
@@ -125,6 +139,7 @@
return NSMakeRect(x, y, width, height); return NSMakeRect(x, y, width, height);
} }
- (NSRect)boundingRectForRange:(NSRange)range - (NSRect)boundingRectForRange:(NSRange)range
{ {
NSUInteger startOffset; NSUInteger startOffset;
@@ -184,6 +199,7 @@
return NSMakeRect(x, self.bounds.origin.y, width, self.bounds.size.height); return NSMakeRect(x, self.bounds.origin.y, width, self.bounds.size.height);
} }
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{ {
CGFloat x = 0; CGFloat x = 0;
@@ -193,6 +209,7 @@
return NSMakePoint(x, 0); return NSMakePoint(x, 0);
} }
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{ {
NSUInteger currentPosition = 0; NSUInteger currentPosition = 0;
@@ -235,11 +252,13 @@
} }
} }
- (BOOL)drawsChildrenManually - (BOOL)drawsChildrenManually
{ {
return YES; return YES;
} }
- (void)draw - (void)draw
{ {
// Get the current context // Get the current context

View File

@@ -9,6 +9,8 @@
/*! /*!
@header @header
This file contains the <code>MPExpressionParser</code> class.
The <code>MPExpressionParser</code> converts a <code>@link The <code>MPExpressionParser</code> converts a <code>@link
MPExpression@/link</code> instance into a <code>@link MPExpression@/link</code> instance into a <code>@link
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance. The rules that are //apple_ref/occ/cl/MPParsedExpression@/link</code> instance. The rules that are

View File

@@ -8,25 +8,27 @@
#import "MPExpressionParser.h" #import "MPExpressionParser.h"
#import "MPExpression.h"
#import "MPTerm.h"
#import "MPToken.h" #import "MPToken.h"
#import "MPExpression.h"
#import "MPParsedExpression.h"
#import "MPTerm.h"
#import "MPSumTerm.h" #import "MPSumTerm.h"
#import "MPProductTerm.h" #import "MPProductTerm.h"
#import "MPFactorialTerm.h"
#import "MPElementaryFunctionTerm.h"
#import "MPFunctionTerm.h"
#import "MPPowerTerm.h"
#import "MPParsedExpression.h"
#import "MPNegatedTerm.h" #import "MPNegatedTerm.h"
#import "MPFunctionTerm.h"
#import "MPElementaryFunctionTerm.h"
#import "MPNumber.h" #import "MPNumber.h"
#import "MPVariable.h" #import "MPVariable.h"
#import "MPFactorialTerm.h" #import "MPFactorialTerm.h"
#import "MPPowerTerm.h"
#define success() state = 0 #define success() state = 0
#define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1 #define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
@interface MPExpressionParser () @interface MPExpressionParser ()
@property (nonatomic, strong) NSArray *tokens; @property (nonatomic, strong) NSArray *tokens;
@@ -46,6 +48,8 @@
@end @end
@implementation MPExpressionParser @implementation MPExpressionParser
- (instancetype)initWithExpression:(MPExpression *)expression - (instancetype)initWithExpression:(MPExpression *)expression
@@ -58,12 +62,14 @@
return self; return self;
} }
- (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors - (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors
{ {
return [self parseExpectingVariableDefinition:NO return [self parseExpectingVariableDefinition:NO
errors:errors]; errors:errors];
} }
- (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag - (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag
errors:(NSArray *__autoreleasing *)errors errors:(NSArray *__autoreleasing *)errors
{ {
@@ -120,6 +126,7 @@
return result; return result;
} }
- (void)runParserMachine - (void)runParserMachine
{ {
NSInteger state = 1; NSInteger state = 1;
@@ -251,6 +258,7 @@
} }
} }
- (void)skipWhitespaces - (void)skipWhitespaces
{ {
while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) { while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) {
@@ -258,6 +266,7 @@
} }
} }
- (id<MPToken>)currentToken - (id<MPToken>)currentToken
{ {
if (self.currentTokenIndex >= self.tokens.count) { if (self.currentTokenIndex >= self.tokens.count) {
@@ -266,11 +275,13 @@
return self.tokens[self.currentTokenIndex]; return self.tokens[self.currentTokenIndex];
} }
- (void)nextToken - (void)nextToken
{ {
self.currentTokenIndex++; self.currentTokenIndex++;
} }
- (NSMutableArray *)errors - (NSMutableArray *)errors
{ {
if (!_errors) { if (!_errors) {
@@ -279,6 +290,7 @@
return _errors; return _errors;
} }
- (NSMutableArray *)summands - (NSMutableArray *)summands
{ {
if (!_summands) { if (!_summands) {
@@ -287,6 +299,7 @@
return _summands; return _summands;
} }
- (NSMutableArray *)factors - (NSMutableArray *)factors
{ {
if (!_factors) { if (!_factors) {
@@ -295,6 +308,7 @@
return _factors; return _factors;
} }
- (NSMutableArray *)functionStack - (NSMutableArray *)functionStack
{ {
if (!_functionStack) { if (!_functionStack) {
@@ -303,6 +317,7 @@
return _functionStack; return _functionStack;
} }
- (NSMutableArray *)valueGroup - (NSMutableArray *)valueGroup
{ {
if (!_valueGroup) { if (!_valueGroup) {
@@ -311,6 +326,7 @@
return _valueGroup; return _valueGroup;
} }
- (BOOL)parseOperatorList:(id<MPToken>)token // Returns YES if list is overall negative - (BOOL)parseOperatorList:(id<MPToken>)token // Returns YES if list is overall negative
{ {
NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""] NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]
@@ -318,17 +334,20 @@
return (operatorString.length & 1) == 1; return (operatorString.length & 1) == 1;
} }
- (NSDecimalNumber *)parseNumber:(id<MPToken>)token - (NSDecimalNumber *)parseNumber:(id<MPToken>)token
{ {
return [NSDecimalNumber decimalNumberWithString:token.stringValue return [NSDecimalNumber decimalNumberWithString:token.stringValue
locale:[NSLocale currentLocale]]; locale:[NSLocale currentLocale]];
} }
- (NSString *)parseVariable:(id<MPToken>)token - (NSString *)parseVariable:(id<MPToken>)token
{ {
return token.stringValue; return token.stringValue;
} }
- (void)collapseSummand - (void)collapseSummand
{ {
if (self.factors.count > 0) { if (self.factors.count > 0) {
@@ -342,6 +361,7 @@
self.summandNegative = NO; self.summandNegative = NO;
} }
- (void)collapseFactor - (void)collapseFactor
{ {
if (self.valueGroup.count > 0) { if (self.valueGroup.count > 0) {
@@ -360,6 +380,7 @@
self.factorNegative = NO; self.factorNegative = NO;
} }
- (void)runSuffixMachine - (void)runSuffixMachine
{ {
BOOL checkMore = YES; BOOL checkMore = YES;
@@ -388,6 +409,7 @@
} }
} }
- (void)addErrorWithCode:(NSInteger)code - (void)addErrorWithCode:(NSInteger)code
localizedDescription:(NSString *)description localizedDescription:(NSString *)description
useRange:(BOOL)flag useRange:(BOOL)flag
@@ -408,6 +430,7 @@
MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]]; MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
} }
- (BOOL)errorOccured - (BOOL)errorOccured
{ {
[self skipWhitespaces]; [self skipWhitespaces];

View File

@@ -9,13 +9,51 @@
#import "MPExpression.h" #import "MPExpression.h"
/*!
@header
This file contains the <code>MPExpressionStorage</code> class.
*/
@class MPExpressionStorage, MPExpressionView, MPExpressionLayout; @class MPExpressionStorage, MPExpressionView, MPExpressionLayout;
/*!
@class MPExpressionStorage
@abstract An expression storage manages the contents of an expression view
and notifies it when the underlying expression changes.
*/
@interface MPExpressionStorage : MPExpression @interface MPExpressionStorage : MPExpression
@property (nonatomic, weak) MPExpressionView *expressionView; // Do not set /*!
@property expressionView
@abstract The receiver's expression view.
@discussion The expression view is the view that displays the contents of the
expression storage. Normally you should not call the setter of
this property. It is automatically assigned when the <code>@link
//apple_ref/occ/instp/MPExpressionView/expressionStorage@/link</code>
property of the <code>@link
//apple_ref/occ/cl/MPExpressionView@/link</code> class is set.
When this property is set the receiver assumes that the
displaying expression view has changed. It then adapts to the
properties of the new expression view and discards the root
layout.
*/
@property (nonatomic, weak) MPExpressionView *expressionView;
/*!
@property rootLayout
@abstract The receiver's root layout.
@discussion The root layout is the root node of the layout tree that draws
the receiver's contents. For more information see the
documentation on the <code>@link
//apple_ref/occ/cl/MPLayout@/link</code> class.
*/
@property (nonatomic, strong) MPExpressionLayout *rootLayout; @property (nonatomic, strong) MPExpressionLayout *rootLayout;
@end @end

View File

@@ -9,17 +9,22 @@
#import "MPExpressionStorage.h" #import "MPExpressionStorage.h"
#import "MPExpressionView.h" #import "MPExpressionView.h"
#import "MPLayout.h"
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPRangePath.h" #import "MPRangePath.h"
@interface MPExpressionStorage () @interface MPExpressionStorage ()
@property (nonatomic, strong) NSLayoutManager *layoutManager; @property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer; @property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, strong) NSTextStorage *textStorage; @property (nonatomic, strong) NSTextStorage *textStorage;
@end @end
@implementation MPExpressionStorage @implementation MPExpressionStorage
- (instancetype)initWithElements:(NSArray *)elements - (instancetype)initWithElements:(NSArray *)elements
@@ -31,6 +36,7 @@
return self; return self;
} }
- (void)setExpressionView:(MPExpressionView *)expressionView - (void)setExpressionView:(MPExpressionView *)expressionView
{ {
_expressionView = expressionView; _expressionView = expressionView;
@@ -38,6 +44,7 @@
self.rootLayout.flipped = expressionView.flipped; self.rootLayout.flipped = expressionView.flipped;
} }
- (void)changedElementsInRangePath:(MPRangePath *)rangePath - (void)changedElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength replacementLength:(NSUInteger)replacementLength
{ {

View File

@@ -7,6 +7,12 @@
// //
/*!
@header
This file contains the <code>MPExpressionTokenizer</code> class.
*/
@class MPExpressionTokenizer, MPExpression; @class MPExpressionTokenizer, MPExpression;

View File

@@ -52,7 +52,7 @@
@"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times @"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times @"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times
@"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|" @"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|"
@"([A-Za-z])|" @"([A-Za-zπ])|"
@"(!)|" @"(!)|"
@"(=)|" @"(=)|"
@"(\\s+)" @"(\\s+)"

View File

@@ -6,68 +6,195 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
// TODO: Undo/Redo + Delegate (evaluateExpressionView:evaluate...)
/*!
@header
This file contains the <code>MPExpressionView</code> class.
<h2>The MathKit Expression System</h2>
MathKit contains class es that make up the so called <i>expression system</i>.
The expression system is divided into three layers: the model, the view and the
controller layer. The <code>MPExpressionView</code> class represents the view
layer and interacts with the Cocoa <code>NSView</code> system. The model is
represented by the classes <code>@link
//apple_ref/occ/cl/MPExpression@/link</code> and <code>@link
//apple_ref/occ/cl/MPFunction@/link</code>. The controller layer between the two
consists of the class <code>@link //apple_ref/occ/cl/MPLayout@/link</code> and
its subclasses. The purpose of the expression system is presenting expressions
to the user and offering possibilities to manipulate and work with expressions.
*/
@class MPExpressionView, MPExpressionStorage, MPFunction, MPRangePath; @class MPExpressionView, MPExpressionStorage, MPFunction, MPRangePath;
//@protocol MPExpressionViewDelegate;
/*!
@class MPExpressionView
@abstract The <code>MPExpressionView</code> class is the front-end class of
the MathKit expression system.
@discussion The class draws the expression managed by the the back-end class
<code>@link //apple_ref/occ/cl/MPExpressionStorage@/link</code>
and is the interface between Application Kit's view system and
the MathKit expression system.
*/
@interface MPExpressionView : NSView <NSUserInterfaceValidations> @interface MPExpressionView : NSView <NSUserInterfaceValidations>
#pragma mark Creation Methods #pragma mark Creation Methods
/*!
@methodgroup Creation Methods
*/
/*!
@method initWithFrame:
@abstract Initializes a <code>MPExpressionView</code> instance.
@discussion This method sets up all layers of the expression system including
an empty expression storage.
This method is the designated initializer of the
<code>MPExpressionView</code> class.
@param frameRect
The frame rectangle for the created view.
@return An initialized <code>MPExpressionView</code> or <code>nil</code>
if the object could not be created.
*/
- (id)initWithFrame:(NSRect)frameRect; - (id)initWithFrame:(NSRect)frameRect;
#pragma mark Properties #pragma mark Properties
/*!
@methodgroup Properties
*/
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
//@property (nonatomic, weak) id<MPExpressionViewDelegate> delegate; /*!
@property expressionStorage
@abstract The receiver's expression storage.
@discussion The expression storage maintains the contents of the receiver.
User interactions change the underlying data. If the expression
storage or the underlying expression change the expression
storage updates the receiver.
*/
@property (nonatomic, strong) MPExpressionStorage *expressionStorage;
/*!
@property selection
@abstract The receiver's selection.
*/
@property (nonatomic, strong) MPRangePath *selection; @property (nonatomic, strong) MPRangePath *selection;
/*!
@property mathError
@abstract If set the receiver will display the localized description of the
specified error.
@discussion There can only be one math error at a time. This property should
not be set simutaneously with the <code>@link
//apple_ref/occ/instp/MPExpressionView/syntaxErrors@/link</code>.
*/
@property (nonatomic, strong) NSError *mathError; @property (nonatomic, strong) NSError *mathError;
/*!
@property syntaxErrors
@abstract If set the receiver will display a dropdown list of syntax
errors.
@discussion The array must only contain <code>NSError</code> instances. In
addition to that every instance must contain a valid
<code>NSIndexPath</code> for the <code>@link
//apple_ref/c/data/MPPathToExpressionKey@/link</code> in its
<code>userDict</code> acompanied by a <code>NSRange</code>
wrapped in a <code>NSValue</code> instance for the <code>@link
//apple_ref/c/data/MPErrorRangeKey@/link</code>.
If the user selects an item from the dropdown list the part of
the expression displayed by the receiver will be selected that is
specified by the values in the corresponding <code>NSError</code>
instance.
*/
@property (nonatomic, strong) NSArray *syntaxErrors; @property (nonatomic, strong) NSArray *syntaxErrors;
/*!
@property target
@abstract The target object to receive action messages from the receiver.
*/
@property (nonatomic, weak) id target; @property (nonatomic, weak) id target;
/*!
@property action
@abstract The receiver's action method to the specified selector.
@discussion The action method is invoked when the user presses <i>enter</i>
in the receiver. Typically this is understood as evaluation
request.
*/
@property (nonatomic) SEL action; @property (nonatomic) SEL action;
//@property (nonatomic) BOOL editable;
//@property (nonatomic) BOOL selectable;
#pragma mark Actions #pragma mark Actions
// Radians - Degrees /*!
@methodgroup Actions
*/
/*!
@method switchToRadians:
@abstract Tells the receiver that it should switch the interpretation of
trigonometric function values to radians.
@param sender
Typically the object that invoked the method.
*/
- (IBAction)switchToRadians:(id)sender; - (IBAction)switchToRadians:(id)sender;
/*!
@method switchToDegrees:
@abstract Tells the receiver that it should switch the interpretation of
trigonometric function values to degrees.
@param sender
Typically the object that invoked the method.
*/
- (IBAction)switchToDegrees:(id)sender; - (IBAction)switchToDegrees:(id)sender;
/*!
@method switchRadiansDegrees:
@abstract Tells the receiver that it should switch the interpretation of
trigonometric function values.
@discussion If the interpretation is currently degrees it is changed to
radians or vice versa.
@param sender
Typically the object that invoked the method.
*/
- (IBAction)switchRadiansDegrees:(id)sender; - (IBAction)switchRadiansDegrees:(id)sender;
// Functions
/*!
@method toggleFunctionsPopover:
@abstract Tells the receiver to toggle the visibility of the functions
popover.
@discussion From the functions popover the user can select and insert
functions into the displayed expression
@param sender
Typically the object that invoked the method.
*/
- (IBAction)toggleFunctionsPopover:(id)sender; - (IBAction)toggleFunctionsPopover:(id)sender;
@end @end
//@protocol MPExpressionViewDelegate <NSObject>
//@optional
//
//#pragma mark Editing
//- (MPRangePath *)expressionView:(MPExpressionView *)expressionView willChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
//- (void)expressionView:(MPExpressionView *)expressionView didChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
//
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldChangeSymbolsInRangePath:(MPRangePath *)rangePath replacementElements:(NSArray *)replacement;
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldInsertFunction:(MPFunction *)function replacingRangePath:(MPRangePath *)currentSelection;
//- (void)expressionViewDidChange:(MPExpressionView *)expressionView;
//
//- (BOOL)expressionViewShouldBeginEditing:(MPExpressionView *)expressionView;
//- (BOOL)expressionViewShouldEndEditing:(MPExpressionView *)expressionView;
//- (void)expressionViewDidBeginEditing:(MPExpressionView *)expressionView;
//- (void)expressionDidEndEditing:(MPExpressionView *)expressionView;
//
//// TODO: Errors...
//#pragma mark Evaluation
//
//- (BOOL)expressionViewShouldEvaluate:(MPExpressionView *)expressionView;
//- (void)expressionViewDidEvaluate:(MPExpressionView *)expressionView;
//
//@end

View File

@@ -8,26 +8,25 @@
#import "MPExpressionView.h" #import "MPExpressionView.h"
#import "MPExpression.h"
#import "MPExpression.h"
#import "MPParsedExpression.h"
#import "MPPowerFunction.h"
#import "MPParenthesisFunction.h"
#import "MPFractionFunction.h"
#import "MPToken.h"
#import "MPRangePath.h"
#import "MPMathRules.h"
#import "MPExpressionStorage.h" #import "MPExpressionStorage.h"
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPFunctionLayout.h"
#import "MPFunctionsViewController.h" #import "MPFunctionsViewController.h"
#import "MPFunctionLayout.h"
#import "MPFractionFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
#import "MPParsedExpression.h"
#import "MPMathRules.h"
#import "MPToken.h"
#import "MPRangePath.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@interface MPExpressionView () @interface MPExpressionView ()
@property (nonatomic, strong) NSButton *functionsButton; @property (nonatomic, strong) NSButton *functionsButton;
@@ -58,6 +57,7 @@
@interface MPExpressionView (MPSelectionHelper) @interface MPExpressionView (MPSelectionHelper)
- (void)restartCaretTimer; - (void)restartCaretTimer;
- (void)updateCaret:(NSTimer *)timer; - (void)updateCaret:(NSTimer *)timer;
@@ -101,6 +101,7 @@
[self updateCaret:self.caretTimer]; [self updateCaret:self.caretTimer];
} }
- (void)updateCaret:(NSTimer *)timer - (void)updateCaret:(NSTimer *)timer
{ {
self.caretVisible = !self.caretVisible; self.caretVisible = !self.caretVisible;
@@ -111,6 +112,7 @@
[self setNeedsDisplayInRect:updatedRect]; [self setNeedsDisplayInRect:updatedRect];
} }
- (NSRect)selectionRect - (NSRect)selectionRect
{ {
NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection]; NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
@@ -120,6 +122,7 @@
return selectionRect; return selectionRect;
} }
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath - (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath
byExtendingSelection:(BOOL)extendingSelection byExtendingSelection:(BOOL)extendingSelection
selectWords:(BOOL)selectWords selectWords:(BOOL)selectWords
@@ -186,6 +189,7 @@
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget]; return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
} }
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath - (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath
byExtendingSelection:(BOOL)extendingSelection byExtendingSelection:(BOOL)extendingSelection
selectWords:(BOOL)selectWords selectWords:(BOOL)selectWords
@@ -257,6 +261,7 @@
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget]; return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
} }
- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath - (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath
newSelectionPath:(NSIndexPath *)newSelectionPath newSelectionPath:(NSIndexPath *)newSelectionPath
{ {
@@ -312,9 +317,13 @@
#pragma mark - #pragma mark -
@implementation MPExpressionView @implementation MPExpressionView
#pragma mark Creation Methods #pragma mark Creation Methods
- (id)initWithFrame:(NSRect)frame - (id)initWithFrame:(NSRect)frame
{ {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
@@ -324,6 +333,7 @@
return self; return self;
} }
- (id)initWithCoder:(NSCoder *)aDecoder - (id)initWithCoder:(NSCoder *)aDecoder
{ {
self = [super initWithCoder:aDecoder]; self = [super initWithCoder:aDecoder];
@@ -333,6 +343,7 @@
return self; return self;
} }
- (void)initializeExpressionView - (void)initializeExpressionView
{ {
// Setup the Expression Storage // Setup the Expression Storage
@@ -348,6 +359,7 @@
[self restartCaretTimer]; [self restartCaretTimer];
} }
- (void)initializeButtons - (void)initializeButtons
{ {
NSButton *functionsButton = self.functionsButton; NSButton *functionsButton = self.functionsButton;
@@ -368,6 +380,7 @@
constant:0]]; constant:0]];
} }
- (void)initializeErrorsViews - (void)initializeErrorsViews
{ {
@@ -394,34 +407,42 @@
views:variableBindings]]; views:variableBindings]];
} }
#pragma mark - NSView Configuration #pragma mark - NSView Configuration
- (BOOL)acceptsFirstResponder - (BOOL)acceptsFirstResponder
{ {
return YES; return YES;
} }
- (BOOL)canBecomeKeyView - (BOOL)canBecomeKeyView
{ {
return YES; return YES;
} }
- (BOOL)isOpaque - (BOOL)isOpaque
{ {
return YES; return YES;
} }
- (void)resetCursorRects - (void)resetCursorRects
{ {
[self addCursorRect:self.bounds [self addCursorRect:self.bounds
cursor:[NSCursor IBeamCursor]]; cursor:[NSCursor IBeamCursor]];
} }
- (BOOL)becomeFirstResponder - (BOOL)becomeFirstResponder
{ {
[self restartCaretTimer]; [self restartCaretTimer];
return [super becomeFirstResponder]; return [super becomeFirstResponder];
} }
- (BOOL)resignFirstResponder - (BOOL)resignFirstResponder
{ {
[self.caretTimer invalidate]; [self.caretTimer invalidate];
@@ -430,12 +451,14 @@
return [super resignFirstResponder]; return [super resignFirstResponder];
} }
- (void)setFrame:(NSRect)frameRect - (void)setFrame:(NSRect)frameRect
{ {
[self setNeedsLayout:YES]; [self setNeedsLayout:YES];
[super setFrame:frameRect]; [super setFrame:frameRect];
} }
- (NSSize)intrinsicContentSize - (NSSize)intrinsicContentSize
{ {
NSSize size = self.expressionStorage.rootLayout.bounds.size; NSSize size = self.expressionStorage.rootLayout.bounds.size;
@@ -443,7 +466,10 @@
return size; return size;
} }
#pragma mark - Properties #pragma mark - Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage - (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{ {
_expressionStorage.expressionView = nil; _expressionStorage.expressionView = nil;
@@ -452,6 +478,7 @@
[self invalidateIntrinsicContentSize]; [self invalidateIntrinsicContentSize];
} }
- (void)setSelection:(MPRangePath *)selection - (void)setSelection:(MPRangePath *)selection
{ {
_selection = selection; _selection = selection;
@@ -459,6 +486,7 @@
self.needsDisplay = YES; self.needsDisplay = YES;
} }
- (void)setSyntaxErrors:(NSArray *)syntaxErrors - (void)setSyntaxErrors:(NSArray *)syntaxErrors
{ {
_syntaxErrors = syntaxErrors; _syntaxErrors = syntaxErrors;
@@ -491,12 +519,14 @@
} }
} }
- (void)setMathError:(NSError *)mathError - (void)setMathError:(NSError *)mathError
{ {
_mathError = mathError; _mathError = mathError;
self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @""; self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @"";
} }
- (NSButton *)functionsButton - (NSButton *)functionsButton
{ {
if (!_functionsButton) { if (!_functionsButton) {
@@ -520,6 +550,7 @@
return _functionsButton; return _functionsButton;
} }
- (NSPopUpButton *)syntaxErrorsPopUpButton - (NSPopUpButton *)syntaxErrorsPopUpButton
{ {
if (!_syntaxErrorsPopUpButton) { if (!_syntaxErrorsPopUpButton) {
@@ -539,6 +570,7 @@
return _syntaxErrorsPopUpButton; return _syntaxErrorsPopUpButton;
} }
- (NSTextField *)mathErrorTextField - (NSTextField *)mathErrorTextField
{ {
if (!_mathErrorTextField) { if (!_mathErrorTextField) {
@@ -555,6 +587,7 @@
return _mathErrorTextField; return _mathErrorTextField;
} }
- (void)didSelectError:(NSPopUpButton *)sender - (void)didSelectError:(NSPopUpButton *)sender
{ {
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1]; NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
@@ -564,7 +597,10 @@
self.selection = MPMakeRangePath(pathToExpression, errorRange.length); self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
} }
#pragma mark - Mouse Event Handling #pragma mark - Mouse Event Handling
- (void)mouseDown:(NSEvent *)theEvent - (void)mouseDown:(NSEvent *)theEvent
{ {
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -577,6 +613,7 @@
self.selection = MPMakeRangePath(selectionPath, 0); self.selection = MPMakeRangePath(selectionPath, 0);
} }
- (void)mouseDragged:(NSEvent *)theEvent - (void)mouseDragged:(NSEvent *)theEvent
{ {
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -590,7 +627,10 @@
newSelectionPath:mouseSelectionPath]; newSelectionPath:mouseSelectionPath];
} }
#pragma mark Key Event Handling #pragma mark Key Event Handling
- (void)keyDown:(NSEvent *)theEvent - (void)keyDown:(NSEvent *)theEvent
{ {
NSString *characters = theEvent.characters; NSString *characters = theEvent.characters;
@@ -617,6 +657,15 @@
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]]; [allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) { if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
if ([characters isEqualTo:@"i"]) {
if (self.selection.location.lastIndex > 0) {
id<MPExpressionElement> symbol = [self.expressionStorage symbolAtIndex:self.selection.location.lastIndex-1];
if ([symbol isEqual:@"p"]) {
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], self.selection.length+1);
characters = @"π";
}
}
}
if ([characters isEqualToString:@"*"]) { if ([characters isEqualToString:@"*"]) {
characters = @"⋅"; characters = @"⋅";
} }
@@ -629,22 +678,28 @@
} }
} }
#pragma mark - Actions #pragma mark - Actions
- (void)switchToDegrees:(id)sender - (void)switchToDegrees:(id)sender
{ {
[MPMathRules sharedRules].isUsingDegrees = YES; [MPMathRules sharedRules].isUsingDegrees = YES;
} }
- (void)switchToRadians:(id)sender - (void)switchToRadians:(id)sender
{ {
[MPMathRules sharedRules].isUsingDegrees = NO; [MPMathRules sharedRules].isUsingDegrees = NO;
} }
- (void)switchRadiansDegrees:(id)sender - (void)switchRadiansDegrees:(id)sender
{ {
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees; [MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
} }
- (IBAction)toggleFunctionsPopover:(id)sender - (IBAction)toggleFunctionsPopover:(id)sender
{ {
if (self.functionsPopover == nil || self.functionsViewController == nil) { if (self.functionsPopover == nil || self.functionsViewController == nil) {
@@ -663,6 +718,7 @@
} }
} }
- (void)insertFunction:(MPFunction *)function - (void)insertFunction:(MPFunction *)function
{ {
[self.functionsPopover close]; [self.functionsPopover close];
@@ -678,8 +734,10 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0); self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
} }
#pragma mark Editing Actions #pragma mark Editing Actions
- (void)insertParenthesisFunction:(id)sender - (void)insertParenthesisFunction:(id)sender
{ {
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
@@ -698,6 +756,7 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length); self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
} }
- (void)insertClosingParenthesis - (void)insertClosingParenthesis
{ {
if (self.selection.length > 0) { if (self.selection.length > 0) {
@@ -721,6 +780,7 @@
} }
} }
- (void)insertPowerFunction - (void)insertPowerFunction
{ {
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
@@ -739,6 +799,7 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length); self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
} }
- (void)insertFractionFunction - (void)insertFractionFunction
{ {
if (self.selection.length == 0) { if (self.selection.length == 0) {
@@ -792,6 +853,7 @@
} }
} }
- (void)insertNewline:(id)sender - (void)insertNewline:(id)sender
{ {
if (self.target && self.action) { if (self.target && self.action) {
@@ -801,6 +863,7 @@
} }
} }
- (void)deleteBackward:(id)sender - (void)deleteBackward:(id)sender
{ {
if (self.selection.length > 0) { if (self.selection.length > 0) {
@@ -853,6 +916,7 @@
} }
} }
- (void)delete:(id)sender - (void)delete:(id)sender
{ {
[self.expressionStorage replaceItemsInRangePath:self.selection [self.expressionStorage replaceItemsInRangePath:self.selection
@@ -861,8 +925,10 @@
self.selection = MPMakeRangePath(self.selection.location, 0); self.selection = MPMakeRangePath(self.selection.location, 0);
} }
#pragma mark Selection Actions #pragma mark Selection Actions
- (void)moveRight:(id)sender - (void)moveRight:(id)sender
{ {
if (self.selection.length > 0) { if (self.selection.length > 0) {
@@ -875,6 +941,7 @@
} }
} }
- (void)moveLeft:(id)sender - (void)moveLeft:(id)sender
{ {
if (self.selection.length > 0) { if (self.selection.length > 0) {
@@ -887,6 +954,7 @@
} }
} }
- (void)moveWordRight:(id)sender - (void)moveWordRight:(id)sender
{ {
NSIndexPath *location = self.selection.maxRangePath; NSIndexPath *location = self.selection.maxRangePath;
@@ -896,6 +964,7 @@
self.selection = MPMakeRangePath(newSelectionLocation, 0); self.selection = MPMakeRangePath(newSelectionLocation, 0);
} }
- (void)moveWordLeft:(id)sender - (void)moveWordLeft:(id)sender
{ {
NSIndexPath *location = self.selection.location; NSIndexPath *location = self.selection.location;
@@ -905,17 +974,20 @@
self.selection = MPMakeRangePath(newSelectionLocation, 0); self.selection = MPMakeRangePath(newSelectionLocation, 0);
} }
- (void)moveToBeginningOfLine:(id)sender - (void)moveToBeginningOfLine:(id)sender
{ {
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0); self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
} }
- (void)moveToEndOfLine:(id)sender - (void)moveToEndOfLine:(id)sender
{ {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0); self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
} }
- (void)moveLeftAndModifySelection:(id)sender - (void)moveLeftAndModifySelection:(id)sender
{ {
if (self.selection.length == 0) { if (self.selection.length == 0) {
@@ -935,6 +1007,7 @@
self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location]; self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location];
} }
- (void)moveRightAndModifySelection:(id)sender - (void)moveRightAndModifySelection:(id)sender
{ {
if (self.selection.length == 0) { if (self.selection.length == 0) {
@@ -955,6 +1028,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
} }
- (void)moveWordRightAndModifySelection:(id)sender - (void)moveWordRightAndModifySelection:(id)sender
{ {
if (self.selection.length == 0) { if (self.selection.length == 0) {
@@ -977,6 +1051,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
} }
- (void)moveWordLeftAndModifySelection:(id)sender - (void)moveWordLeftAndModifySelection:(id)sender
{ {
if (self.selection.length == 0) { if (self.selection.length == 0) {
@@ -999,6 +1074,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
} }
- (void)moveUp:(id)sender - (void)moveUp:(id)sender
{ {
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex]; NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
@@ -1011,6 +1087,7 @@
} }
} }
- (void)moveDown:(id)sender - (void)moveDown:(id)sender
{ {
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex]; NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
@@ -1023,13 +1100,17 @@
} }
} }
- (void)selectAll:(id)sender - (void)selectAll:(id)sender
{ {
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0]; NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols); self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
} }
#pragma mark Drawing Methods #pragma mark Drawing Methods
- (void)drawRect:(NSRect)dirtyRect - (void)drawRect:(NSRect)dirtyRect
{ {
// Draw the background // Draw the background
@@ -1066,6 +1147,7 @@
#pragma mark - User Interface Validations #pragma mark - User Interface Validations
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
{ {
BOOL degrees = [MPMathRules sharedRules].isUsingDegrees; BOOL degrees = [MPMathRules sharedRules].isUsingDegrees;

View File

@@ -9,6 +9,12 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPFactorialTerm</code> class.
*/
@class MPFactorialTerm; @class MPFactorialTerm;

View File

@@ -8,6 +8,8 @@
#import "MPFactorialTerm.h" #import "MPFactorialTerm.h"
@implementation MPFactorialTerm @implementation MPFactorialTerm
- (instancetype)initWithTerm:(MPTerm *)term - (instancetype)initWithTerm:(MPTerm *)term
@@ -20,6 +22,7 @@
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
NSDecimalNumber *value = [self.term evaluate:error]; NSDecimalNumber *value = [self.term evaluate:error];

View File

@@ -9,6 +9,12 @@
#import "MPFunction.h" #import "MPFunction.h"
/*!
@header
This file contains the <code>MPFractionFunction</code> class.
*/
@class MPFractionFunction, MPExpression; @class MPFractionFunction, MPExpression;

View File

@@ -11,21 +11,26 @@
#import "MPFractionTerm.h" #import "MPFractionTerm.h"
#import "MPExpression.h" #import "MPExpression.h"
@implementation MPFractionFunction @implementation MPFractionFunction
MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression) MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression)
MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression) MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression)
- (NSArray *)childrenAccessors - (NSArray *)childrenAccessors
{ {
return @[@"nominatorExpression", @"denominatorExpression"]; return @[@"nominatorExpression", @"denominatorExpression"];
} }
- (Class)functionTermClass - (Class)functionTermClass
{ {
return [MPFractionTerm class]; return [MPFractionTerm class];
} }
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression]; return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression];

View File

@@ -9,12 +9,32 @@
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
/*!
@header
This file contains the <code>MPFractionFunctionLayout</code> class.
*/
@class MPFractionFunctionLayout, MPFractionFunction; @class MPFractionFunctionLayout, MPFractionFunction;
/*!
@class MPFractionFunctionLayout
@abstract A fraction function layout displays a <code>@link
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
@discussion The nominator is displayed above the denominator. Between the two
children a horizontal bar is drawn.
*/
@interface MPFractionFunctionLayout : MPFunctionLayout @interface MPFractionFunctionLayout : MPFunctionLayout
/*!
@method fractionFunction
@abstract Returns the <code>@link
//apple_ref/occ/cl/MPFractionFunction@/link</code> represented by
the receiver.
*/
- (MPFractionFunction *)fractionFunction; - (MPFractionFunction *)fractionFunction;
@end @end

View File

@@ -10,6 +10,7 @@
#import "MPFractionFunction.h" #import "MPFractionFunction.h"
#define kFractionFunctionLineWidth 1.0 #define kFractionFunctionLineWidth 1.0
#define kFractionFunctionHorizontalInset 2.0 #define kFractionFunctionHorizontalInset 2.0
#define kFractionFunctionNominatorOffset 0 #define kFractionFunctionNominatorOffset 0
@@ -17,6 +18,8 @@
#define MPFractionMiddle (CTFontGetCapHeight((CTFontRef)self.font) / 2) #define MPFractionMiddle (CTFontGetCapHeight((CTFontRef)self.font) / 2)
@implementation MPFractionFunctionLayout @implementation MPFractionFunctionLayout
- (MPFractionFunction *)fractionFunction - (MPFractionFunction *)fractionFunction
@@ -24,21 +27,25 @@
return (MPFractionFunction *)self.function; return (MPFractionFunction *)self.function;
} }
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{ {
return 1; return 1;
} }
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{ {
return 0; return 0;
} }
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
} }
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{ {
NSRect bounds = self.bounds; NSRect bounds = self.bounds;
@@ -54,11 +61,13 @@
} }
} }
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index - (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{ {
return YES; return YES;
} }
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{ {
if (point.x < self.bounds.size.width / 2) { if (point.x < self.bounds.size.width / 2) {
@@ -68,6 +77,7 @@
} }
} }
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds; NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds;
@@ -80,6 +90,7 @@
return bounds; return bounds;
} }
- (void)draw - (void)draw
{ {
CGFloat y = MPFractionMiddle - kFractionFunctionLineWidth / 2; CGFloat y = MPFractionMiddle - kFractionFunctionLineWidth / 2;

View File

@@ -9,10 +9,24 @@
#import "MPFunctionTerm.h" #import "MPFunctionTerm.h"
/*!
@header
This file contains the <code>MPFractionTerm</code> class.
*/
@class MPFractionTerm; @class MPFractionTerm;
/*!
@class MPFractionTerm
@abstract Represens a <code>@link
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
@discussion A fraction is evaluating by dividing the nominator by the
denominator.
*/
@interface MPFractionTerm : MPFunctionTerm @interface MPFractionTerm : MPFunctionTerm
@end @end

View File

@@ -10,6 +10,8 @@
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
@implementation MPFractionTerm @implementation MPFractionTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error

View File

@@ -10,6 +10,12 @@
#import "MPToken.h" #import "MPToken.h"
/*!
@header
This file contains the <code>MPFunction(MPToken) category</code>.
*/
/*! /*!
@category MPFunction (MPToken) @category MPFunction (MPToken)

View File

@@ -11,6 +11,8 @@
/*! /*!
@header @header
This file contains the <code>MPFunction</code> class.
<code>MPFunction</code> is a half abstract class that is designed to be <code>MPFunction</code> is a half abstract class that is designed to be
subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called
<i>functions</i>) need to regard the following guidelines: <i>functions</i>) need to regard the following guidelines:
@@ -41,7 +43,6 @@
*/ */
/*! /*!
@define MPFunctionAccessorImplementation @define MPFunctionAccessorImplementation
@abstract This macro implements the setter of a previously declared @abstract This macro implements the setter of a previously declared

View File

@@ -18,7 +18,6 @@
@implementation MPFunction @implementation MPFunction
#pragma mark Creation Methods #pragma mark Creation Methods

View File

@@ -9,53 +9,334 @@
#import "MPLayout.h" #import "MPLayout.h"
/*!
@header
This file contains the <code>MPFunctionLayout</code> class.
The <code>MPFunctionLayout</code> class is an abstract class that implements
many of the methods from the layout system. Concrete subclasses implement the
actual layout of a specific function and its children. For that reason
<code>MPFunctionLayout</code> features a private cache. In that cache any
subclass can store objects so they do not need to be recreated everytime the
function is rendered.
*/
@class MPFunctionLayout, MPFunction, MPExpressionLayout; @class MPFunctionLayout, MPFunction, MPExpressionLayout;
/*!
@class MPFunctionLayout
@abstract A function layout represents a <code>@link
//apple_ref/occ/cl/MPFunction@/link</code>.
@discussion <code>MPFunctionLayout</code> is an abstract class that
implements many required methods of the layout system. For more
information about the layout system see the documentation on the
<code>@link //apple_ref/occ/cl/MPLayout@/link</code> class.
*/
@interface MPFunctionLayout : MPLayout @interface MPFunctionLayout : MPLayout
/*!
@method functionLayoutForFunction:parent:
@abstract Creates a function layout that can draw the specified function.
@discussion This method is the preferred whay to construct function layouts.
@param function
The function to create a layout for. Must not be
<code>nil</code>.
@param parent
The parent of the created function layout. May be
<code>nil</code>.
@return A newly created function layout.
*/
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function + (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent; parent:(MPExpressionLayout *)parent;
@property (readonly, nonatomic, weak) MPFunction *function;
@end /*!
@method initWithFunction:parent:
@abstract Initializes the receiver with the specified function and parent.
@interface MPFunctionLayout (MPSubclassOverride) @discussion This method should not be called direcly. It is however possible
do perform custom initialization in sublcasses by overriding this method.
This method is the designated initializer of the
<code>MPFunctionLayout</code> class.
@param function
The function represented by the receiver. Must not be
<code>nil</code>.
@param parent
The parent of the receiver or <code>nil</code> if it does not
have one.
@return A newly initialized <code>MPFunctionLayout</code> instance.
*/
- (instancetype)initWithFunction:(MPFunction *)function - (instancetype)initWithFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent; parent:(MPExpressionLayout *)parent;
#pragma mark Cache Methods
/*!
@property function
@abstract The function represented by the receiver.
*/
@property (readonly, nonatomic, weak) MPFunction *function;
/*!
@method lineForPrivateCacheIndex:generator:
@abstract Returns the cached line for the specified private index.
@discussion This is a convenience method that calls <code>@link
//apple_ref/occ/instm/MPFunctionLayout/objectForPrivateCacheIndex:generator:@/link</code>.
See the documentation on that method for details.
@param index
The index of the line to retrieve. Private cache indexes start at <code>0</code>.
@param generator
A block that generates a line in case there is no cached one.
@return The cached line or the newly generated one if there was no cache value.
*/
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index - (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
generator:(CTLineRef (^)())generator; generator:(CTLineRef (^)())generator;
/*!
@method objectForPrivateCacheIndex:generator:
@abstract Returns the cached value for the specified private index.
@discussion If there is no object in the private cache for the specified
index the specified <code>generator</code> is invoked. The result
of the <code>generator</code> is then stored at the specified
<code>index</code> and returned.
@param index
The index of the private cache item to retrieve. Private cache
indexes start at <code>0</code>.
@param generator
A block that generates an object in case there is no cached one.
@return The cached object or the newly generated one if there was no
cache value.
*/
- (id)objectForPrivateCacheIndex:(NSUInteger)index - (id)objectForPrivateCacheIndex:(NSUInteger)index
generator:(id (^)())generator; generator:(id (^)())generator;
// Should also implement accessor method for special function type: @end
// - (MPCustomFunction *)customFunction
// {
// return (MPCustomFunction *)self.function;
// }
#pragma mark Size and Drawing Methods
/*!
@category MPFunctionLayout (MPSubclassOverride)
@abstract The methods defined in this category must be implemented by any
concrete subclass of <code>MPFunctionLayout</code>.
*/
@interface MPFunctionLayout (MPSubclassOverride)
/* Apart from the methods below it is recommended (although not required) to
* implement a method in the following form:
* - (CustomFunctionType *)customFunction
* {
* return (CustomFunctionType *)self.function;
* }
*/
/*!
@method childAtIndexUsesSmallSize:
@abstract Determines whether the child expression at the specified index
should be drawn using the small font.
@discussion This method may be overridden to change the default behaviour.
@param index
The index of the child whose size is to be determined.
@return <code>YES</code> if the receiver's child at the specified
<code>index</code> should use the small font, <code>NO</code>
otherwise. The default implementation returns <code>NO</code>.
*/
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index; // May be implemented - (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index; // May be implemented
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index; // To be implemented
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point; // To be implemented
- (NSRect)generateBounds; // To be implemented
- (void)draw; // To be implemented
// Specify the child that is used when the cursor enters the function from the left or right respectively
- (NSUInteger)indexOfLeadingChild; // To be implemented
- (NSUInteger)indexOfTrailingChild; // To be implemented
// The index of the child before (left) or after (right) the child at @c index. return NSNotFound if there is no child before or after @c index. /*!
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index; // May be implemented @method offsetOfChildLayoutAtIndex:
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index; // May be implemented @abstract Implementation requirement from superclass. See <code>@link
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index; // May be implemented //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index; // May be implemented for details.
- (NSIndexSet *)indexesOfRemainingChildren; // May be implemented */
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
/*!
@method indexPathForLocalMousePoint:
@abstract Performs hit testing.
@discussion This method is called when the user selected a point in a
function that is not in any of its children. The return value of
this method identifies the position relative to the function
represented by the receiver where the caret is to be placed.
This method must be implemented by subclasses.
@param point
The location the user clicked at relative to the receiver.
@return An index path identifying the new caret position relative to the
function represented by the receiver. Use an index path
containing a single index to place the caret before or after the
represented function (<code>0</code> means before, <code>1</code>
means after).
*/
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point;
/*!
@method generateBounds
@abstract Implementation requirement from superclass. See <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
for details.
*/
- (NSRect)generateBounds;
/*!
@method draw
@abstract Implementation requirement from superclass. See <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/draw@/link</code>
for details.
*/
- (void)draw;
/*!
@method indexOfLeadingChild
@abstract Identifies the index of the receiver's child that will get
selected when the cursor <i>enters</i> the function in the
direction of reading.
@discussion You may implement this method do override the default behaviour.
@return The index of the leading child. The default implementation
returns <code>0</code>.
*/
- (NSUInteger)indexOfLeadingChild;
/*!
@method indexOfTrailingChild
@abstract Identifies the index of the receiver's child that will get
selected when the cursor <i>enters</i> the function in the
direction opposite to reading.
@discussion You may implement this method do override the default behaviour.
@return The index of the trailing child. The default implementation
returns <code>0</code>.
*/
- (NSUInteger)indexOfTrailingChild;
/*!
@method indexOfChildBeforeChildAtIndex:
@abstract Identifies the index of the receiver's child that will get
selected when the cursor <i>leaves</i> the child at the specified
index in the direction opposite to reading.
@discussion Return <code>NSNotFound</code> from this method if the function
should be <i>left</i> when the child at the specified index is
<i>left</i>.
You may implement this method to override the default behaviour.
@param index
The index of the child the caret <i>left</i>.
@return The index of the child that is to be <i>entered</i> or
<code>NSNotFound</code> if there are no more children. The
default implementation returns <code>NSNotFound</code>.
*/
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index;
/*!
@method indexOfChildAfterChildAtIndex:
@abstract Identifies the index of the receiver's child that will get
selected when the cursor <i>leaves</i> the child at the specified
index in the direction of reading.
@discussion Return <code>NSNotFound</code> from this method if the function
should be <i>left</i> when the child at the specified index is
<i>left</i>.
You may implement this method to override the default behaviour.
@param index
The index of the child the caret <i>left</i>.
@return The index of the child that is to be <i>entered</i> or
<code>NSNotFound</code> if there are no more children.
*/
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index;
/*!
@method indexOfChildBelowChildAtIndex:
@abstract Returns the index of the child that is graphically positioned
below the child at the specified index.
@discussion If there are no other children below the one at the specified
index this method should return <code>index</code>.
You may implement this method to override the default behaviour.
@param index
The index of the child whose neighbor is to be determined.
@return The index of the child directly below the one at the specified
index. The default implementation returns <code>index</code>.
*/
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index;
/*!
@method indexOfChildAboveChildAtIndex:
@abstract Returns the index of the child that is graphically positioned
above the child at the specified index.
@discussion If there are no other children above the one at the specified
index this method should return <code>index</code>.
You may implement this method to override the default behaviour.
@param index
The index of the child whose neighbor is to be determined.
@return The index of the child directly above the one at the specified
index. The default implementation returns <code>index</code>.
*/
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index;
/*!
@method indexesOfRemainingChildren
@abstract Returns the indexes of the receiver's children that should
replace the receiver if it is deleted.
@discussion You may implement this method to override the default behaviour.
@return The indexes of the children that replace the receiver when it is
deleted. The default implementation returns an empty index set.
*/
- (NSIndexSet *)indexesOfRemainingChildren;
@end @end

View File

@@ -8,24 +8,28 @@
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPExpression.h" #import "MPExpressionLayout.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPSumFunction.h" #import "MPFractionFunction.h"
#import "MPParenthesisFunction.h" #import "MPParenthesisFunction.h"
#import "MPPowerFunction.h" #import "MPPowerFunction.h"
#import "MPFractionFunction.h" #import "MPSumFunction.h"
#import "MPExpressionLayout.h" #import "MPFractionFunctionLayout.h"
#import "MPSumFunctionLayout.h"
#import "MPParenthesisFunctionLayout.h" #import "MPParenthesisFunctionLayout.h"
#import "MPPowerFunctionLayout.h" #import "MPPowerFunctionLayout.h"
#import "MPFractionFunctionLayout.h" #import "MPSumFunctionLayout.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@implementation MPFunctionLayout @implementation MPFunctionLayout
#pragma mark Creation Methods #pragma mark Creation Methods
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function + (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent parent:(MPExpressionLayout *)parent
{ {
@@ -43,6 +47,7 @@
parent:parent]; parent:parent];
} }
- (instancetype)initWithFunction:(MPFunction *)function - (instancetype)initWithFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent parent:(MPExpressionLayout *)parent
{ {
@@ -53,7 +58,9 @@
return self; return self;
} }
#pragma mark Cache Methods #pragma mark Cache Methods
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index - (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
generator:(CTLineRef (^)())generator generator:(CTLineRef (^)())generator
{ {
@@ -67,6 +74,7 @@
return (__bridge CTLineRef)(lineObject); return (__bridge CTLineRef)(lineObject);
} }
- (id)objectForPrivateCacheIndex:(NSUInteger)index - (id)objectForPrivateCacheIndex:(NSUInteger)index
generator:(id (^)())generator generator:(id (^)())generator
{ {
@@ -74,11 +82,13 @@
return [self cachableObjectForIndex:actualIndex generator:generator]; return [self cachableObjectForIndex:actualIndex generator:generator];
} }
- (NSUInteger)numberOfChildren - (NSUInteger)numberOfChildren
{ {
return self.function.numberOfChildren; return self.function.numberOfChildren;
} }
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index - (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{ {
return [self cachableObjectForIndex:index generator:^id{ return [self cachableObjectForIndex:index generator:^id{
@@ -91,11 +101,13 @@
}]; }];
} }
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index - (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{ {
return NO; return NO;
} }
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{ {
// A single index is used to communicate back wether the // A single index is used to communicate back wether the
@@ -116,36 +128,43 @@
return [self indexPathForLocalMousePoint:point]; return [self indexPathForLocalMousePoint:point];
} }
- (NSUInteger)indexOfLeadingChild - (NSUInteger)indexOfLeadingChild
{ {
return 0; return 0;
} }
- (NSUInteger)indexOfTrailingChild - (NSUInteger)indexOfTrailingChild
{ {
return 0; return 0;
} }
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
{ {
return NSNotFound; return NSNotFound;
} }
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{ {
return NSNotFound; return NSNotFound;
} }
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{ {
return index; return index;
} }
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{ {
return index; return index;
} }
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSet]; return [NSIndexSet indexSet];

View File

@@ -8,6 +8,28 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPFunctionTerm</code> class.
*/
/*!
@define MPEvaluateExpression
@abstract Evaluates the receiver's child at the specified index and assigns
the evaluation result to a variable.
@discussion If errors occur during evaluation this macro returns
<code>nil</code>. Also there has to exist a <code>NSError *__autoreleasing *</code> pointer called <code>error</code>.
@param var
The name of the variable that will be declared in this macro. It
will be of the type <code>NSDecimalNumber *</code> and contain
the result of the evaluation.
@param index
The index of the child to be evaluated.
*/
#define MPEvaluateExpression(var, index) NSDecimalNumber *var = [[self expressionAtIndex:index] evaluate:error]; if (var == nil) return nil #define MPEvaluateExpression(var, index) NSDecimalNumber *var = [[self expressionAtIndex:index] evaluate:error]; if (var == nil) return nil
@@ -15,11 +37,62 @@
@class MPFunctionTerm, MPFunction, MPParsedExpression; @class MPFunctionTerm, MPFunction, MPParsedExpression;
/*!
@class MPFunctionTerm
@abstract A function term represents the term of a generic function.
@discussion This class is an abstract class that can not be evaluated. It is
the common superclass of all terms that represent <code>@link
//apple_ref/occ/cl/MPFunction@/link</code> instances.
*/
@interface MPFunctionTerm : MPTerm @interface MPFunctionTerm : MPTerm
/*!
@method initWithFunction:errors:
@abstract Initializes a <code>MPFunctionTerm</code> from the specified
<code>@link //apple_ref/occ/cl/MPFunction@/link</code> instance.
@discussion This method parses the children of the specified
<code>function</code> and stores the respective parsed
expressions. They are accessible using the <code>@link
//apple_ref/occ/instm/MPFunctionTerm/expressionAtIndex:@/link</code>
method.
Whether the children should contain a variable definition is
determined using the <code>@link
//apple_ref/occ/instm/MPFunction/expectsVariableDefinitionInChildAtIndex:@/link</code>
@param function
The function to initialize the receiver with. This method parses
the function's children. If any of them contain syntax errors
they are returned indirectly through the <code>errors</code>
parameter.
@param errors
If any of the <code>function</code>'s children contain syntax
errors they are returned indirectly through this parameter. In
that case the method returns <code>nil</code>. If there are no
errors the parameter is not modified. This parameter is never set
to an empty array.
@return A new <code>MPFunctionTerm</code> instance.
*/
- (instancetype)initWithFunction:(MPFunction *)function - (instancetype)initWithFunction:(MPFunction *)function
errors:(NSArray *__autoreleasing *)errors; /* designated initializer */ errors:(NSArray *__autoreleasing *)errors; /* designated initializer */
/*!
@method expressionAtIndex:
@abstract Returns a <code>@link
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance that
represents the child at <code>anIndex</code> of the function that
is represented by the receiver.
@param anIndex
The index of the child.
@return A parsed expression that represents th
*/
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex; - (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex;
@end @end

View File

@@ -9,10 +9,6 @@
#import "MPFunctionTerm.h" #import "MPFunctionTerm.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPExpression.h"
#import "MPParsedExpression.h"
#import "MPToken.h"
@@ -64,6 +60,7 @@
return self; return self;
} }
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex - (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex
{ {
return [self.parsedExpressions objectAtIndex:anIndex]; return [self.parsedExpressions objectAtIndex:anIndex];

View File

@@ -7,19 +7,74 @@
// //
/*!
@header
This file contains the <code>MPFunctionsViewController</code> class.
*/
@class MPFunctionsViewController, MPFunctionsCollectionView; @class MPFunctionsViewController, MPFunctionsCollectionView;
/*!
@class MPFunctionsViewController
@abstract Controls a view from which the user can select function to be
inserted into a <code>@link
//apple_ref/occ/cl/MPExpressionView@/link</code>.
@discussion The view contain a <code>NSCollectionView</code> displaying the
prototypes an a <code>NSTextField</code> displaying a description
of the currently selected prototype.
*/
@interface MPFunctionsViewController : NSViewController @interface MPFunctionsViewController : NSViewController
- (id)init; /*!
@property collectionView
@abstract The <code>NSCollectionView</code> that displays the function
prototypes.
*/
@property (weak) IBOutlet NSCollectionView *collectionView;
@property (weak) IBOutlet MPFunctionsCollectionView *collectionView;
/*!
@property functionPrototypes
@abstract Contains the <code>@link
//apple_ref/occ/cl/MPFunction@/link</code> objects that get
inserted into an expression if selected.
@discussion Every object in the array must be an <code>@link
//apple_ref/occ/cl/MPFunction@/link</code> instance.
*/
@property (nonatomic, strong) NSArray *functionPrototypes; @property (nonatomic, strong) NSArray *functionPrototypes;
/*!
@property currentDescription
@abstract The string that describes the function prototype that is
currently selected.
@discussion If this string is empty a placeholder value will be displayed.
*/
@property (nonatomic, strong) NSString *currentDescription; @property (nonatomic, strong) NSString *currentDescription;
/*!
@property target
@abstract The target object to receive action messages from the receiver.
*/
@property (nonatomic, weak) id target; @property (nonatomic, weak) id target;
@property (nonatomic) SEL action; // 1 argument: The function to insert
/*!
@property action
@abstract The receiver's action method to the specified selector.
@discussion The action method is invoked when the user selects one of the
function prototypes in the collection view. The prototype that
was selected is passed as the one and only argument to
the specified selector.
*/
@property (nonatomic) SEL action;
@end @end

View File

@@ -8,18 +8,21 @@
#import "MPFunctionsViewController.h" #import "MPFunctionsViewController.h"
#import "MPExpression.h"
#import "NSString+MPExpressionElement.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPSumFunction.h" #import "MPFractionFunction.h"
#import "MPParenthesisFunction.h" #import "MPParenthesisFunction.h"
#import "MPPowerFunction.h" #import "MPPowerFunction.h"
#import "MPFractionFunction.h" #import "MPSumFunction.h"
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "NSString+MPExpressionElement.h"
@class MPFunctionsCollectionView, MPFunctionTemplateView, MPFunctionTemplateItem; @class MPFunctionsCollectionView, MPFunctionTemplateView, MPFunctionTemplateItem;
@interface MPFunctionsCollectionView : NSCollectionView @interface MPFunctionsCollectionView : NSCollectionView
@property (nonatomic, weak) MPFunctionTemplateItem *hoverItem; @property (nonatomic, weak) MPFunctionTemplateItem *hoverItem;
@@ -30,6 +33,7 @@
@end @end
@interface MPFunctionTemplateView : NSView @interface MPFunctionTemplateView : NSView
@property (nonatomic, strong) MPFunction *functionTemplate; @property (nonatomic, strong) MPFunction *functionTemplate;
@@ -42,16 +46,20 @@
@interface MPFunctionTemplateItem : NSCollectionViewItem @interface MPFunctionTemplateItem : NSCollectionViewItem
@property (nonatomic, copy) NSString *templateName; @property (nonatomic, copy) NSString *templateName;
@end @end
@implementation MPFunctionsCollectionView @implementation MPFunctionsCollectionView
- (void)mouseDown:(NSEvent *)theEvent - (void)mouseDown:(NSEvent *)theEvent
{ {
} }
- (void)mouseUp:(NSEvent *)theEvent - (void)mouseUp:(NSEvent *)theEvent
{ {
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -87,25 +95,30 @@
[super updateTrackingAreas]; [super updateTrackingAreas];
} }
- (void)mouseEntered:(NSEvent *)theEvent - (void)mouseEntered:(NSEvent *)theEvent
{ {
self.mouseOver = YES; self.mouseOver = YES;
self.needsDisplay = YES; self.needsDisplay = YES;
} }
- (void)mouseExited:(NSEvent *)theEvent - (void)mouseExited:(NSEvent *)theEvent
{ {
self.mouseOver = NO; self.mouseOver = NO;
self.needsDisplay = YES; self.needsDisplay = YES;
} }
- (void)setFunctionTemplate:(MPFunction *)functionTemplate - (void)setFunctionTemplate:(MPFunction *)functionTemplate
{ {
_functionTemplate = functionTemplate; _functionTemplate = functionTemplate;
_functionTemplateLayout = nil; _functionTemplateLayout = nil;
} }
@synthesize functionTemplateLayout = _functionTemplateLayout; @synthesize functionTemplateLayout = _functionTemplateLayout;
- (MPFunctionLayout *)functionTemplateLayout - (MPFunctionLayout *)functionTemplateLayout
{ {
@@ -117,16 +130,19 @@
return _functionTemplateLayout; return _functionTemplateLayout;
} }
- (NSSize)intrinsicContentSize - (NSSize)intrinsicContentSize
{ {
return [self.functionTemplateLayout bounds].size; return [self.functionTemplateLayout bounds].size;
} }
- (BOOL)isOpaque - (BOOL)isOpaque
{ {
return NO; return NO;
} }
- (void)drawRect:(NSRect)dirtyRect - (void)drawRect:(NSRect)dirtyRect
{ {
if (self.mouseOver) { if (self.mouseOver) {
@@ -150,11 +166,13 @@
static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMouseOverContext"; static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMouseOverContext";
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{ {
return [super awakeAfterUsingCoder:aDecoder]; return [super awakeAfterUsingCoder:aDecoder];
} }
- (void)observeValueForKeyPath:(NSString *)keyPath - (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object ofObject:(id)object
change:(NSDictionary *)change change:(NSDictionary *)change
@@ -171,6 +189,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
} }
} }
- (void)setRepresentedObject:(id)representedObject - (void)setRepresentedObject:(id)representedObject
{ {
MPFunctionTemplateView *view = (MPFunctionTemplateView *)self.view; MPFunctionTemplateView *view = (MPFunctionTemplateView *)self.view;
@@ -191,6 +210,8 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
@end @end
@implementation MPFunctionsViewController @implementation MPFunctionsViewController
- (id)init - (id)init
@@ -199,6 +220,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
bundle:[NSBundle bundleForClass:[self class]]]; bundle:[NSBundle bundleForClass:[self class]]];
} }
- (void)awakeFromNib - (void)awakeFromNib
{ {
MPFunction *sumFunction = [[MPSumFunction alloc] init]; MPFunction *sumFunction = [[MPSumFunction alloc] init];
@@ -227,40 +249,45 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
forKeyPath:@"hoverItem" forKeyPath:@"hoverItem"
options:0 options:0
context:MPCollectionViewHoverItemChangeContext]; context:MPCollectionViewHoverItemChangeContext];
self.collectionView.target = self.target; ((MPFunctionsCollectionView *)self.collectionView).target = self.target;
self.collectionView.action = self.action; ((MPFunctionsCollectionView *)self.collectionView).action = self.action;
} }
static void *MPCollectionViewHoverItemChangeContext = @"MPCollectionViewHoverItemChangeContext"; static void *MPCollectionViewHoverItemChangeContext = @"MPCollectionViewHoverItemChangeContext";
- (void)setView:(NSView *)view - (void)setView:(NSView *)view
{ {
[super setView:view]; [super setView:view];
} }
- (void)setTarget:(id)target - (void)setTarget:(id)target
{ {
_target = target; _target = target;
if (self.collectionView) { if (self.collectionView) {
self.collectionView.target = target; ((MPFunctionsCollectionView *)self.collectionView).target = target;
} }
} }
- (void)setAction:(SEL)action - (void)setAction:(SEL)action
{ {
_action = action; _action = action;
if (self.collectionView) { if (self.collectionView) {
self.collectionView.action = action; ((MPFunctionsCollectionView *)self.collectionView).action = action;
} }
} }
- (void)observeValueForKeyPath:(NSString *)keyPath - (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object ofObject:(id)object
change:(NSDictionary *)change change:(NSDictionary *)change
context:(void *)context context:(void *)context
{ {
if (context == MPCollectionViewHoverItemChangeContext) { if (context == MPCollectionViewHoverItemChangeContext) {
self.currentDescription = self.collectionView.hoverItem.templateName; self.currentDescription = ((MPFunctionsCollectionView *)self.collectionView).hoverItem.templateName;
} else { } else {
[super observeValueForKeyPath:keyPath [super observeValueForKeyPath:keyPath
ofObject:object ofObject:object

View File

@@ -6,12 +6,82 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
/*!
@header
This file contains the <code>MPLayout</code> class.
The <code>MPLayout</code> class renders an expression to be displayed in an
<code>@link //apple_ref/occ/cl/MPExpressionView@/link</code>. There are multiple
subclasses of <code>MPLayout</code> the most important of which are <code>@link
//apple_ref/occ/cl/MPExpressionLayout@/link</code> and <code>@link
//apple_ref/occ/cl/MPFunctionLayout@/link</code> for rendering expressions and
functions respectively.
Both types of layouts can occur in two different sizes: normal and small. The
root expression ueses the normal size. Depending on the concrete subclass of
<code>@link //apple_ref/occ/cl/MPFunctionLayout@/link</code> that is used a
function's children (and any subsequent children) may be rendered using the
small size. Also there are two different fonts available for layouts: a normal
and a special font. The special font should only be used to emphasize special
content that uses the normal font.
If an expression is empty an empty box is rendered instead. That box (called the
<i>empty box</i> uses the font metrics of the empty expression it represents.
That means it uses different sizes depending on whether the expression uses a
small or normal sized font. Also it is drawn a little smaller than the actual
box's height is because it looks nicer. Depending on your needs you can query
the metrics of the empty box using the <code>kMPEmptyBox...</code> or
<code>kMPEmptyBoxDrawing...</code> macros. These macros must not be used outside
of the <code>MPLayout</code> class or any of it subclasses.
*/
/*!
@define kMPEmptyBoxWidth
@abstract The width of the empty box.
*/
#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0) #define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0)
/*!
@define kMPEmptyBoxHeight
@abstract The actual height of the empty box.
*/
#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)) #define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))
/*!
@define kMPEmptyBoxYOrigin
@abstract The actual vertical origin of the empty box.
@discussion The vertical origin is a negative value to be compliant with the
metrics of strings.
*/
#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))) #define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)))
/*!
@define kMPEmptyBoxDrawingWidth
@abstract The with the empty box is rendered with.
*/
#define kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth #define kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth
/*!
@define kMPEmptyBoxDrawingHeight
@abstract The height the empty box is drawn with.
*/
#define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font)) #define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font))
/*!
@define kMPEmptyBoxDrawingYOrigin
@abstract The vertical origin the empty box is drawn at.
@discussion The vertical origin is a negative value to be compliant with the
metrics of strings.
*/
#define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2)) #define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2))
@@ -19,58 +89,548 @@
@class MPLayout, MPRangePath; @class MPLayout, MPRangePath;
/*!
@class MPLayout
@abstract This is an abstract class that defines the basic methods that
must be implemented by other layout classes. It also defines some
useful helper methods.
@discussion The <code>MPLayout</code> class maintains a generic cache. It is
used to store child layouts and actual cache information.
Every layout instance represents either an expression or a
function. The instance's children represent the children of the
represented object.
*/
@interface MPLayout : NSObject @interface MPLayout : NSObject
#pragma mark Creation Methods #pragma mark Creation Methods
/*!
@methodgroup Creation Methods
*/
/*!
@method init
@abstract Initializes the receiver.
@discussion This method should only be used to initialize the expression
layout that renders the root layout.
This method is the designated initializer of the
<code>MPLayout</code> class.
@return A newly initialized <code>MPLayout</code> instance.
*/
- (instancetype)init; - (instancetype)init;
/*!
@method initWithParent:
@abstract Initializes the receiver with the specified parent.
@discussion This method should generally be used when initializing layout
instances. In most cases it should be called at some point in the
implementation of the <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/childLayoutAtIndex:@/link</code>
method.
@param parent
The receiver's parent.
@return A newly initialized <code>MPLayout</code> instance.
*/
- (instancetype)initWithParent:(MPLayout *)parent; - (instancetype)initWithParent:(MPLayout *)parent;
#pragma mark Properties
- (NSFont *)normalFontWithSize:(CGFloat)size;
- (NSFont *)specialFontWithSize:(CGFloat)size;
- (CGFloat)contextInferredFontSize;
- (CGFloat)normalFontSize;
- (CGFloat)smallFontSize;
#pragma mark Properties
/*!
@methodgroup Properties
*/
/*!
@method font
@abstract Returns the receiver's context inferred font.
@discussion The context inferred font is the normal font using the context
inferred font size. Normally this method is used for any textual
content of a layout.
@return The font the receiver should use for rendering textual content.
*/
- (NSFont *)font; - (NSFont *)font;
/*!
@method normalFontWithSize:
@abstract Returns a <code>NSFont</code> object that represents the normal
font in the specified <code>size</code>.
@discussion Instead of this method you want to use <code>@link
//apple_ref/occ/instm/MPLayout/font@/link</code> instead in most
cases.
@param size
The size of the font.
@return A <code>NSFont</code> object that can be used to render textual
content of the receiver in the specified <code>size</code>.
*/
- (NSFont *)normalFontWithSize:(CGFloat)size;
/*!
@method specialFontWithSize:
@abstract Returns a <code>NSFont</code> object that represents the special
font in the specified <code>size</code>.
@discussion Use fonts returned by this method only if you need to emphasize
text that is surrounded by other text that uses the normal font.
@param size
The size of the font.
@return A <code>NSFont</code> object that can be used to render special
textual content of the receiver in the specified
<code>size</code>.
*/
- (NSFont *)specialFontWithSize:(CGFloat)size;
/*!
@method contextInferredFontSize
@abstract Returns the appropriate font size for the receiver based on the
context it will be rendered in.
*/
- (CGFloat)contextInferredFontSize;
/*!
@method normalFontSize
@abstract Returns the font size that is used in the root expression.
*/
- (CGFloat)normalFontSize;
/*!
@method smallFontSize
@abstract Returns the font size that is used in expressions that are using
the small size.
*/
- (CGFloat)smallFontSize;
#pragma mark Cache Tree #pragma mark Cache Tree
/*!
@methodgroup Cache Tree
*/
/*!
@property parent
@abstract The receiver's parent layout.
@discussion The parent layout is most likely the one that created the
receiver and is responsible for setting its attributes
appropriately based on the rendering context.
*/
@property (readonly, nonatomic, weak) MPLayout *parent; @property (readonly, nonatomic, weak) MPLayout *parent;
#pragma mark Cache Methods #pragma mark Cache Methods
// Querying Caches /*!
@methodgroup Cache Methods
*/
/*!
@method chacableObjectForIndex:generator:
@abstract Returns the cached object for the specified <code>index</code> or
creates one if it does not exist.
@discussion This method only returns <code>nil</code> if the
<code>generator</code> returns <code>nil</code>.
@param index
The index of the cached object to retrieve.
@param generator
This block is executed if there is no cached object at
<code>index</code>. The result of this block is then cached at
the <code>index</code>.
@return The cached object for <code>index</code> or the result of the
<code>generator</code> if there is no cached object.
*/
- (id)cachableObjectForIndex:(NSUInteger)index - (id)cachableObjectForIndex:(NSUInteger)index
generator:(id(^)())generator; generator:(id(^)())generator;
// Clearing Caches
/*!
@method clearCacheInRange:replacementLength:
@abstract Removes the objects in the specified from the cache and moves all
objects at subsequent indexes.
@discussion The objects at subsequent indexes are moved
<code>replacementLength - range.length</code> places. If
<code>replacementLength</code> is greater than
<code>range.length</code> they are moved to smaller indexes.
@param range
The range of objects to be removed.
@param replacementLength
The number of indexes replacing the ones specified by
<code>range</code>.
*/
- (void)clearCacheInRange:(NSRange)range - (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength; replacementLength:(NSUInteger)replacementLength;
/*!
@method invalidate
@abstract Invalidates the receiver.
@discussion Invalidating the receiver causes him to discard all cached
information about itself and send a <code>invalidate</code>
message to its parent. The cached information about the
receiver's children is <b>not</b> discarded. Use <code>@link
//apple_ref/occ/instm/MPLayout/clearCacheInRange:replacementLength:@/link</code>
for that purpose.
*/
- (void)invalidate; - (void)invalidate;
/*!
@method childLayoutAtIndexPath:
@abstract Returns the child layout at the specified index path.
@discussion If the <code>indexPath</code> is empty the receiver is returned.
If the child layout the index path specifies is not yet created
this method should do so and add it to the cache of the
respective parent layout.
@param indexPath
The index path of the child.
@return The child at the specified index path.
*/
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath; - (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Calculation and Drawing Methods
#pragma mark Rendering Methods
/*!
@methodgroup Rendering Methods
*/
/*!
@method createLineForString:
@abstract Renders the specified string.
@discussion This method uses the normal font and the context inferred font
size to render the string. In most cases this is the appropriate
method for rendering textual content in a layout.
@param aString
The string to be rendered.
@return A <code>CTLineRef</code> representing the rendered string.
*/
- (CTLineRef)createLineForString:(NSString *)aString; - (CTLineRef)createLineForString:(NSString *)aString;
/*!
@method createLineForString:emphasize:
@abstract Renders the specified string.
@discussion This method uses either the normal or the special font depending
on the <code>emphasize</code> flag. It uses the context inferred
font size.
@param aString
The string to be rendered.
@param emphasize
A flag indicating whether the special font should be used.
@return A <code>CTLineRef</code> representing the rendered string.
*/
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize; - (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize;
/*!
@method createLineForString:usingFont:
@abstract Renders the specified string in the specified font.
@discussion In most cases you do should prefer <code>@link
//apple_ref/occ/instm/MPLayout/createLineForString:@/link</code>
or <code>@link
//apple_ref/occ/instm/MPLayout/createLineForString:emphasize:@/link</code>
over this method.
@param aString
The string to be rendered.
@param font
The font to render <code>aString</code> in.
@return A <code>CTLineRef</code> representing the rendered string.
*/
- (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font; - (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font;
/*!
@property flipped
@abstract This property indicates whether the receiver uses a flipped
layout.
@discussion The value of this property should be the same in the receiver,
its parent and all of its children.
*/
@property (nonatomic, getter = isFlipped) BOOL flipped; @property (nonatomic, getter = isFlipped) BOOL flipped;
/*!
@property usesSmallSize
@abstract This property indicates whether the receiver uses the small font
size.
@discussion This property is used to determine the <code>@link
//apple_ref/occ/instm/MPLayout/contextInferredFontSize@/link</code>.
If this value is <code>YES</code> all of the receiver's children
should also use the small size. If not the children may or may
not (depending on the actual layout implementation) use the small
size.
*/
@property (nonatomic) BOOL usesSmallSize; @property (nonatomic) BOOL usesSmallSize;
/*!
@method bounds
@abstract Returns the receiver's bounds.
@discussion If the receiver has cached bounds it will return the cached
value. The cached value can be removed using the <code>@link
//apple_ref/occ/instm/MPLayout/invalidate@/link</code> method. If
there is no cached value this method will generate a new cached
value by calling the <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
method.
@return The receiver's bounds.
*/
- (NSRect)bounds; - (NSRect)bounds;
/*!
@method boundingRectForRangePath:
@abstract Returns the rect relative to the origin of the receiver's bounds
that contains the symbols identified by the specified range path.
@discussion Use this method to determine where the user's selection should be
drawn at.
@param rangePath
The range path whose bounds are needed.
@return The rectangle containing the symbols identified by
<code>rangePath</code>.
*/
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */ - (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */
/*!
@method drawAtPoint:
@abstract Draws the receiver at the specified point.
@discussion If the receiver returns <code>NO</code> from its <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
method this method also draws every single child of the receiver.
The location the children are drawn at is determined by the
<code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
method.
@param point
The point the receiver is to be drawn at.
*/
- (void)drawAtPoint:(NSPoint)point; - (void)drawAtPoint:(NSPoint)point;
@end @end
/*!
@category MPLayout (MPSubclassImplement)
@abstract The methods in this category must be implemented by any concrete
subclass of <code>MPLayout</code>.
*/
@interface MPLayout (MPSubclassImplement) @interface MPLayout (MPSubclassImplement)
/*!
@method numberOfChildren
@abstract Returns the number of sublayouts of the receiver.
@discussion This method must be implemented by subclasses.
*/
- (NSUInteger)numberOfChildren; - (NSUInteger)numberOfChildren;
/*!
@method drawsChildrenManually
@abstract Returns whether the receiver wants to take over responsibility of
drawing its children.
@discussion If you return <code>YES</code> from this method
<code>MPLayout</code> does not draw the receiver's children. The
receiver's <code>//apple_ref/occ/intfm/MPLayout/draw</code>
method has to implement that functionality. If you return
<code>NO</code> from this method the receiver must not draw its
children in the <code>//apple_ref/occ/intfm/MPLayout/draw</code>
method.
This method may be implemented by subclasses to opt in and change
the default behaviour.
@return <code>YES</code> if the receiver draws its children
automatically, <code>NO</code> otherwise. The default
implementation returns <code>NO</code>.
*/
- (BOOL)drawsChildrenManually; - (BOOL)drawsChildrenManually;
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
- (NSRect)generateBounds; // To be implemented
- (NSRect)boundingRectForRange:(NSRange)range; // To be implemented, use rangePath instead, this one has wrong origin /*!
@method childLayoutAtIndex:
@abstract Returns the receiver's child at the specified index.
@discussion As long as the specified <code>index</code> does not exceed the
receiver's number of children, this method should always return a
value based on the following criteria:
1. If the receiver represents a <code>@link
//apple_ref/occ/cl/MPFunction@/link</code> instance it should
always return a valid <code>MPLayout</code> instance.
2. If the receiver represents a <code>@link
//apple_ref/occ/cl/MPExpression@/link</code> instance it
returns only a valid <code>MPLayout</code> instance if the
represented expression's element at <code>index</code> is a
function.
This method should cache any generated child layout and use the
cache whenever possible in order to reduce resources needed for
rendering.
This method must be implemented by subclasses.
@param index
The index of the requested child layout.
@return A <code>MPLayout</code> instance representing the respective
child of the object that is represented by the receiver, or
<code>nil</code> if the child represents a string element in an
expression.
*/
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index;
/*!
@method generateBounds
@abstract Calculates the receiever's bounds.
@discussion This method should not use any cached values. Caching of the
calculated bounds is done automatically by the
<code>MPLayout</code> class. This method is only called if the
receiving <code>MPLayout</code> instance has been invalidated and
the receiver's bounds have been requested.
This method must be implemented by subclasses.
@return The receiver's bounds.
*/
- (NSRect)generateBounds;
/*!
@method boundingRectForRange:
@abstract Returns the rectangle that encloses the symbols in the specified
range.
@discussion This method is called to calculate the rectangle that encloses
the user's selection. If the specified <code>range</code> has a
length of <code>0</code> the returned rectangle should be the
bounds of the caret (which has a width of <code>0</code>). The
height and y-origin of the returned rectangle should be equal to
the respective values of the receiver.
This method must be implemented by subclasses.
@param range
The range of the selection whose bounds should be calculated.
@return A rectangle enclosing the user's selection.
*/
- (NSRect)boundingRectForRange:(NSRange)range;
/*!
@method offsetOfChildLayoutAtIndex:
@abstract Returns the location of the child layout at the specified index.
@discussion This method must be implemented by subclasses even if the
subclass returns <code>YES</code> from the <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
method.
@param index
The index of the child whose location is to be calculated.
@return The location of the child layout at the specified index relative
to the receiver.
*/
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index; - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
/*!
@method indexPathForMousePoint:
@abstract Performs hit testing.
@discussion This method tests the specified <code>point</code> against all of
its children and returns the index path (relative to the index
path of the receiver) that identifies the location inside the
expression tree the user selected. The specified point must be
inside of the receiver and must be specified relatively to the
receiver. If the specified point is not inside any of the
receiver's children but in the receiver itself the returned index
path must contain one index specifiying the location in the
receiver.
This method must be implemented by subclasses.
@param point
The point inside the receiver that is to be tested.
@return The index path that points to the point the user selected. All
indexes in the path except for the last one are specified in the
element reference frame. The last index is specified in the
symbol reference frame.
*/
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point; - (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
- (void)draw; // To be implemented
/*!
@method draw
@abstract Draws the receiver.
@discussion If the receiver returns <code>YES</code> from its <code>@link
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
method the implementation of this method must also draw the
receiver's children.
This method must be implemented by subclasses. If the receiver
does not have anything to draw except for its children you should
implement this method with an empty method body.
*/
- (void)draw;
@end @end

View File

@@ -11,20 +11,25 @@
#import "MPRangePath.h" #import "MPRangePath.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@interface MPLayout () @interface MPLayout ()
// Querying and Storing Caches
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index; - (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
- (void)ensureCacheSizeForIndex:(NSUInteger)index; - (void)ensureCacheSizeForIndex:(NSUInteger)index;
@end @end
@implementation MPLayout { @implementation MPLayout {
NSMutableArray *_cache; NSMutableArray *_cache;
NSRect _cachedBounds; NSRect _cachedBounds;
} }
#pragma mark Creation Methods #pragma mark Creation Methods
- (id)init - (id)init
{ {
self = [super init]; self = [super init];
@@ -35,6 +40,7 @@
return self; return self;
} }
- (instancetype)initWithParent:(MPLayout *)parent - (instancetype)initWithParent:(MPLayout *)parent
{ {
self = [self init]; self = [self init];
@@ -44,41 +50,51 @@
return self; return self;
} }
#pragma mark Properties #pragma mark Properties
- (NSFont *)normalFontWithSize:(CGFloat)size - (NSFont *)normalFontWithSize:(CGFloat)size
{ {
return [NSFont fontWithName:@"CMU Serif" return [NSFont fontWithName:@"CMU Serif"
size:size]; size:size];
} }
- (NSFont *)specialFontWithSize:(CGFloat)size - (NSFont *)specialFontWithSize:(CGFloat)size
{ {
return [NSFont fontWithName:@"CMU Serif Italic" return [NSFont fontWithName:@"CMU Serif Italic"
size:size]; size:size];
} }
- (CGFloat)contextInferredFontSize - (CGFloat)contextInferredFontSize
{ {
return self.usesSmallSize ? self.smallFontSize : self.normalFontSize; return self.usesSmallSize ? self.smallFontSize : self.normalFontSize;
} }
- (CGFloat)normalFontSize - (CGFloat)normalFontSize
{ {
return 18.0; return 18.0;
} }
- (CGFloat)smallFontSize - (CGFloat)smallFontSize
{ {
return 12.0; return 12.0;
} }
- (NSFont *)font - (NSFont *)font
{ {
return [self normalFontWithSize:self.contextInferredFontSize]; return [self normalFontWithSize:self.contextInferredFontSize];
} }
#pragma mark Cache Tree #pragma mark Cache Tree
// Querying and Storing Caches
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index - (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
{ {
if (index >= _cache.count) { if (index >= _cache.count) {
@@ -87,6 +103,7 @@
return _cache[index] != [NSNull null]; return _cache[index] != [NSNull null];
} }
- (id)cachableObjectForIndex:(NSUInteger)index - (id)cachableObjectForIndex:(NSUInteger)index
generator:(id (^)())generator generator:(id (^)())generator
{ {
@@ -99,6 +116,7 @@
return object; return object;
} }
- (void)ensureCacheSizeForIndex:(NSUInteger)index - (void)ensureCacheSizeForIndex:(NSUInteger)index
{ {
while (index >= _cache.count) { while (index >= _cache.count) {
@@ -106,7 +124,7 @@
} }
} }
// Clearing Caches
- (void)clearCacheInRange:(NSRange)range - (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength replacementLength:(NSUInteger)replacementLength
{ {
@@ -119,12 +137,14 @@
[self invalidate]; [self invalidate];
} }
- (void)invalidate - (void)invalidate
{ {
_cachedBounds = NSZeroRect; _cachedBounds = NSZeroRect;
[self.parent invalidate]; [self.parent invalidate];
} }
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath - (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath
{ {
if (indexPath.length == 0) { if (indexPath.length == 0) {
@@ -134,19 +154,24 @@
return [child childLayoutAtIndexPath:[indexPath indexPathByRemovingFirstIndex]]; return [child childLayoutAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
} }
#pragma mark Calculation and Drawing Methods #pragma mark Calculation and Drawing Methods
- (CTLineRef)createLineForString:(NSString *)aString - (CTLineRef)createLineForString:(NSString *)aString
{ {
return [self createLineForString:aString return [self createLineForString:aString
usingFont:self.font]; usingFont:self.font];
} }
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize - (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize
{ {
return [self createLineForString:aString return [self createLineForString:aString
usingFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; usingFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
} }
- (CTLineRef)createLineForString:(NSString *)aString - (CTLineRef)createLineForString:(NSString *)aString
usingFont:(NSFont *)font usingFont:(NSFont *)font
{ {
@@ -158,6 +183,7 @@
return line; return line;
} }
- (NSRect)bounds - (NSRect)bounds
{ {
if (NSEqualRects(_cachedBounds, NSZeroRect)) { if (NSEqualRects(_cachedBounds, NSZeroRect)) {
@@ -166,6 +192,7 @@
return _cachedBounds; return _cachedBounds;
} }
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath - (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath
{ {
if (rangePath.location.length == 1) { if (rangePath.location.length == 1) {
@@ -180,11 +207,13 @@
return bounds; return bounds;
} }
- (BOOL)drawsChildrenManually - (BOOL)drawsChildrenManually
{ {
return NO; return NO;
} }
- (void)drawAtPoint:(NSPoint)point - (void)drawAtPoint:(NSPoint)point
{ {
NSAffineTransform *transform = [NSAffineTransform transform]; NSAffineTransform *transform = [NSAffineTransform transform];

View File

@@ -7,25 +7,49 @@
// //
/*!
@header
This file contains the <code>MPMathRules</code> class.
*/
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey;
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey; /*!
@const MPMathRulesIsUsingDegreesKey
@abstract Predefined key that is used to store the properties of the
<code>MPMathRules</code> class in the <code>NSUserDefaults</code>
database.
*/
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey; FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
@class MPMathRules; @class MPMathRules;
/*!
@class MPMathRules
@abstract The math rules class stores global settings concerning
mathematical interpretations
@discussion The <code>MPMathRules</code> class is a singletone class. There
can only exist one instance (the shared instance) at any time.
*/
@interface MPMathRules : NSObject @interface MPMathRules : NSObject
/*!
@method sharedRules
@abstract Returnes the shared <code>MPMathRules</code> instance.
*/
+ (MPMathRules *)sharedRules; + (MPMathRules *)sharedRules;
@property (nonatomic, getter = isUsingUserDefaultValues) BOOL usingUserDefaultValues;
// Default value uses the key "..." in NSUserDefaults or ... if the key does not exist. /*!
@property (nonatomic) NSUInteger maximumOperatorChainLength; // +--++-5 -> Chain length: 6, 2 default (sign of number and operator) (0 is invalid?) @property isUsingDegrees
@property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0 @abstract Specifies whether trigonometric functions use degree values.
@discussion If set to <code>NO</code> radians are used instead. The default
value is <code>NO</code>.
*/
@property (nonatomic) BOOL isUsingDegrees; @property (nonatomic) BOOL isUsingDegrees;
@end @end

View File

@@ -8,14 +8,16 @@
#import "MPMathRules.h" #import "MPMathRules.h"
NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey";
NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey";
NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey"; NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey";
@implementation MPMathRules @implementation MPMathRules
static MPMathRules *sharedRules; static MPMathRules *sharedRules;
+ (MPMathRules *)sharedRules + (MPMathRules *)sharedRules
{ {
if (!sharedRules) { if (!sharedRules) {
@@ -24,6 +26,7 @@ static MPMathRules *sharedRules;
return sharedRules; return sharedRules;
} }
- (instancetype)init - (instancetype)init
{ {
if (sharedRules) { if (sharedRules) {
@@ -31,53 +34,21 @@ static MPMathRules *sharedRules;
} }
self = [super init]; self = [super init];
if (self) { if (self) {
_usingUserDefaultValues = YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *userDefaultsMaximumOperatorChainLength = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthKey];
NSNumber *userDefaultsMaximumOperatorChainLengthInMultiplication = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
NSNumber *userDefaultsIsUsingDegrees = [userDefaults objectForKey:MPMathRulesIsUsingDegreesKey];
_maximumOperatorChainLength = userDefaultsMaximumOperatorChainLength != nil ? userDefaultsMaximumOperatorChainLength.unsignedIntegerValue : 2;
_maximumOperatorChainLengthInMultiplication = userDefaultsMaximumOperatorChainLengthInMultiplication != nil ? userDefaultsMaximumOperatorChainLengthInMultiplication.unsignedIntegerValue : 1;
_isUsingDegrees = userDefaultsIsUsingDegrees.boolValue;
} }
return self; return self;
} }
- (void)setUsingUserDefaultValues:(BOOL)usingUserDefaultValues
- (BOOL)isUsingDegrees
{ {
_usingUserDefaultValues = usingUserDefaultValues; return [[NSUserDefaults standardUserDefaults] boolForKey:MPMathRulesIsUsingDegreesKey];
// Save the current values
self.maximumOperatorChainLength = self.maximumOperatorChainLength;
self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication;
self.isUsingDegrees = self.isUsingDegrees;
} }
- (void)setMaximumOperatorChainLength:(NSUInteger)maximumOperatorChainLength
{
_maximumOperatorChainLength = maximumOperatorChainLength;
if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLength)
forKey:MPMathRulesMaximumOperatorChainLengthKey];
}
}
- (void)setMaximumOperatorChainLengthInMultiplication:(NSUInteger)maximumOperatorChainLengthInMultiplication
{
_maximumOperatorChainLengthInMultiplication = maximumOperatorChainLengthInMultiplication;
if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLengthInMultiplication)
forKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
}
}
- (void)setIsUsingDegrees:(BOOL)isUsingDegrees - (void)setIsUsingDegrees:(BOOL)isUsingDegrees
{ {
_isUsingDegrees = isUsingDegrees;
if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees [[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
forKey:MPMathRulesIsUsingDegreesKey]; forKey:MPMathRulesIsUsingDegreesKey];
} }
}
@end @end

View File

@@ -9,6 +9,12 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPNegatedTerm</code> class.
*/
@class MPNegatedTerm; @class MPNegatedTerm;
@@ -20,7 +26,6 @@
*/ */
@interface MPNegatedTerm : MPTerm @interface MPNegatedTerm : MPTerm
/*! /*!
@method initWithTerm: @method initWithTerm:
@abstract Initializes a new negated term. @abstract Initializes a new negated term.

View File

@@ -8,6 +8,8 @@
#import "MPNegatedTerm.h" #import "MPNegatedTerm.h"
@implementation MPNegatedTerm @implementation MPNegatedTerm
- (instancetype)initWithTerm:(MPTerm *)term - (instancetype)initWithTerm:(MPTerm *)term
@@ -20,6 +22,7 @@
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
NSDecimalNumber *value = [self.term evaluate:error]; NSDecimalNumber *value = [self.term evaluate:error];

View File

@@ -9,14 +9,44 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPNumber</code> class.
*/
@class MPNumber; @class MPNumber;
/*!
@class MPNumber
@abstract This class represents a number that evaluates to itself.
@discussion Numbers include integers as well as floating point numbers. They
have to be representable as a decimal number literal (e.g.
<code>3.4</code>). Numbers that have periods or are irrational
are not implemented by this class.
*/
@interface MPNumber : MPTerm @interface MPNumber : MPTerm
/*!
@method initWithNumber:
@abstract Initializes a number term with the specified <code>number</code>.
@param number
The number that the term should evaluate to. Must not be
<code>nil</code>.
@return A new <code>MPNumberTerm</code> instance.
*/
- (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */ - (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */
/*!
@property number
@abstract The receiver's number.
*/
@property (readonly, nonatomic, strong) NSDecimalNumber *number; @property (readonly, nonatomic, strong) NSDecimalNumber *number;
@end @end

View File

@@ -8,9 +8,7 @@
#import "MPNumber.h" #import "MPNumber.h"
#import "MPParsedExpression.h"
#import "MPToken.h"
@implementation MPNumber @implementation MPNumber
@@ -24,6 +22,7 @@
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
return self.number; return self.number;

View File

@@ -9,6 +9,12 @@
#import "MPFunction.h" #import "MPFunction.h"
/*!
@header
This file contains the <code>MPParenthesisFunction</code> class.
*/
@class MPParenthesisFunction, MPExpression; @class MPParenthesisFunction, MPExpression;

View File

@@ -18,16 +18,19 @@
MPFunctionAccessorImplementation(Expression, _expression) MPFunctionAccessorImplementation(Expression, _expression)
- (NSArray *)childrenAccessors - (NSArray *)childrenAccessors
{ {
return @[@"expression"]; return @[@"expression"];
} }
- (Class)functionTermClass - (Class)functionTermClass
{ {
return [MPParenthesisTerm class]; return [MPParenthesisTerm class];
} }
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"(%@)", self.expression.description]; return [NSString stringWithFormat:@"(%@)", self.expression.description];

View File

@@ -9,12 +9,32 @@
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
/*!
@header
This file contains the <code>MPParenthesisFunctionLayout</code> class.
*/
@class MPParenthesisFunctionLayout, MPParenthesisFunction; @class MPParenthesisFunctionLayout, MPParenthesisFunction;
/*!
@class MPParenthesisFunctionLayout
@abstract A parenthesis function layout displays a <code>@link
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
@discussion The child of the parenthesis function is encapsulated by standard
parenthesis that are scaled to the smae height as the child.
*/
@interface MPParenthesisFunctionLayout : MPFunctionLayout @interface MPParenthesisFunctionLayout : MPFunctionLayout
/*!
@method parenthesisFunction
@abstract Returns the <code>@link
//apple_ref/occ/cl/MPParenthesisFunction@/link</code> represented
by the receiver.
*/
- (MPParenthesisFunction *)parenthesisFunction; - (MPParenthesisFunction *)parenthesisFunction;
@end @end

View File

@@ -10,9 +10,12 @@
#import "MPParenthesisFunction.h" #import "MPParenthesisFunction.h"
#define MPParenthesisFunctionOpeningParensOffset 2 #define MPParenthesisFunctionOpeningParensOffset 2
#define MPParenthesisFunctionClosingParensOffset 0 #define MPParenthesisFunctionClosingParensOffset 0
@interface MPParenthesisFunctionLayout () @interface MPParenthesisFunctionLayout ()
- (NSBezierPath *)openingParens; - (NSBezierPath *)openingParens;
@@ -25,6 +28,8 @@
@end @end
@implementation MPParenthesisFunctionLayout @implementation MPParenthesisFunctionLayout
- (MPParenthesisFunction *)parenthesisFunction - (MPParenthesisFunction *)parenthesisFunction
@@ -32,6 +37,7 @@
return (MPParenthesisFunction *)self.function; return (MPParenthesisFunction *)self.function;
} }
- (NSBezierPath *)openingParens - (NSBezierPath *)openingParens
{ {
NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{ NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{
@@ -60,6 +66,7 @@
return parens; return parens;
} }
- (NSBezierPath *)closingParens - (NSBezierPath *)closingParens
{ {
NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{ NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{
@@ -88,6 +95,7 @@
return parens; return parens;
} }
- (NSBezierPath *)transformedOpeningParens - (NSBezierPath *)transformedOpeningParens
{ {
NSBezierPath *parens = self.openingParens.copy; NSBezierPath *parens = self.openingParens.copy;
@@ -95,6 +103,7 @@
return parens; return parens;
} }
- (NSBezierPath *)transformedClosingParens - (NSBezierPath *)transformedClosingParens
{ {
NSBezierPath *parens = self.closingParens.copy; NSBezierPath *parens = self.closingParens.copy;
@@ -102,6 +111,7 @@
return parens; return parens;
} }
- (NSAffineTransform *)parenthesisTransform - (NSAffineTransform *)parenthesisTransform
{ {
NSAffineTransform *transform = [NSAffineTransform transform]; NSAffineTransform *transform = [NSAffineTransform transform];
@@ -110,6 +120,7 @@
return transform; return transform;
} }
- (CGFloat)scaleFactor - (CGFloat)scaleFactor
{ {
NSRect parensBounds = self.openingParens.bounds; NSRect parensBounds = self.openingParens.bounds;
@@ -122,22 +133,25 @@
return expressionHeight / parensHeight; return expressionHeight / parensHeight;
} }
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{ {
return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0); return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0);
} }
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{ {
#warning Missing Implementation
return [NSIndexPath indexPathWithIndex:0]; return [NSIndexPath indexPathWithIndex:0];
} }
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSetWithIndex:0]; return [NSIndexSet indexSetWithIndex:0];
} }
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
NSRect openingParensBounds = self.transformedOpeningParens.bounds; NSRect openingParensBounds = self.transformedOpeningParens.bounds;
@@ -159,6 +173,7 @@
return bounds; return bounds;
} }
- (void)draw - (void)draw
{ {
NSBezierPath *openingParens = self.transformedOpeningParens; NSBezierPath *openingParens = self.transformedOpeningParens;

View File

@@ -9,10 +9,24 @@
#import "MPFunctionTerm.h" #import "MPFunctionTerm.h"
/*!
@header
This file contains the <code>MPParenthesisTerm</code> class.
*/
@class MPParenthesisTerm; @class MPParenthesisTerm;
/*!
@class MPParenthesisTerm
@abstract Represents a <code>@link
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
@discussion A parenthesis function encapsulates a term and thus prioritizes
it in evaluation.
*/
@interface MPParenthesisTerm : MPFunctionTerm @interface MPParenthesisTerm : MPFunctionTerm
@end @end

View File

@@ -10,6 +10,8 @@
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
@implementation MPParenthesisTerm @implementation MPParenthesisTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error

View File

@@ -7,8 +7,10 @@
// //
/*!
@class MPParsedExpression, MPTerm; @header
This file contains the <code>MPParsedExpression</code> class.
*/
/*! /*!
@@ -50,6 +52,10 @@ FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
FOUNDATION_EXPORT NSString *const MPErrorRangeKey; FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
@class MPParsedExpression, MPTerm;
/*! /*!
@class MPParsedExpression @class MPParsedExpression
@abstract A parsed expression represents an expression whose syntax is @abstract A parsed expression represents an expression whose syntax is
@@ -93,7 +99,7 @@ FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
@discussion This method is a convenience method for evaluating the receiver's @discussion This method is a convenience method for evaluating the receiver's
<code>@link <code>@link
//apple_ref/occ/intfp/MPParsedExpression/term@/link</code>. //apple_ref/occ/instp/MPParsedExpression/term@/link</code>.
@param error @param error
If an error occured during evaluation it will be returned If an error occured during evaluation it will be returned

View File

@@ -9,13 +9,15 @@
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
#import "MPToken.h" #import "MPToken.h"
#import "MPTerm.h" #import "MPTerm.h"
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain"; NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey"; NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
NSString *const MPErrorRangeKey = @"MPErrorRangeKey"; NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
@implementation MPParsedExpression @implementation MPParsedExpression
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error - (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error

View File

@@ -9,6 +9,12 @@
#import "MPFunction.h" #import "MPFunction.h"
/*!
@header
This file contains the <code>MPPowerFunction</code> class.
*/
@class MPPowerFunction, MPExpression; @class MPPowerFunction, MPExpression;

View File

@@ -10,15 +10,19 @@
#import "MPExpression.h" #import "MPExpression.h"
@implementation MPPowerFunction @implementation MPPowerFunction
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression) MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
- (NSArray *)childrenAccessors - (NSArray *)childrenAccessors
{ {
return @[@"exponentExpression"]; return @[@"exponentExpression"];
} }
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"^%@", self.exponentExpression.description]; return [NSString stringWithFormat:@"^%@", self.exponentExpression.description];

View File

@@ -9,14 +9,46 @@
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
/*!
@header
This file contains the <code>MPPowerFunctionLayout</code> class.
*/
@class MPPowerFunctionLayout, MPPowerFunction; @class MPPowerFunctionLayout, MPPowerFunction;
/*!
@class MPPowerFunctionLayout
@abstract A power function layout displays a <code>@link
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
@discussion A power function layout draws its child at the top right of the
base. Because of this the power function layout has a special
property that gets set during the drawing of an expression:
<code>@link
//apple_ref/occ/instp/MPPowerFunctionLayout/baseBounds@/link</code>.
*/
@interface MPPowerFunctionLayout : MPFunctionLayout @interface MPPowerFunctionLayout : MPFunctionLayout
/*!
@property baseBounds
@abstract The bounds of the expression that is the base of the receiving
power function layout.
@discussion This value should be considered very volatile. It may change even
if the power function itself didn't. This value is guaranteed to
stay the same only during a single cycle of drawing.
*/
@property (nonatomic) NSRect baseBounds; @property (nonatomic) NSRect baseBounds;
/*!
@method powerFunction
@abstract Returns the <code>@link
//apple_ref/occ/cl/MPPowerFunction@/link</code> represented by
the receiver.
*/
- (MPPowerFunction *)powerFunction; - (MPPowerFunction *)powerFunction;
@end @end

View File

@@ -10,9 +10,12 @@
#import "MPPowerFunction.h" #import "MPPowerFunction.h"
#define kPowerFunctionExponentXOffset 1 #define kPowerFunctionExponentXOffset 1
#define kPowerFunctionTrailingOffset 2 #define kPowerFunctionTrailingOffset 2
@implementation MPPowerFunctionLayout @implementation MPPowerFunctionLayout
- (NSRect)baseBounds - (NSRect)baseBounds
@@ -23,16 +26,19 @@
return _baseBounds; return _baseBounds;
} }
- (MPPowerFunction *)powerFunction - (MPPowerFunction *)powerFunction
{ {
return (MPPowerFunction *)self.function; return (MPPowerFunction *)self.function;
} }
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSetWithIndex:0]; return [NSIndexSet indexSetWithIndex:0];
} }
#warning Broken Power Layout #warning Broken Power Layout
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{ {
@@ -41,16 +47,19 @@
return NSMakePoint(kPowerFunctionExponentXOffset, y); return NSMakePoint(kPowerFunctionExponentXOffset, y);
} }
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index - (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{ {
return YES; return YES;
} }
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{ {
return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0]; return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
} }
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds; NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
@@ -59,9 +68,8 @@
return NSMakeRect(0, 0, width, height); return NSMakeRect(0, 0, width, height);
} }
- (void)draw - (void)draw
{ {}
[[NSBezierPath bezierPathWithRect:self.bounds] stroke];
}
@end @end

View File

@@ -9,12 +9,33 @@
#import "MPFunctionTerm.h" #import "MPFunctionTerm.h"
/*!
@header
This file contains the <code>MPPowerTerm</code> class.
*/
@class MPPowerTerm, MPTerm; @class MPPowerTerm, MPTerm;
/*!
@class MPPowerTerm
@abstract Represents a <code>@link
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
@discussion A power function is evaluated using the C <code>pow</code>
function.
*/
@interface MPPowerTerm : MPFunctionTerm @interface MPPowerTerm : MPFunctionTerm
/*!
@property baseTerm
@abstract The base of the power.
@discussion This value is set during the evaluation of the power term and
should be considered very volatile.
*/
@property (nonatomic, strong) MPTerm *baseTerm; @property (nonatomic, strong) MPTerm *baseTerm;
@end @end

View File

@@ -7,8 +7,11 @@
// //
#import "MPPowerTerm.h" #import "MPPowerTerm.h"
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
@implementation MPPowerTerm @implementation MPPowerTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error

View File

@@ -9,6 +9,12 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPProductTerm</code> class.
*/
@class MPProductTerm; @class MPProductTerm;
@@ -22,7 +28,6 @@
*/ */
@interface MPProductTerm : MPTerm @interface MPProductTerm : MPTerm
/*! /*!
@method initWithFactors: @method initWithFactors:
@abstract Initializes a new product term with the specified @abstract Initializes a new product term with the specified

View File

@@ -8,17 +8,7 @@
#import "MPProductTerm.h" #import "MPProductTerm.h"
#import "MPParsedExpression.h"
#import "MPPowerFunction.h"
#import "MPFunctionTerm.h"
#import "MPElementaryFunctionTerm.h"
#import "MPNumber.h"
#import "MPFactorialTerm.h"
#import "MPPowerTerm.h"
#import "MPVariable.h"
#import "MPToken.h"
@implementation MPProductTerm @implementation MPProductTerm
@@ -33,6 +23,7 @@
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
NSDecimalNumber *value = [NSDecimalNumber one]; NSDecimalNumber *value = [NSDecimalNumber one];

View File

@@ -11,6 +11,8 @@
/*! /*!
@header @header
This file contains the <code>MPRangePath</code> class.
The <code>MPRangePath</code> combines an <code>NSIndexPath</code> with a The <code>MPRangePath</code> combines an <code>NSIndexPath</code> with a
<code>NSRange</code>. A range path is used in a tree structure to identify a <code>NSRange</code>. A range path is used in a tree structure to identify a
number of subsequent nodes. This is achieved by combining a range path that number of subsequent nodes. This is achieved by combining a range path that
@@ -36,6 +38,7 @@
#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)] #define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]
@class MPRangePath, MPExpression; @class MPRangePath, MPExpression;
@@ -268,7 +271,6 @@
@interface MPExpression (MPRangeExtension) @interface MPExpression (MPRangeExtension)
/*! /*!
@method subexpressionWithRangePath: @method subexpressionWithRangePath:
@abstract Creates a new expression with the symbols in the specified range @abstract Creates a new expression with the symbols in the specified range

View File

@@ -12,7 +12,6 @@
@implementation MPRangePath @implementation MPRangePath
#pragma mark Creation Methods #pragma mark Creation Methods

View File

@@ -9,6 +9,12 @@
#import "MPFunction.h" #import "MPFunction.h"
/*!
@header
This file contains the <code>MPSumFunction</code> class.
*/
@class MPSumFunction, MPExpression; @class MPSumFunction, MPExpression;

View File

@@ -15,7 +15,6 @@
@implementation MPSumFunction @implementation MPSumFunction
MPFunctionAccessorImplementation(StartExpression, _startExpression) MPFunctionAccessorImplementation(StartExpression, _startExpression)
MPFunctionAccessorImplementation(TargetExpression, _targetExpression) MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
MPFunctionAccessorImplementation(SumExpression, _sumExpression) MPFunctionAccessorImplementation(SumExpression, _sumExpression)
@@ -26,11 +25,13 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
return @[@"startExpression", @"targetExpression", @"sumExpression"]; return @[@"startExpression", @"targetExpression", @"sumExpression"];
} }
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index - (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index
{ {
return index == 0; return index == 0;
} }
- (Class)functionTermClass - (Class)functionTermClass
{ {
return [MPSumFunctionTerm class]; return [MPSumFunctionTerm class];

View File

@@ -9,12 +9,33 @@
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
/*!
@header
This file contains the <code>MPSumFunctionLayout</code> class.
*/
@class MPSumFunctionLayout, MPSumFunction; @class MPSumFunctionLayout, MPSumFunction;
/*!
@class MPSumFunctionLayout
@abstract A sum function layout displays a <code>@link
//apple_ref/occ/cl/MPSumFunction@/link</code>.
@discussion A sum is drawn using a capital greeg sigma (∑) The three children
are placed around it: The start expression below, target
expression above and sum expression to the right of it.
*/
@interface MPSumFunctionLayout : MPFunctionLayout @interface MPSumFunctionLayout : MPFunctionLayout
/*!
@method sumFunction
@abstract Returns the <code>@link
//apple_ref/occ/cl/MPSumFunction@/link</code> represented by the
receiver.
*/
- (MPSumFunction *)sumFunction; - (MPSumFunction *)sumFunction;
@end @end

View File

@@ -9,14 +9,16 @@
#import "MPSumFunctionLayout.h" #import "MPSumFunctionLayout.h"
#import "MPSumFunction.h" #import "MPSumFunction.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
#define kSumFunctionStartExpressionOffset 0 #define kSumFunctionStartExpressionOffset 0
#define kSumFunctionTargetExpressionOffset 0 #define kSumFunctionTargetExpressionOffset 0
#define kSumFunctionSumExpressionOffset 3 #define kSumFunctionSumExpressionOffset 3
#define kSumFunctionTrailingOffset 5 #define kSumFunctionTrailingOffset 5
@implementation MPSumFunctionLayout @implementation MPSumFunctionLayout
- (MPSumFunction *)sumFunction - (MPSumFunction *)sumFunction
@@ -24,16 +26,19 @@
return (MPSumFunction *)self.function; return (MPSumFunction *)self.function;
} }
- (NSUInteger)indexOfLeadingChild - (NSUInteger)indexOfLeadingChild
{ {
return 2; return 2;
} }
- (NSUInteger)indexOfTrailingChild - (NSUInteger)indexOfTrailingChild
{ {
return 2; return 2;
} }
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{ {
if (index != 2) { if (index != 2) {
@@ -42,21 +47,25 @@
return [super indexOfChildAfterChildAtIndex:index]; return [super indexOfChildAfterChildAtIndex:index];
} }
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{ {
return 0; return 0;
} }
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{ {
return 1; return 1;
} }
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSetWithIndex:2]; return [NSIndexSet indexSetWithIndex:2];
} }
- (CTLineRef)line - (CTLineRef)line
{ {
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{ CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
@@ -67,6 +76,7 @@
return line; return line;
} }
- (NSRect)localLineBounds - (NSRect)localLineBounds
{ {
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0); NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
@@ -77,6 +87,7 @@
return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height); return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height);
} }
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{ {
NSRect childBounds = [self childLayoutAtIndex:index].bounds; NSRect childBounds = [self childLayoutAtIndex:index].bounds;
@@ -101,11 +112,13 @@
return offset; return offset;
} }
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index - (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{ {
return (index == 0 || index == 1) ? YES : self.usesSmallSize; return (index == 0 || index == 1) ? YES : self.usesSmallSize;
} }
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{ {
if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) { if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) {
@@ -115,6 +128,7 @@
} }
} }
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0); NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
@@ -139,6 +153,7 @@
return bounds; return bounds;
} }
- (void)draw - (void)draw
{ {
// Get the current context // Get the current context

View File

@@ -8,11 +8,30 @@
#import "MPFunctionTerm.h" #import "MPFunctionTerm.h"
/*!
@header
This file contains the <code>MPSumFunctionTerm</code> class.
*/
@class MPSumFunctionTerm; @class MPSumFunctionTerm;
/*!
@class MPSumFunctionTerm
@abstract Represents and evaluates a <code>@link
//apple_ref/occ/cl/MPSumFunction@/link</code>.
@discussion A sum function evaluates its sum term <code>n</code> times. How
often it actually is evaluated depends on the boundary
expressions (start and target). Both are inclusive.
A sum function also features a variable that contains the current
value of the iteration. The variable is defined in the start
expression and incremented by <code>1</code> after every
iteration.
*/
@interface MPSumFunctionTerm : MPFunctionTerm @interface MPSumFunctionTerm : MPFunctionTerm
@end @end

View File

@@ -10,6 +10,8 @@
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
@implementation MPSumFunctionTerm @implementation MPSumFunctionTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error

View File

@@ -9,6 +9,12 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPSumTerm</code> class.
*/
@class MPSumTerm; @class MPSumTerm;

View File

@@ -8,10 +8,7 @@
#import "MPSumTerm.h" #import "MPSumTerm.h"
#import "MPParsedExpression.h"
#import "MPProductTerm.h"
#import "MPToken.h"
@implementation MPSumTerm @implementation MPSumTerm
@@ -26,6 +23,7 @@
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
NSDecimalNumber *value = [NSDecimalNumber zero]; NSDecimalNumber *value = [NSDecimalNumber zero];

View File

@@ -9,6 +9,8 @@
/*! /*!
@header @header
This file contains the <code>MPTerm</code> class.
The <code>MPTerm</code> class is used to evaluate a mathematical expression. A The <code>MPTerm</code> class is used to evaluate a mathematical expression. A
term is a purely mathematical representation of a part of an expression. A term term is a purely mathematical representation of a part of an expression. A term
can be defined with three rules: can be defined with three rules:
@@ -66,6 +68,7 @@
#define ReturnNilIfNil(test) if (test == nil) return nil #define ReturnNilIfNil(test) if (test == nil) return nil
@class MPTerm; @class MPTerm;

View File

@@ -9,9 +9,10 @@
#import "MPTerm.h" #import "MPTerm.h"
#import "MPParsedExpression.h" #import "MPParsedExpression.h"
#import "MPSumTerm.h"
#import "MPEvaluationContext.h" #import "MPEvaluationContext.h"
@implementation MPTerm @implementation MPTerm
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error - (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
@@ -22,6 +23,7 @@
return result; return result;
} }
- (BOOL)defineVariable:(NSString *)variableName - (BOOL)defineVariable:(NSString *)variableName
value:(NSDecimalNumber *)value value:(NSDecimalNumber *)value
error:(NSError *__autoreleasing *)error error:(NSError *__autoreleasing *)error
@@ -39,6 +41,7 @@
return couldDefineVariable; return couldDefineVariable;
} }
- (NSDecimalNumber *)valueForVariable:(NSString *)variableName - (NSDecimalNumber *)valueForVariable:(NSString *)variableName
error:(NSError *__autoreleasing *)error error:(NSError *__autoreleasing *)error
{ {
@@ -54,6 +57,7 @@
return value; return value;
} }
- (void)undefineVariable:(NSString *)variableName - (void)undefineVariable:(NSString *)variableName
{ {
[[MPEvaluationContext sharedContext] undefineVariable:variableName]; [[MPEvaluationContext sharedContext] undefineVariable:variableName];

View File

@@ -9,6 +9,8 @@
/*! /*!
@header @header
This file contains the <code>MPToken</code> class and protocol.
One way to represent a mathematical expression using the <code>@link One way to represent a mathematical expression using the <code>@link
MPExpression@/link</code> class is a sequence of tokens. A token is a logical MPExpression@/link</code> class is a sequence of tokens. A token is a logical
unit of input. The different types of units are identified by a @link unit of input. The different types of units are identified by a @link
@@ -30,10 +32,6 @@ while ((token = [self nextToken]).tokenType != MPEOFToken) {
*/ */
@class MPToken;
@protocol MPToken;
/*! /*!
@typedef MPTokenType @typedef MPTokenType
@abstract The type of a token identifies its behaviour in a mathematical @abstract The type of a token identifies its behaviour in a mathematical
@@ -103,6 +101,10 @@ typedef enum {
@class MPToken;
@protocol MPToken;
/*! /*!
@protocol MPToken @protocol MPToken
@abstract Tokens represent logical units in an expresion. @abstract Tokens represent logical units in an expresion.

View File

@@ -9,14 +9,44 @@
#import "MPTerm.h" #import "MPTerm.h"
/*!
@header
This file contains the <code>MPVariable</code> class.
*/
@class MPVariable; @class MPVariable;
/*!
@class MPVariable
@abstract This class represents a variable.
@discussion Variables are evaluated in the <code>@link
//apple_ref/occ/cl/MPEvaluationContext@/link</code> and generate
errors if they are not defined.
*/
@interface MPVariable : MPTerm @interface MPVariable : MPTerm
/*!
@method initWithVariableName:
@abstract Initializes a <code>MPVariable</code> with the specified
<code>variableName</code>
@param variableName
The name of the variable. Must not be <code>nil</code> and must
be at least one character long.
@return A new <code>MPVariable</code> instance.
*/
- (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */ - (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */
/*!
@property variableName
@abstract The receiver's variable name.
*/
@property (readonly, nonatomic, strong) NSString *variableName; @property (readonly, nonatomic, strong) NSString *variableName;
@end @end

View File

@@ -8,12 +8,7 @@
#import "MPVariable.h" #import "MPVariable.h"
#import "MPParsedExpression.h"
#import "MPToken.h"
#import "MPExpression.h"
#import "MPEvaluationContext.h"
@implementation MPVariable @implementation MPVariable
@@ -22,11 +17,13 @@
self = [super init]; self = [super init];
if (self) { if (self) {
NSAssert(variableName != nil, @"variableName must not be nil."); NSAssert(variableName != nil, @"variableName must not be nil.");
NSAssert(variableName.length > 0, @"variableName must be at least one character long.");
_variableName = variableName; _variableName = variableName;
} }
return self; return self;
} }
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error - (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{ {
return [self valueForVariable:self.variableName return [self valueForVariable:self.variableName

View File

@@ -7,10 +7,20 @@
// //
/*!
@header
This file contains the <code>MPWhiteView</code> class.
*/
@class MPWhiteView; @class MPWhiteView;
/*!
@class MPWhiteView
@abstract A <code>NSView</code> that draws a white square in its frame.
*/
@interface MPWhiteView : NSView @interface MPWhiteView : NSView
@end @end

View File

@@ -6,20 +6,19 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import "MPExpression.h"
#import "MPExpression.h" #import "MPExpression.h"
#import "NSString+MPExpressionElement.h" #import "NSString+MPExpressionElement.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPSumFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
#import "MPFractionFunction.h"
#import "MPToken.h" #import "MPToken.h"
#import "MPFunction+MPToken.h" #import "MPFunction+MPToken.h"
#import "MPFractionFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
#import "MPSumFunction.h"
#import "MPParsedExpression.h"
#import "MPTerm.h"
#import "MPEvaluationContext.h" #import "MPEvaluationContext.h"
#import "MPMathRules.h" #import "MPMathRules.h"
@@ -27,5 +26,13 @@
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
#import "NSRegularExpression+MPParsingAdditions.h" #import "NSRegularExpression+MPParsingAdditions.h"
#import "MPExpressionView.h"
#import "MPExpressionStorage.h" #import "MPExpressionStorage.h"
#import "MPExpressionView.h"
#import "MPLayout.h"
#import "MPExpressionLayout.h"
#import "MPFunctionLayout.h"
#import "MPFractionFunctionLayout.h"
#import "MPParenthesisFunctionLayout.h"
#import "MPPowerFunctionLayout.h"
#import "MPSumFunctionLayout.h"

View File

@@ -7,6 +7,12 @@
// //
/*!
@header
This file contains the <code>NSIndexPath(MPAdditions)</code> category.
*/
/*! /*!
@category NSIndexPath (MPAdditions) @category NSIndexPath (MPAdditions)
@@ -15,7 +21,6 @@
*/ */
@interface NSIndexPath (MPAdditions) @interface NSIndexPath (MPAdditions)
/*! /*!
@property firstIndex @property firstIndex
@abstract The first index from the receiver. @abstract The first index from the receiver.

View File

@@ -12,7 +12,6 @@
@implementation NSIndexPath (MPAdditions) @implementation NSIndexPath (MPAdditions)
- (NSUInteger)firstIndex - (NSUInteger)firstIndex
{ {
return [self indexAtPosition:0]; return [self indexAtPosition:0];

View File

@@ -7,6 +7,13 @@
// //
/*!
@header
This file contains the <code>NSRegularExpression(MPParsingAdditions)</code>
category.
*/
/*! /*!
@category NSRegularExpression (MPParsingAdditions) @category NSRegularExpression (MPParsingAdditions)
@@ -15,7 +22,6 @@
*/ */
@interface NSRegularExpression (MPParsingAdditions) @interface NSRegularExpression (MPParsingAdditions)
/*! /*!
@method firstMathInString: @method firstMathInString:
@abstract Returns the first match of the regular expression within the @abstract Returns the first match of the regular expression within the

View File

@@ -9,11 +9,18 @@
#import "MPExpression.h" #import "MPExpression.h"
/*!
@header
This file contains the <code>NSString(MPExpressionElement)</code> category.
*/
/*! /*!
@category NSString (MPExpressionElement) @category NSString (MPExpressionElement)
@abstract This category adds <code>@link MPExpressionElement@/link</code> @abstract This category adds <code>@link
protocol conformance to the <code>NSString</code> class. //apple_ref/occ/intf/MPExpressionElement@/link</code> protocol
conformance to the <code>NSString</code> class.
*/ */
@interface NSString (MPExpressionElement) <MPExpressionElement> @interface NSString (MPExpressionElement) <MPExpressionElement>

View File

@@ -253,8 +253,6 @@
3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = "<group>"; }; 3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = "<group>"; };
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = "<group>"; }; 3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = "<group>"; };
3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = "<group>"; };
3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = "<group>"; };
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
3BF9976E18DE623E009CF6C4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
3BF9977118DE623E009CF6C4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 3BF9977118DE623E009CF6C4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -460,15 +458,6 @@
name = "Function Layouts"; name = "Function Layouts";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3BC4661819B3692A0033F13A /* Temp */ = {
isa = PBXGroup;
children = (
3BC4661519B365070033F13A /* MPArrayCache.h */,
3BC4661619B365070033F13A /* MPArrayCache.m */,
);
name = Temp;
sourceTree = "<group>";
};
3BC46B4B19B38CB60033F13A /* Base Expression Classes */ = { 3BC46B4B19B38CB60033F13A /* Base Expression Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -510,22 +499,22 @@
3B6338671A3A4C2B00698BFB /* MPParsedExpression.m */, 3B6338671A3A4C2B00698BFB /* MPParsedExpression.m */,
3B63387A1A3A4C7E00698BFB /* MPTerm.h */, 3B63387A1A3A4C7E00698BFB /* MPTerm.h */,
3B63387B1A3A4C7E00698BFB /* MPTerm.m */, 3B63387B1A3A4C7E00698BFB /* MPTerm.m */,
3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
3B6338781A3A4C7E00698BFB /* MPSumTerm.h */, 3B6338781A3A4C7E00698BFB /* MPSumTerm.h */,
3B6338791A3A4C7E00698BFB /* MPSumTerm.m */, 3B6338791A3A4C7E00698BFB /* MPSumTerm.m */,
3B63386A1A3A4C2B00698BFB /* MPProductTerm.h */, 3B63386A1A3A4C2B00698BFB /* MPProductTerm.h */,
3B63386B1A3A4C2B00698BFB /* MPProductTerm.m */, 3B63386B1A3A4C2B00698BFB /* MPProductTerm.m */,
3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */, 3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */, 3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */,
3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */,
3B6338881A3A4CAF00698BFB /* MPElementaryFunctionTerm.h */, 3B6338881A3A4CAF00698BFB /* MPElementaryFunctionTerm.h */,
3B6338891A3A4CAF00698BFB /* MPElementaryFunctionTerm.m */, 3B6338891A3A4CAF00698BFB /* MPElementaryFunctionTerm.m */,
3B6338621A3A4C2B00698BFB /* MPNumber.h */, 3B6338621A3A4C2B00698BFB /* MPNumber.h */,
3B6338631A3A4C2B00698BFB /* MPNumber.m */, 3B6338631A3A4C2B00698BFB /* MPNumber.m */,
3B63387C1A3A4C7E00698BFB /* MPVariable.h */, 3B63387C1A3A4C7E00698BFB /* MPVariable.h */,
3B63387D1A3A4C7E00698BFB /* MPVariable.m */, 3B63387D1A3A4C7E00698BFB /* MPVariable.m */,
3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */, 3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */,
3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */, 3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */,
3B6338901A3A4CCA00698BFB /* MPEvaluationContext.h */, 3B6338901A3A4CCA00698BFB /* MPEvaluationContext.h */,
3B6338911A3A4CCA00698BFB /* MPEvaluationContext.m */, 3B6338911A3A4CCA00698BFB /* MPEvaluationContext.m */,
3B6338941A3A4CD100698BFB /* MPMathRules.h */, 3B6338941A3A4CD100698BFB /* MPMathRules.h */,
@@ -538,14 +527,14 @@
3BEFF75F1A17FF5C00301C0C /* Functions */ = { 3BEFF75F1A17FF5C00301C0C /* Functions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */, 3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */,
3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */, 3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */,
3B6338641A3A4C2B00698BFB /* MPParenthesisTerm.h */, 3B6338641A3A4C2B00698BFB /* MPParenthesisTerm.h */,
3B6338651A3A4C2B00698BFB /* MPParenthesisTerm.m */, 3B6338651A3A4C2B00698BFB /* MPParenthesisTerm.m */,
3B6338681A3A4C2B00698BFB /* MPPowerTerm.h */, 3B6338681A3A4C2B00698BFB /* MPPowerTerm.h */,
3B6338691A3A4C2B00698BFB /* MPPowerTerm.m */, 3B6338691A3A4C2B00698BFB /* MPPowerTerm.m */,
3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */, 3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */,
3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */, 3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */,
); );
name = Functions; name = Functions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -598,7 +587,6 @@
3BF9977418DE623E009CF6C4 /* MathPad */ = { 3BF9977418DE623E009CF6C4 /* MathPad */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3BC4661819B3692A0033F13A /* Temp */,
3BF9978018DE623E009CF6C4 /* MPDocument.h */, 3BF9978018DE623E009CF6C4 /* MPDocument.h */,
3BF9978118DE623E009CF6C4 /* MPDocument.m */, 3BF9978118DE623E009CF6C4 /* MPDocument.m */,
3B87E353190082E200259938 /* Resources */, 3B87E353190082E200259938 /* Resources */,

View File

@@ -1,34 +0,0 @@
//
// MPArrayCache.h
// MathPad
//
// Created by Kim Wittenburg on 31.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import <Foundation/Foundation.h>
// A wrapper around NSCache to support index-based caches. Indexes are
// automatically adjusted to represent array-like behaviours.
@interface MPArrayCache : NSObject {
@private
NSCache *_cache;
}
- (id)init; /* designated initializer */
- (void)cacheObject:(id)object
forIndex:(NSUInteger)index;
- (id)cachedObjectForIndex:(NSUInteger)index;
- (void)clearCacheAtIndex:(NSUInteger)index
replacementLength:(NSUInteger)replacementLength;
- (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength;
- (void)replaceCachedObjectAtIndex:(NSUInteger)index
withObjects:(NSArray *)objects;
- (void)replaceCachedObjectsInRange:(NSRange)range
withObjects:(NSArray *)objects;
@end

View File

@@ -1,59 +0,0 @@
//
// MPArrayCache.m
// MathPad
//
// Created by Kim Wittenburg on 31.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPArrayCache.h"
@implementation MPArrayCache {
NSUInteger lastIndex;
}
- (instancetype)init
{
self = [super init];
if (self) {
lastIndex = 0;
_cache = [[NSCache alloc] init];
}
return self;
}
- (void)cacheObject:(id)object
forIndex:(NSUInteger)index
{
[_cache setObject:object
forKey:@(index)];
if (index > lastIndex) {
lastIndex = index;
}
}
- (id)cachedObjectForIndex:(NSUInteger)index
{
return [_cache objectForKey:@(index)];
}
- (void)clearCacheAtIndex:(NSUInteger)index replacementLength:(NSUInteger)replacementLength
{
[_cache removeObjectForKey:@(index)];
for (NSUInteger i = index+1; i < lastIndex; i++) {
id object = [_cache objectForKey:@(i)];
if (object) {
[_cache removeObjectForKey:@(i)];
}
}
lastIndex += replacementLength - 1;
}
- (void)clearCacheInRange:(NSRange)range replacementLength:(NSUInteger)replacementLength;
- (void)replaceCachedObjectAtIndex:(NSUInteger)index
withObjects:(NSArray *)objects;
- (void)replaceCachedObjectsInRange:(NSRange)range
withObjects:(NSArray *)objects;
@end

View File

@@ -8,11 +8,39 @@
#import <MathKit/MathKit.h> #import <MathKit/MathKit.h>
/*!
@class MPDocument
@abstract This class is the document class that displays the MathPad user
interface.
*/
@interface MPDocument : NSDocument @interface MPDocument : NSDocument
/*!
@property expressionView
@abstract The main expression view.
*/
@property (weak) IBOutlet MPExpressionView *expressionView; @property (weak) IBOutlet MPExpressionView *expressionView;
/*!
@property resultLabel
@abstract The label which displays the result of the calculation.
@discussion The label is placed inside the expression view.
*/
@property (weak) IBOutlet NSTextField *resultLabel; @property (weak) IBOutlet NSTextField *resultLabel;
/*!
@method evaluateExpression:
@abstract Called by the expression view when it should be evaluated.
@param sender
Typically the object that invoked the method.
*/
- (IBAction)evaluateExpression:(id)sender; - (IBAction)evaluateExpression:(id)sender;
@end @end

View File

@@ -8,9 +8,11 @@
#import "MPDocument.h" #import "MPDocument.h"
#import "MPParsedExpression.h"
@implementation MPDocument
@implementation MPDocument {
MPExpression *loadedExpression;
}
- (id)init - (id)init
{ {
@@ -21,46 +23,47 @@
return self; return self;
} }
- (NSString *)windowNibName - (NSString *)windowNibName
{ {
// Override returning the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
return @"MPDocument"; return @"MPDocument";
} }
- (void)windowControllerDidLoadNib:(NSWindowController *)aController - (void)windowControllerDidLoadNib:(NSWindowController *)aController
{ {
[super windowControllerDidLoadNib:aController]; [super windowControllerDidLoadNib:aController];
self.expressionView.target = self; self.expressionView.target = self;
self.expressionView.action = @selector(evaluateExpression:); self.expressionView.action = @selector(evaluateExpression:);
// Add any code here that needs to be executed once the windowController has loaded the document's window. if (loadedExpression) {
[self.expressionView.expressionStorage appendElements:[loadedExpression allItemsInReferenceFrame:MPElementReferenceFrame]];
} }
}
+ (BOOL)autosavesInPlace + (BOOL)autosavesInPlace
{ {
return YES; return YES;
} }
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{ {
// Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil. return [NSKeyedArchiver archivedDataWithRootObject:self.expressionView.expressionStorage];
// You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
@throw exception;
return nil;
} }
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{ {
// Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO. MPExpression *expression = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead. loadedExpression = expression;
// If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
@throw exception;
return YES; return YES;
} }
#pragma mark Actions #pragma mark Actions
- (IBAction)evaluateExpression:(id)sender { - (IBAction)evaluateExpression:(id)sender {
NSArray *errors; NSArray *errors;
MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors]; MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors];
@@ -74,4 +77,5 @@
self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @""; self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @"";
} }
@end @end