diff --git a/MathKit/MPElementaryFunctionTerm.h b/MathKit/MPElementaryFunctionTerm.h
index 8921f78..bd9825c 100644
--- a/MathKit/MPElementaryFunctionTerm.h
+++ b/MathKit/MPElementaryFunctionTerm.h
@@ -9,15 +9,63 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPElementaryFunction class.
+ */
+
+
@class MPElementaryFunctionTerm;
+/*!
+ @class MPElementaryFunctionTerm
+ @abstract A MPElementaryFunction implements various
+ mathematical functions that have a valid text representation.
+ */
@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 "sin", "cos", "tan",
+ "asin", "arcsin", "acos",
+ "arccos", "atan",
+ "arctan", "log", "lg" and
+ "ln".
+
+ @param term
+ The term that should be evaluated using function.
+
+ @return A new MPElementaryFunctionTerm instance.
+ */
- (instancetype)initWithFunctionIdentifier:(NSString *)function
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. "sin" 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
+ term may be converted from radians into degrees.
+ */
@property (readonly, nonatomic, strong) MPTerm *term;
@end
\ No newline at end of file
diff --git a/MathKit/MPElementaryFunctionTerm.m b/MathKit/MPElementaryFunctionTerm.m
index d95bdf4..b492228 100644
--- a/MathKit/MPElementaryFunctionTerm.m
+++ b/MathKit/MPElementaryFunctionTerm.m
@@ -9,13 +9,10 @@
#import "MPElementaryFunctionTerm.h"
#import "MPParsedExpression.h"
-#import "MPPowerFunction.h"
-#import "MPProductTerm.h"
-#import "MPToken.h"
-
-#import "MPExpression.h"
#import "MPMathRules.h"
+
+
@implementation MPElementaryFunctionTerm {
NSDecimalNumber *(^_function)(NSDecimalNumber *);
}
@@ -53,16 +50,18 @@
} else if ([function isEqualToString:@"ln"]) {
func = &log;
} 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
takesArcValue:takesArc
returnsArcValue:returnsArc];
_term = term;
+ _functionIdentifier = function.copy;
}
return self;
}
+
- (void)setFunction:(double (*)(double))function
takesArcValue:(BOOL)takesArc
returnsArcValue:(BOOL)returnsArc
@@ -80,6 +79,7 @@
};
}
+
- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees
{
if ([MPMathRules sharedRules].isUsingDegrees) {
@@ -90,6 +90,7 @@
}
}
+
- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants
{
if ([MPMathRules sharedRules].isUsingDegrees) {
@@ -100,13 +101,24 @@
}
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
NSDecimalNumber *value = [self.term evaluate:error];
if (!value) {
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
diff --git a/MathKit/MPEvaluationContext.h b/MathKit/MPEvaluationContext.h
index 27c409b..d2f2bd4 100644
--- a/MathKit/MPEvaluationContext.h
+++ b/MathKit/MPEvaluationContext.h
@@ -7,21 +7,117 @@
//
+/*!
+ @header
+ This file contains the MPEvaluationContext 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
+ @abstract The evaluation context maintains a map of variables and
+ associates them with a certain level.
+
+ @discussion Different levels are nested using @link
+ //apple_ref/occ/instm/MPEvaluationContext/push@/link and
+ @link
+ //apple_ref/occ/instm/MPEvaluationContext/pop@/link
+ messages.
+
+ MPEvaluationContext is a singletone class. There can
+ only exist one instance (the shared instance) at any time.
+ */
@interface MPEvaluationContext : NSObject
+/*!
+ @method sharedContext
+ @abstract Returns the shared evaluation context.
+ */
+ (MPEvaluationContext *)sharedContext;
+
+/*!
+ @method push
+ @abstract Pushes a new level on context.
+ */
- (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;
+
+/*!
+ @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 YES if the variable was defined successfully,
+ NO if it is already defined in a lower level.
+ */
- (BOOL)defineVariable:(NSString *)variable
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;
+
+/*!
+ @method valueForVariable:
+ @abstract Returns the value for the specified variable.
+
+ @discussion If the variable has not been defined previously this
+ method returns nil.
+
+ @param variable
+ The variable whose value is to be retrieved.
+
+ @return The value variable was defined with or
+ nil if it was not defined.
+ */
- (NSDecimalNumber *)valueForVariable:(NSString *)variable;
@end
diff --git a/MathKit/MPEvaluationContext.m b/MathKit/MPEvaluationContext.m
index 134cebf..21fac5c 100644
--- a/MathKit/MPEvaluationContext.m
+++ b/MathKit/MPEvaluationContext.m
@@ -8,13 +8,21 @@
#import "MPEvaluationContext.h"
+
+
@interface MPEvaluationContext ()
+
@property (nonatomic, strong) NSMutableArray *stack;
+
@end
+
+
@implementation MPEvaluationContext
static MPEvaluationContext *sharedContext;
+
+
+ (MPEvaluationContext *)sharedContext
{
if (!sharedContext) {
@@ -23,6 +31,7 @@ static MPEvaluationContext *sharedContext;
return sharedContext;
}
+
- (instancetype)init
{
if (sharedContext) {
@@ -40,16 +49,19 @@ static MPEvaluationContext *sharedContext;
return self;
}
+
- (void)push
{
[self.stack addObject:[[NSMutableDictionary alloc] init]];
}
+
- (void)pop
{
[self.stack removeLastObject];
}
+
- (BOOL)defineVariable:(NSString *)variable
value:(NSDecimalNumber *)value
{
@@ -62,17 +74,20 @@ static MPEvaluationContext *sharedContext;
return YES;
}
+
- (void)undefineVariable:(NSString *)variable
{
NSMutableDictionary *currentBindings = self.stack.lastObject;
[currentBindings removeObjectForKey:variable];
}
+
- (BOOL)isVariableDefined:(NSString *)variable
{
return [self valueForVariable:variable] != nil;
}
+
- (NSDecimalNumber *)valueForVariable:(NSString *)variable
{
NSUInteger currentIndex = self.stack.count;
diff --git a/MathKit/MPExpression.h b/MathKit/MPExpression.h
index a32718f..11772b6 100644
--- a/MathKit/MPExpression.h
+++ b/MathKit/MPExpression.h
@@ -10,6 +10,9 @@
/*!
@header
+ This file contains the MPExpression class and the
+ MPExpressionElement protocol.
+
The MPExpression class is used to represent a mathematical
expression. It is used in the storage layer of the MathKit Expression System. An
instance of the MPExpression 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 userInfo
+ dictionary. You can query it using the
+ MPIllegalElementExceptionElementKey key.
+ */
+FOUNDATION_EXPORT NSString *const MPIllegalElementException;
+
+
+/*!
+ @const MPIllegalElementExceptionElementKey
+ @abstract Predefined key for an invalid element that caused a
+ MPIllegalElementException to be raised.
+
+ @discussion The invalid element can be of any type. Numbers and structs are
+ wrapped in an NSNumber or NSValue
+ instance respectively.
+ */
+FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
+
+
+/*!
+ @typedef MPReferenceFrame
+ @abstract A reference frame specifies the way an item 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
@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 userInfo
- dictionary. You can query it using the
- MPIllegalElementExceptionElementKey key.
- */
-FOUNDATION_EXPORT NSString *const MPIllegalElementException;
-
-
-/*!
- @const MPIllegalElementExceptionElementKey
- @abstract Predefined key for an invalid element that caused a
- MPIllegalElementException to be raised.
-
- @discussion The invalid element can be of any type. Numbers and structs are
- wrapped in an NSNumber or NSValue
- instance respectively.
- */
-FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
-
-
-/*!
- @typedef MPReferenceFrame
- @abstract A reference frame specifies the way an item 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
@abstract A MPExpression instance represents a mathematical
diff --git a/MathKit/MPExpression.m b/MathKit/MPExpression.m
index 6d201a5..da26d60 100644
--- a/MathKit/MPExpression.m
+++ b/MathKit/MPExpression.m
@@ -21,11 +21,11 @@
#import "NSIndexPath+MPAdditions.h"
-
NSString *const MPIllegalElementException = @"Illegal Element Exception";
NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey";
+
@interface MPExpression () {
NSMutableArray * _elements;
}
@@ -299,7 +299,6 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
}
-#warning If multiple equal expressions exist errors may occur...
- (NSUInteger)indexOfElement:(id)element
{
return [_elements indexOfObject:element];
@@ -656,7 +655,7 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
- (NSString *)description
{
-#warning Bad Implementation
+#warning Incomplete Implementation
NSMutableString *description = [[NSMutableString alloc] init];
NSUInteger index = 0;
for (id element in _elements) {
diff --git a/MathKit/MPExpressionLayout.h b/MathKit/MPExpressionLayout.h
index cc8d52c..d44e723 100644
--- a/MathKit/MPExpressionLayout.h
+++ b/MathKit/MPExpressionLayout.h
@@ -9,14 +9,53 @@
#import "MPLayout.h"
+/*!
+ @header
+ This file contains the MPExpressionLayout class.
+ */
+
+
@class MPExpressionLayout, MPExpression, MPFunctionLayout;
+
+/*!
+ @class MPExpressionLayout
+ @abstract An expression layout draws an @link
+ //apple_ref/occ/cl/MPExpression@/link.
+
+ @discussion For more information on the layout system see the documentation
+ on the @link //apple_ref/occ/cl/MPLayout@/link
+ class.
+ */
@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 nil as the parent.
+
+ @param expression
+ The expression that sould be represented by the receiver. Must
+ not be ni.
+
+ @param parent
+ The parent layout of the receiver. Specify nil to
+ initalize a root expression layout.
+
+ @return A newly initialized expression layout.
+ */
- (instancetype)initWithExpression:(MPExpression *)expression
parent:(MPFunctionLayout *)parent;
+
+/*!
+ @property expression
+ @abstract The expression represented by the receiver.
+ */
@property (readonly, nonatomic, weak) MPExpression *expression;
@end
diff --git a/MathKit/MPExpressionLayout.m b/MathKit/MPExpressionLayout.m
index 30333fa..2ffcbeb 100644
--- a/MathKit/MPExpressionLayout.m
+++ b/MathKit/MPExpressionLayout.m
@@ -9,21 +9,23 @@
#import "MPExpressionLayout.h"
#import "MPExpression.h"
-#import "MPExpression.h"
-#import "MPPowerFunction.h"
-
+#import "MPFunction.h"
#import "MPFunctionLayout.h"
#import "MPPowerFunctionLayout.h"
#import "MPToken.h"
#import "NSIndexPath+MPAdditions.h"
+
+
@interface MPExpressionLayout (MPLineGeneration)
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
@end
+
+
@implementation MPExpressionLayout (MPLineGeneration)
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index
@@ -60,9 +62,13 @@
@end
+
+
@implementation MPExpressionLayout
# pragma mark Creation Methods
+
+
- (instancetype)initWithExpression:(MPExpression *)expression
parent:(MPFunctionLayout *)parent
{
@@ -73,12 +79,16 @@
return self;
}
+
#pragma mark Cache Methods
+
+
- (NSUInteger)numberOfChildren
{
return self.expression.countElements;
}
+
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
id cachedObject = [self cachableObjectForIndex:index generator:^id{
@@ -95,6 +105,7 @@
return nil;
}
+
- (NSRect)boundsOfElementAtIndex:(NSUInteger)index
{
id symbol = [self.expression elementAtIndex:index];
@@ -109,7 +120,10 @@
}
}
+
#pragma mark Drawing Methods
+
+
- (NSRect)generateBounds
{
if (self.expression.countElements == 0) {
@@ -125,6 +139,7 @@
return NSMakeRect(x, y, width, height);
}
+
- (NSRect)boundingRectForRange:(NSRange)range
{
NSUInteger startOffset;
@@ -184,6 +199,7 @@
return NSMakeRect(x, self.bounds.origin.y, width, self.bounds.size.height);
}
+
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
CGFloat x = 0;
@@ -193,6 +209,7 @@
return NSMakePoint(x, 0);
}
+
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{
NSUInteger currentPosition = 0;
@@ -235,11 +252,13 @@
}
}
+
- (BOOL)drawsChildrenManually
{
return YES;
}
+
- (void)draw
{
// Get the current context
diff --git a/MathKit/MPExpressionParser.h b/MathKit/MPExpressionParser.h
index 217f5eb..1d6a63b 100644
--- a/MathKit/MPExpressionParser.h
+++ b/MathKit/MPExpressionParser.h
@@ -9,6 +9,8 @@
/*!
@header
+ This file contains the MPExpressionParser class.
+
The MPExpressionParser converts a @link
MPExpression@/link instance into a @link
//apple_ref/occ/cl/MPParsedExpression@/link instance. The rules that are
diff --git a/MathKit/MPExpressionParser.m b/MathKit/MPExpressionParser.m
index 89e7d34..80140db 100644
--- a/MathKit/MPExpressionParser.m
+++ b/MathKit/MPExpressionParser.m
@@ -8,25 +8,27 @@
#import "MPExpressionParser.h"
-#import "MPExpression.h"
-#import "MPTerm.h"
#import "MPToken.h"
+#import "MPExpression.h"
+#import "MPParsedExpression.h"
+
+#import "MPTerm.h"
#import "MPSumTerm.h"
#import "MPProductTerm.h"
-#import "MPFactorialTerm.h"
-#import "MPElementaryFunctionTerm.h"
-#import "MPFunctionTerm.h"
-#import "MPPowerTerm.h"
-#import "MPParsedExpression.h"
#import "MPNegatedTerm.h"
-
+#import "MPFunctionTerm.h"
+#import "MPElementaryFunctionTerm.h"
#import "MPNumber.h"
#import "MPVariable.h"
#import "MPFactorialTerm.h"
+#import "MPPowerTerm.h"
+
#define success() state = 0
#define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
+
+
@interface MPExpressionParser ()
@property (nonatomic, strong) NSArray *tokens;
@@ -46,6 +48,8 @@
@end
+
+
@implementation MPExpressionParser
- (instancetype)initWithExpression:(MPExpression *)expression
@@ -58,12 +62,14 @@
return self;
}
+
- (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors
{
return [self parseExpectingVariableDefinition:NO
errors:errors];
}
+
- (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag
errors:(NSArray *__autoreleasing *)errors
{
@@ -120,6 +126,7 @@
return result;
}
+
- (void)runParserMachine
{
NSInteger state = 1;
@@ -251,6 +258,7 @@
}
}
+
- (void)skipWhitespaces
{
while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) {
@@ -258,6 +266,7 @@
}
}
+
- (id)currentToken
{
if (self.currentTokenIndex >= self.tokens.count) {
@@ -266,11 +275,13 @@
return self.tokens[self.currentTokenIndex];
}
+
- (void)nextToken
{
self.currentTokenIndex++;
}
+
- (NSMutableArray *)errors
{
if (!_errors) {
@@ -279,6 +290,7 @@
return _errors;
}
+
- (NSMutableArray *)summands
{
if (!_summands) {
@@ -287,6 +299,7 @@
return _summands;
}
+
- (NSMutableArray *)factors
{
if (!_factors) {
@@ -295,6 +308,7 @@
return _factors;
}
+
- (NSMutableArray *)functionStack
{
if (!_functionStack) {
@@ -303,6 +317,7 @@
return _functionStack;
}
+
- (NSMutableArray *)valueGroup
{
if (!_valueGroup) {
@@ -311,6 +326,7 @@
return _valueGroup;
}
+
- (BOOL)parseOperatorList:(id)token // Returns YES if list is overall negative
{
NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]
@@ -318,17 +334,20 @@
return (operatorString.length & 1) == 1;
}
+
- (NSDecimalNumber *)parseNumber:(id)token
{
return [NSDecimalNumber decimalNumberWithString:token.stringValue
locale:[NSLocale currentLocale]];
}
+
- (NSString *)parseVariable:(id)token
{
return token.stringValue;
}
+
- (void)collapseSummand
{
if (self.factors.count > 0) {
@@ -342,6 +361,7 @@
self.summandNegative = NO;
}
+
- (void)collapseFactor
{
if (self.valueGroup.count > 0) {
@@ -360,6 +380,7 @@
self.factorNegative = NO;
}
+
- (void)runSuffixMachine
{
BOOL checkMore = YES;
@@ -388,6 +409,7 @@
}
}
+
- (void)addErrorWithCode:(NSInteger)code
localizedDescription:(NSString *)description
useRange:(BOOL)flag
@@ -408,6 +430,7 @@
MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
}
+
- (BOOL)errorOccured
{
[self skipWhitespaces];
diff --git a/MathKit/MPExpressionStorage.h b/MathKit/MPExpressionStorage.h
index ea80169..fdb4d7d 100644
--- a/MathKit/MPExpressionStorage.h
+++ b/MathKit/MPExpressionStorage.h
@@ -9,13 +9,51 @@
#import "MPExpression.h"
+/*!
+ @header
+ This file contains the MPExpressionStorage class.
+ */
+
+
@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
-@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 @link
+ //apple_ref/occ/instp/MPExpressionView/expressionStorage@/link
+ property of the @link
+ //apple_ref/occ/cl/MPExpressionView@/link 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 @link
+ //apple_ref/occ/cl/MPLayout@/link class.
+ */
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
@end
diff --git a/MathKit/MPExpressionStorage.m b/MathKit/MPExpressionStorage.m
index 1432afc..83d7e3a 100644
--- a/MathKit/MPExpressionStorage.m
+++ b/MathKit/MPExpressionStorage.m
@@ -9,17 +9,22 @@
#import "MPExpressionStorage.h"
#import "MPExpressionView.h"
-#import "MPLayout.h"
#import "MPExpressionLayout.h"
#import "MPRangePath.h"
+
+
@interface MPExpressionStorage ()
+
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, strong) NSTextStorage *textStorage;
+
@end
+
+
@implementation MPExpressionStorage
- (instancetype)initWithElements:(NSArray *)elements
@@ -31,6 +36,7 @@
return self;
}
+
- (void)setExpressionView:(MPExpressionView *)expressionView
{
_expressionView = expressionView;
@@ -38,6 +44,7 @@
self.rootLayout.flipped = expressionView.flipped;
}
+
- (void)changedElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength
{
diff --git a/MathKit/MPExpressionTokenizer.h b/MathKit/MPExpressionTokenizer.h
index 1c6d7ae..0a36a21 100644
--- a/MathKit/MPExpressionTokenizer.h
+++ b/MathKit/MPExpressionTokenizer.h
@@ -7,6 +7,12 @@
//
+/*!
+ @header
+ This file contains the MPExpressionTokenizer class.
+ */
+
+
@class MPExpressionTokenizer, MPExpression;
diff --git a/MathKit/MPExpressionTokenizer.m b/MathKit/MPExpressionTokenizer.m
index 09914bd..5dd4cce 100644
--- a/MathKit/MPExpressionTokenizer.m
+++ b/MathKit/MPExpressionTokenizer.m
@@ -52,7 +52,7 @@
@"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times
@"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|"
- @"([A-Za-z])|"
+ @"([A-Za-zĪ])|"
@"(!)|"
@"(=)|"
@"(\\s+)"
diff --git a/MathKit/MPExpressionView.h b/MathKit/MPExpressionView.h
index d978e60..49b941b 100644
--- a/MathKit/MPExpressionView.h
+++ b/MathKit/MPExpressionView.h
@@ -6,68 +6,195 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
-// TODO: Undo/Redo + Delegate (evaluateExpressionView:evaluate...)
+
+/*!
+ @header
+ This file contains the MPExpressionView class.
+
+ The MathKit Expression System
+ MathKit contains class es that make up the so called expression system.
+ The expression system is divided into three layers: the model, the view and the
+ controller layer. The MPExpressionView class represents the view
+ layer and interacts with the Cocoa NSView system. The model is
+ represented by the classes @link
+ //apple_ref/occ/cl/MPExpression@/link and @link
+ //apple_ref/occ/cl/MPFunction@/link. The controller layer between the two
+ consists of the class @link //apple_ref/occ/cl/MPLayout@/link 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;
-//@protocol MPExpressionViewDelegate;
+/*!
+ @class MPExpressionView
+ @abstract The MPExpressionView class is the front-end class of
+ the MathKit expression system.
+
+ @discussion The class draws the expression managed by the the back-end class
+ @link //apple_ref/occ/cl/MPExpressionStorage@/link
+ and is the interface between Application Kit's view system and
+ the MathKit expression system.
+ */
@interface MPExpressionView : NSView
#pragma mark Creation Methods
+/*!
+ @methodgroup Creation Methods
+ */
+
+/*!
+ @method initWithFrame:
+ @abstract Initializes a MPExpressionView 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
+ MPExpressionView class.
+
+ @param frameRect
+ The frame rectangle for the created view.
+
+ @return An initialized MPExpressionView or nil
+ if the object could not be created.
+ */
- (id)initWithFrame:(NSRect)frameRect;
+
#pragma mark Properties
+/*!
+ @methodgroup Properties
+ */
-@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
-//@property (nonatomic, weak) id 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 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 @link
+ //apple_ref/occ/instp/MPExpressionView/syntaxErrors@/link.
+ */
@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 NSError instances. In
+ addition to that every instance must contain a valid
+ NSIndexPath for the @link
+ //apple_ref/c/data/MPPathToExpressionKey@/link in its
+ userDict acompanied by a NSRange
+ wrapped in a NSValue instance for the @link
+ //apple_ref/c/data/MPErrorRangeKey@/link.
+
+ 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 NSError
+ instance.
+ */
@property (nonatomic, strong) NSArray *syntaxErrors;
+
+/*!
+ @property target
+ @abstract The target object to receive action messages from the receiver.
+ */
@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 enter
+ in the receiver. Typically this is understood as evaluation
+ request.
+ */
@property (nonatomic) SEL action;
-//@property (nonatomic) BOOL editable;
-//@property (nonatomic) BOOL selectable;
#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;
+
+
+/*!
+ @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;
+
+
+/*!
+ @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;
-// 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;
@end
-
-
-//@protocol MPExpressionViewDelegate
-//@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
\ No newline at end of file
diff --git a/MathKit/MPExpressionView.m b/MathKit/MPExpressionView.m
index a4317c4..f17463e 100644
--- a/MathKit/MPExpressionView.m
+++ b/MathKit/MPExpressionView.m
@@ -8,26 +8,25 @@
#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 "MPExpressionLayout.h"
-#import "MPFunctionLayout.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"
+
+
@interface MPExpressionView ()
@property (nonatomic, strong) NSButton *functionsButton;
@@ -58,6 +57,7 @@
@interface MPExpressionView (MPSelectionHelper)
+
- (void)restartCaretTimer;
- (void)updateCaret:(NSTimer *)timer;
@@ -101,6 +101,7 @@
[self updateCaret:self.caretTimer];
}
+
- (void)updateCaret:(NSTimer *)timer
{
self.caretVisible = !self.caretVisible;
@@ -111,6 +112,7 @@
[self setNeedsDisplayInRect:updatedRect];
}
+
- (NSRect)selectionRect
{
NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
@@ -120,6 +122,7 @@
return selectionRect;
}
+
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath
byExtendingSelection:(BOOL)extendingSelection
selectWords:(BOOL)selectWords
@@ -186,6 +189,7 @@
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
}
+
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath
byExtendingSelection:(BOOL)extendingSelection
selectWords:(BOOL)selectWords
@@ -257,6 +261,7 @@
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
}
+
- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath
newSelectionPath:(NSIndexPath *)newSelectionPath
{
@@ -312,9 +317,13 @@
#pragma mark -
+
+
@implementation MPExpressionView
#pragma mark Creation Methods
+
+
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
@@ -324,6 +333,7 @@
return self;
}
+
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
@@ -333,6 +343,7 @@
return self;
}
+
- (void)initializeExpressionView
{
// Setup the Expression Storage
@@ -348,6 +359,7 @@
[self restartCaretTimer];
}
+
- (void)initializeButtons
{
NSButton *functionsButton = self.functionsButton;
@@ -368,6 +380,7 @@
constant:0]];
}
+
- (void)initializeErrorsViews
{
@@ -394,34 +407,42 @@
views:variableBindings]];
}
+
#pragma mark - NSView Configuration
+
+
- (BOOL)acceptsFirstResponder
{
return YES;
}
+
- (BOOL)canBecomeKeyView
{
return YES;
}
+
- (BOOL)isOpaque
{
return YES;
}
+
- (void)resetCursorRects
{
[self addCursorRect:self.bounds
cursor:[NSCursor IBeamCursor]];
}
+
- (BOOL)becomeFirstResponder
{
[self restartCaretTimer];
return [super becomeFirstResponder];
}
+
- (BOOL)resignFirstResponder
{
[self.caretTimer invalidate];
@@ -430,12 +451,14 @@
return [super resignFirstResponder];
}
+
- (void)setFrame:(NSRect)frameRect
{
[self setNeedsLayout:YES];
[super setFrame:frameRect];
}
+
- (NSSize)intrinsicContentSize
{
NSSize size = self.expressionStorage.rootLayout.bounds.size;
@@ -443,7 +466,10 @@
return size;
}
+
#pragma mark - Properties
+
+
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{
_expressionStorage.expressionView = nil;
@@ -452,6 +478,7 @@
[self invalidateIntrinsicContentSize];
}
+
- (void)setSelection:(MPRangePath *)selection
{
_selection = selection;
@@ -459,6 +486,7 @@
self.needsDisplay = YES;
}
+
- (void)setSyntaxErrors:(NSArray *)syntaxErrors
{
_syntaxErrors = syntaxErrors;
@@ -491,12 +519,14 @@
}
}
+
- (void)setMathError:(NSError *)mathError
{
_mathError = mathError;
self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @"";
}
+
- (NSButton *)functionsButton
{
if (!_functionsButton) {
@@ -520,6 +550,7 @@
return _functionsButton;
}
+
- (NSPopUpButton *)syntaxErrorsPopUpButton
{
if (!_syntaxErrorsPopUpButton) {
@@ -539,6 +570,7 @@
return _syntaxErrorsPopUpButton;
}
+
- (NSTextField *)mathErrorTextField
{
if (!_mathErrorTextField) {
@@ -555,6 +587,7 @@
return _mathErrorTextField;
}
+
- (void)didSelectError:(NSPopUpButton *)sender
{
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
@@ -564,7 +597,10 @@
self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
}
+
#pragma mark - Mouse Event Handling
+
+
- (void)mouseDown:(NSEvent *)theEvent
{
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -577,6 +613,7 @@
self.selection = MPMakeRangePath(selectionPath, 0);
}
+
- (void)mouseDragged:(NSEvent *)theEvent
{
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -590,7 +627,10 @@
newSelectionPath:mouseSelectionPath];
}
+
#pragma mark Key Event Handling
+
+
- (void)keyDown:(NSEvent *)theEvent
{
NSString *characters = theEvent.characters;
@@ -617,6 +657,15 @@
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
+ if ([characters isEqualTo:@"i"]) {
+ if (self.selection.location.lastIndex > 0) {
+ id 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:@"*"]) {
characters = @"â
";
}
@@ -629,22 +678,28 @@
}
}
+
#pragma mark - Actions
+
+
- (void)switchToDegrees:(id)sender
{
[MPMathRules sharedRules].isUsingDegrees = YES;
}
+
- (void)switchToRadians:(id)sender
{
[MPMathRules sharedRules].isUsingDegrees = NO;
}
+
- (void)switchRadiansDegrees:(id)sender
{
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
}
+
- (IBAction)toggleFunctionsPopover:(id)sender
{
if (self.functionsPopover == nil || self.functionsViewController == nil) {
@@ -663,6 +718,7 @@
}
}
+
- (void)insertFunction:(MPFunction *)function
{
[self.functionsPopover close];
@@ -678,8 +734,10 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
}
+
#pragma mark Editing Actions
+
- (void)insertParenthesisFunction:(id)sender
{
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
@@ -698,6 +756,7 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
}
+
- (void)insertClosingParenthesis
{
if (self.selection.length > 0) {
@@ -721,6 +780,7 @@
}
}
+
- (void)insertPowerFunction
{
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
@@ -739,6 +799,7 @@
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
}
+
- (void)insertFractionFunction
{
if (self.selection.length == 0) {
@@ -792,6 +853,7 @@
}
}
+
- (void)insertNewline:(id)sender
{
if (self.target && self.action) {
@@ -801,6 +863,7 @@
}
}
+
- (void)deleteBackward:(id)sender
{
if (self.selection.length > 0) {
@@ -853,6 +916,7 @@
}
}
+
- (void)delete:(id)sender
{
[self.expressionStorage replaceItemsInRangePath:self.selection
@@ -861,8 +925,10 @@
self.selection = MPMakeRangePath(self.selection.location, 0);
}
+
#pragma mark Selection Actions
+
- (void)moveRight:(id)sender
{
if (self.selection.length > 0) {
@@ -875,6 +941,7 @@
}
}
+
- (void)moveLeft:(id)sender
{
if (self.selection.length > 0) {
@@ -887,6 +954,7 @@
}
}
+
- (void)moveWordRight:(id)sender
{
NSIndexPath *location = self.selection.maxRangePath;
@@ -896,6 +964,7 @@
self.selection = MPMakeRangePath(newSelectionLocation, 0);
}
+
- (void)moveWordLeft:(id)sender
{
NSIndexPath *location = self.selection.location;
@@ -905,17 +974,20 @@
self.selection = MPMakeRangePath(newSelectionLocation, 0);
}
+
- (void)moveToBeginningOfLine:(id)sender
{
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
}
+
- (void)moveToEndOfLine:(id)sender
{
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
}
+
- (void)moveLeftAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
@@ -935,6 +1007,7 @@
self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location];
}
+
- (void)moveRightAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
@@ -955,6 +1028,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
}
+
- (void)moveWordRightAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
@@ -977,6 +1051,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
}
+
- (void)moveWordLeftAndModifySelection:(id)sender
{
if (self.selection.length == 0) {
@@ -999,6 +1074,7 @@
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
}
+
- (void)moveUp:(id)sender
{
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
@@ -1011,6 +1087,7 @@
}
}
+
- (void)moveDown:(id)sender
{
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
@@ -1023,13 +1100,17 @@
}
}
+
- (void)selectAll:(id)sender
{
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
}
+
#pragma mark Drawing Methods
+
+
- (void)drawRect:(NSRect)dirtyRect
{
// Draw the background
@@ -1066,6 +1147,7 @@
#pragma mark - User Interface Validations
+
- (BOOL)validateUserInterfaceItem:(id)anItem
{
BOOL degrees = [MPMathRules sharedRules].isUsingDegrees;
diff --git a/MathKit/MPFactorialTerm.h b/MathKit/MPFactorialTerm.h
index ec2450c..aedc168 100644
--- a/MathKit/MPFactorialTerm.h
+++ b/MathKit/MPFactorialTerm.h
@@ -9,6 +9,12 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPFactorialTerm class.
+ */
+
+
@class MPFactorialTerm;
diff --git a/MathKit/MPFactorialTerm.m b/MathKit/MPFactorialTerm.m
index 0c16c85..5b9c3f9 100644
--- a/MathKit/MPFactorialTerm.m
+++ b/MathKit/MPFactorialTerm.m
@@ -8,6 +8,8 @@
#import "MPFactorialTerm.h"
+
+
@implementation MPFactorialTerm
- (instancetype)initWithTerm:(MPTerm *)term
@@ -20,6 +22,7 @@
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
NSDecimalNumber *value = [self.term evaluate:error];
diff --git a/MathKit/MPFractionFunction.h b/MathKit/MPFractionFunction.h
index 83a71b8..e9cceac 100644
--- a/MathKit/MPFractionFunction.h
+++ b/MathKit/MPFractionFunction.h
@@ -9,6 +9,12 @@
#import "MPFunction.h"
+/*!
+ @header
+ This file contains the MPFractionFunction class.
+ */
+
+
@class MPFractionFunction, MPExpression;
diff --git a/MathKit/MPFractionFunction.m b/MathKit/MPFractionFunction.m
index 76312e6..944c455 100644
--- a/MathKit/MPFractionFunction.m
+++ b/MathKit/MPFractionFunction.m
@@ -11,21 +11,26 @@
#import "MPFractionTerm.h"
#import "MPExpression.h"
+
+
@implementation MPFractionFunction
MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression)
MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression)
+
- (NSArray *)childrenAccessors
{
return @[@"nominatorExpression", @"denominatorExpression"];
}
+
- (Class)functionTermClass
{
return [MPFractionTerm class];
}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression];
diff --git a/MathKit/MPFractionFunctionLayout.h b/MathKit/MPFractionFunctionLayout.h
index ec361b5..ce3e95b 100644
--- a/MathKit/MPFractionFunctionLayout.h
+++ b/MathKit/MPFractionFunctionLayout.h
@@ -9,12 +9,32 @@
#import "MPFunctionLayout.h"
+/*!
+ @header
+ This file contains the MPFractionFunctionLayout class.
+ */
+
+
@class MPFractionFunctionLayout, MPFractionFunction;
+/*!
+ @class MPFractionFunctionLayout
+ @abstract A fraction function layout displays a @link
+ //apple_ref/occ/cl/MPFractionFunction@/link.
+
+ @discussion The nominator is displayed above the denominator. Between the two
+ children a horizontal bar is drawn.
+ */
@interface MPFractionFunctionLayout : MPFunctionLayout
+/*!
+ @method fractionFunction
+ @abstract Returns the @link
+ //apple_ref/occ/cl/MPFractionFunction@/link represented by
+ the receiver.
+ */
- (MPFractionFunction *)fractionFunction;
@end
diff --git a/MathKit/MPFractionFunctionLayout.m b/MathKit/MPFractionFunctionLayout.m
index 7683484..a0c3f4f 100644
--- a/MathKit/MPFractionFunctionLayout.m
+++ b/MathKit/MPFractionFunctionLayout.m
@@ -10,6 +10,7 @@
#import "MPFractionFunction.h"
+
#define kFractionFunctionLineWidth 1.0
#define kFractionFunctionHorizontalInset 2.0
#define kFractionFunctionNominatorOffset 0
@@ -17,6 +18,8 @@
#define MPFractionMiddle (CTFontGetCapHeight((CTFontRef)self.font) / 2)
+
+
@implementation MPFractionFunctionLayout
- (MPFractionFunction *)fractionFunction
@@ -24,21 +27,25 @@
return (MPFractionFunction *)self.function;
}
+
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{
return 1;
}
+
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{
return 0;
}
+
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
}
+
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
NSRect bounds = self.bounds;
@@ -54,11 +61,13 @@
}
}
+
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return YES;
}
+
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{
if (point.x < self.bounds.size.width / 2) {
@@ -68,6 +77,7 @@
}
}
+
- (NSRect)generateBounds
{
NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds;
@@ -80,6 +90,7 @@
return bounds;
}
+
- (void)draw
{
CGFloat y = MPFractionMiddle - kFractionFunctionLineWidth / 2;
diff --git a/MathKit/MPFractionTerm.h b/MathKit/MPFractionTerm.h
index 0c35590..f08c6ab 100644
--- a/MathKit/MPFractionTerm.h
+++ b/MathKit/MPFractionTerm.h
@@ -9,10 +9,24 @@
#import "MPFunctionTerm.h"
+/*!
+ @header
+ This file contains the MPFractionTerm class.
+ */
+
+
@class MPFractionTerm;
+/*!
+ @class MPFractionTerm
+ @abstract Represens a @link
+ //apple_ref/occ/cl/MPFractionFunction@/link.
+
+ @discussion A fraction is evaluating by dividing the nominator by the
+ denominator.
+ */
@interface MPFractionTerm : MPFunctionTerm
@end
diff --git a/MathKit/MPFractionTerm.m b/MathKit/MPFractionTerm.m
index a7c34b4..2e39fdb 100644
--- a/MathKit/MPFractionTerm.m
+++ b/MathKit/MPFractionTerm.m
@@ -10,6 +10,8 @@
#import "MPParsedExpression.h"
+
+
@implementation MPFractionTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
diff --git a/MathKit/MPFunction+MPToken.h b/MathKit/MPFunction+MPToken.h
index d567982..2d3339f 100644
--- a/MathKit/MPFunction+MPToken.h
+++ b/MathKit/MPFunction+MPToken.h
@@ -10,6 +10,12 @@
#import "MPToken.h"
+/*!
+ @header
+ This file contains the MPFunction(MPToken) category.
+ */
+
+
/*!
@category MPFunction (MPToken)
diff --git a/MathKit/MPFunction.h b/MathKit/MPFunction.h
index 3bd420f..f4bf663 100644
--- a/MathKit/MPFunction.h
+++ b/MathKit/MPFunction.h
@@ -11,6 +11,8 @@
/*!
@header
+ This file contains the MPFunction class.
+
MPFunction is a half abstract class that is designed to be
subclassed. Subclasses of the MPFunction class (subsequently called
functions) need to regard the following guidelines:
@@ -41,7 +43,6 @@
*/
-
/*!
@define MPFunctionAccessorImplementation
@abstract This macro implements the setter of a previously declared
diff --git a/MathKit/MPFunction.m b/MathKit/MPFunction.m
index a7a23f2..ba7f746 100644
--- a/MathKit/MPFunction.m
+++ b/MathKit/MPFunction.m
@@ -18,7 +18,6 @@
@implementation MPFunction
-
#pragma mark Creation Methods
diff --git a/MathKit/MPFunctionLayout.h b/MathKit/MPFunctionLayout.h
index e2b0933..4ff97c3 100644
--- a/MathKit/MPFunctionLayout.h
+++ b/MathKit/MPFunctionLayout.h
@@ -9,53 +9,334 @@
#import "MPLayout.h"
+/*!
+ @header
+ This file contains the MPFunctionLayout class.
+
+ The MPFunctionLayout 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
+ MPFunctionLayout 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
+ @abstract A function layout represents a @link
+ //apple_ref/occ/cl/MPFunction@/link.
+
+ @discussion MPFunctionLayout 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
+ @link //apple_ref/occ/cl/MPLayout@/link class.
+ */
@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
+ nil.
+
+ @param parent
+ The parent of the created function layout. May be
+ nil.
+
+ @return A newly created function layout.
+ */
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent;
-@property (readonly, nonatomic, weak) MPFunction *function;
-@end
-
-@interface MPFunctionLayout (MPSubclassOverride)
+/*!
+ @method initWithFunction:parent:
+ @abstract Initializes the receiver with the specified function and parent.
+
+ @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
+ MPFunctionLayout class.
+ @param function
+ The function represented by the receiver. Must not be
+ nil.
+
+ @param parent
+ The parent of the receiver or nil if it does not
+ have one.
+
+ @return A newly initialized MPFunctionLayout instance.
+ */
- (instancetype)initWithFunction:(MPFunction *)function
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 @link
+ //apple_ref/occ/instm/MPFunctionLayout/objectForPrivateCacheIndex:generator:@/link.
+ See the documentation on that method for details.
+
+ @param index
+ The index of the line to retrieve. Private cache indexes start at 0.
+
+ @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
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 generator is invoked. The result
+ of the generator is then stored at the specified
+ index and returned.
+
+ @param index
+ The index of the private cache item to retrieve. Private cache
+ indexes start at 0.
+
+ @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
generator:(id (^)())generator;
-// Should also implement accessor method for special function type:
-// - (MPCustomFunction *)customFunction
-// {
-// return (MPCustomFunction *)self.function;
-// }
+@end
-#pragma mark Size and Drawing Methods
+
+
+/*!
+ @category MPFunctionLayout (MPSubclassOverride)
+ @abstract The methods defined in this category must be implemented by any
+ concrete subclass of MPFunctionLayout.
+ */
+@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 YES if the receiver's child at the specified
+ index should use the small font, NO
+ otherwise. The default implementation returns NO.
+ */
- (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
-- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index; // May be implemented
-- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index; // May be implemented
-- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index; // May be implemented
-- (NSIndexSet *)indexesOfRemainingChildren; // May be implemented
+/*!
+ @method offsetOfChildLayoutAtIndex:
+ @abstract Implementation requirement from superclass. See @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link
+ for details.
+ */
+- (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 (0 means before, 1
+ means after).
+ */
+- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point;
+
+
+/*!
+ @method generateBounds
+ @abstract Implementation requirement from superclass. See @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link
+ for details.
+ */
+- (NSRect)generateBounds;
+
+
+/*!
+ @method draw
+ @abstract Implementation requirement from superclass. See @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/draw@/link
+ for details.
+ */
+- (void)draw;
+
+
+/*!
+ @method indexOfLeadingChild
+ @abstract Identifies the index of the receiver's child that will get
+ selected when the cursor enters 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 0.
+ */
+- (NSUInteger)indexOfLeadingChild;
+
+
+/*!
+ @method indexOfTrailingChild
+ @abstract Identifies the index of the receiver's child that will get
+ selected when the cursor enters 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 0.
+ */
+- (NSUInteger)indexOfTrailingChild;
+
+
+/*!
+ @method indexOfChildBeforeChildAtIndex:
+ @abstract Identifies the index of the receiver's child that will get
+ selected when the cursor leaves the child at the specified
+ index in the direction opposite to reading.
+
+ @discussion Return NSNotFound from this method if the function
+ should be left when the child at the specified index is
+ left.
+
+ You may implement this method to override the default behaviour.
+
+ @param index
+ The index of the child the caret left.
+
+ @return The index of the child that is to be entered or
+ NSNotFound if there are no more children. The
+ default implementation returns NSNotFound.
+ */
+- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index;
+
+
+/*!
+ @method indexOfChildAfterChildAtIndex:
+ @abstract Identifies the index of the receiver's child that will get
+ selected when the cursor leaves the child at the specified
+ index in the direction of reading.
+
+ @discussion Return NSNotFound from this method if the function
+ should be left when the child at the specified index is
+ left.
+
+ You may implement this method to override the default behaviour.
+
+ @param index
+ The index of the child the caret left.
+
+ @return The index of the child that is to be entered or
+ NSNotFound 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 index.
+
+ 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 index.
+ */
+- (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 index.
+
+ 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 index.
+ */
+- (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
\ No newline at end of file
diff --git a/MathKit/MPFunctionLayout.m b/MathKit/MPFunctionLayout.m
index 5bac16d..822a34b 100644
--- a/MathKit/MPFunctionLayout.m
+++ b/MathKit/MPFunctionLayout.m
@@ -8,24 +8,28 @@
#import "MPFunctionLayout.h"
-#import "MPExpression.h"
+#import "MPExpressionLayout.h"
+
#import "MPFunction.h"
-#import "MPSumFunction.h"
+#import "MPFractionFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
-#import "MPFractionFunction.h"
+#import "MPSumFunction.h"
-#import "MPExpressionLayout.h"
-#import "MPSumFunctionLayout.h"
+#import "MPFractionFunctionLayout.h"
#import "MPParenthesisFunctionLayout.h"
#import "MPPowerFunctionLayout.h"
-#import "MPFractionFunctionLayout.h"
+#import "MPSumFunctionLayout.h"
#import "NSIndexPath+MPAdditions.h"
+
+
@implementation MPFunctionLayout
#pragma mark Creation Methods
+
+
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent
{
@@ -43,6 +47,7 @@
parent:parent];
}
+
- (instancetype)initWithFunction:(MPFunction *)function
parent:(MPExpressionLayout *)parent
{
@@ -53,7 +58,9 @@
return self;
}
+
#pragma mark Cache Methods
+
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
generator:(CTLineRef (^)())generator
{
@@ -67,6 +74,7 @@
return (__bridge CTLineRef)(lineObject);
}
+
- (id)objectForPrivateCacheIndex:(NSUInteger)index
generator:(id (^)())generator
{
@@ -74,11 +82,13 @@
return [self cachableObjectForIndex:actualIndex generator:generator];
}
+
- (NSUInteger)numberOfChildren
{
return self.function.numberOfChildren;
}
+
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
return [self cachableObjectForIndex:index generator:^id{
@@ -91,11 +101,13 @@
}];
}
+
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return NO;
}
+
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{
// A single index is used to communicate back wether the
@@ -116,36 +128,43 @@
return [self indexPathForLocalMousePoint:point];
}
+
- (NSUInteger)indexOfLeadingChild
{
return 0;
}
+
- (NSUInteger)indexOfTrailingChild
{
return 0;
}
+
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
{
return NSNotFound;
}
+
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{
return NSNotFound;
}
+
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{
return index;
}
+
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{
return index;
}
+
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSet];
diff --git a/MathKit/MPFunctionTerm.h b/MathKit/MPFunctionTerm.h
index 0d84429..20a1eee 100644
--- a/MathKit/MPFunctionTerm.h
+++ b/MathKit/MPFunctionTerm.h
@@ -8,6 +8,28 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPFunctionTerm 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
+ nil. Also there has to exist a NSError *__autoreleasing * pointer called error.
+
+ @param var
+ The name of the variable that will be declared in this macro. It
+ will be of the type NSDecimalNumber * 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
@@ -15,11 +37,62 @@
@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 @link
+ //apple_ref/occ/cl/MPFunction@/link instances.
+ */
@interface MPFunctionTerm : MPTerm
+/*!
+ @method initWithFunction:errors:
+ @abstract Initializes a MPFunctionTerm from the specified
+ @link //apple_ref/occ/cl/MPFunction@/link instance.
+
+ @discussion This method parses the children of the specified
+ function and stores the respective parsed
+ expressions. They are accessible using the @link
+ //apple_ref/occ/instm/MPFunctionTerm/expressionAtIndex:@/link
+ method.
+
+ Whether the children should contain a variable definition is
+ determined using the @link
+ //apple_ref/occ/instm/MPFunction/expectsVariableDefinitionInChildAtIndex:@/link
+
+ @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 errors
+ parameter.
+
+ @param errors
+ If any of the function's children contain syntax
+ errors they are returned indirectly through this parameter. In
+ that case the method returns nil. If there are no
+ errors the parameter is not modified. This parameter is never set
+ to an empty array.
+
+ @return A new MPFunctionTerm instance.
+ */
- (instancetype)initWithFunction:(MPFunction *)function
errors:(NSArray *__autoreleasing *)errors; /* designated initializer */
+
+/*!
+ @method expressionAtIndex:
+ @abstract Returns a @link
+ //apple_ref/occ/cl/MPParsedExpression@/link instance that
+ represents the child at anIndex 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;
@end
diff --git a/MathKit/MPFunctionTerm.m b/MathKit/MPFunctionTerm.m
index bfc9249..050c702 100644
--- a/MathKit/MPFunctionTerm.m
+++ b/MathKit/MPFunctionTerm.m
@@ -9,10 +9,6 @@
#import "MPFunctionTerm.h"
#import "MPFunction.h"
-#import "MPExpression.h"
-#import "MPParsedExpression.h"
-
-#import "MPToken.h"
@@ -64,6 +60,7 @@
return self;
}
+
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex
{
return [self.parsedExpressions objectAtIndex:anIndex];
diff --git a/MathKit/MPFunctionsViewController.h b/MathKit/MPFunctionsViewController.h
index 204bb57..0fc5c84 100644
--- a/MathKit/MPFunctionsViewController.h
+++ b/MathKit/MPFunctionsViewController.h
@@ -7,19 +7,74 @@
//
+/*!
+ @header
+ This file contains the MPFunctionsViewController class.
+ */
+
+
@class MPFunctionsViewController, MPFunctionsCollectionView;
+/*!
+ @class MPFunctionsViewController
+ @abstract Controls a view from which the user can select function to be
+ inserted into a @link
+ //apple_ref/occ/cl/MPExpressionView@/link.
+
+ @discussion The view contain a NSCollectionView displaying the
+ prototypes an a NSTextField displaying a description
+ of the currently selected prototype.
+ */
@interface MPFunctionsViewController : NSViewController
-- (id)init;
+/*!
+ @property collectionView
+ @abstract The NSCollectionView that displays the function
+ prototypes.
+ */
+@property (weak) IBOutlet NSCollectionView *collectionView;
-@property (weak) IBOutlet MPFunctionsCollectionView *collectionView;
+
+/*!
+ @property functionPrototypes
+ @abstract Contains the @link
+ //apple_ref/occ/cl/MPFunction@/link objects that get
+ inserted into an expression if selected.
+
+ @discussion Every object in the array must be an @link
+ //apple_ref/occ/cl/MPFunction@/link instance.
+ */
@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 target
+ @abstract The target object to receive action messages from the receiver.
+ */
@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
diff --git a/MathKit/MPFunctionsViewController.m b/MathKit/MPFunctionsViewController.m
index 0b21c93..a60a730 100644
--- a/MathKit/MPFunctionsViewController.m
+++ b/MathKit/MPFunctionsViewController.m
@@ -8,18 +8,21 @@
#import "MPFunctionsViewController.h"
-#import "MPExpression.h"
-#import "NSString+MPExpressionElement.h"
#import "MPFunction.h"
-#import "MPSumFunction.h"
+#import "MPFractionFunction.h"
#import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
-#import "MPFractionFunction.h"
+#import "MPSumFunction.h"
#import "MPFunctionLayout.h"
+#import "NSString+MPExpressionElement.h"
+
+
+
@class MPFunctionsCollectionView, MPFunctionTemplateView, MPFunctionTemplateItem;
+
@interface MPFunctionsCollectionView : NSCollectionView
@property (nonatomic, weak) MPFunctionTemplateItem *hoverItem;
@@ -30,6 +33,7 @@
@end
+
@interface MPFunctionTemplateView : NSView
@property (nonatomic, strong) MPFunction *functionTemplate;
@@ -42,16 +46,20 @@
@interface MPFunctionTemplateItem : NSCollectionViewItem
+
@property (nonatomic, copy) NSString *templateName;
+
@end
+
@implementation MPFunctionsCollectionView
- (void)mouseDown:(NSEvent *)theEvent
{
}
+
- (void)mouseUp:(NSEvent *)theEvent
{
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
@@ -87,25 +95,30 @@
[super updateTrackingAreas];
}
+
- (void)mouseEntered:(NSEvent *)theEvent
{
self.mouseOver = YES;
self.needsDisplay = YES;
}
+
- (void)mouseExited:(NSEvent *)theEvent
{
self.mouseOver = NO;
self.needsDisplay = YES;
}
+
- (void)setFunctionTemplate:(MPFunction *)functionTemplate
{
_functionTemplate = functionTemplate;
_functionTemplateLayout = nil;
}
+
@synthesize functionTemplateLayout = _functionTemplateLayout;
+
- (MPFunctionLayout *)functionTemplateLayout
{
@@ -117,16 +130,19 @@
return _functionTemplateLayout;
}
+
- (NSSize)intrinsicContentSize
{
return [self.functionTemplateLayout bounds].size;
}
+
- (BOOL)isOpaque
{
return NO;
}
+
- (void)drawRect:(NSRect)dirtyRect
{
if (self.mouseOver) {
@@ -150,11 +166,13 @@
static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMouseOverContext";
+
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
return [super awakeAfterUsingCoder:aDecoder];
}
+
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
@@ -171,6 +189,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
}
}
+
- (void)setRepresentedObject:(id)representedObject
{
MPFunctionTemplateView *view = (MPFunctionTemplateView *)self.view;
@@ -191,6 +210,8 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
@end
+
+
@implementation MPFunctionsViewController
- (id)init
@@ -199,6 +220,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
bundle:[NSBundle bundleForClass:[self class]]];
}
+
- (void)awakeFromNib
{
MPFunction *sumFunction = [[MPSumFunction alloc] init];
@@ -227,40 +249,45 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
forKeyPath:@"hoverItem"
options:0
context:MPCollectionViewHoverItemChangeContext];
- self.collectionView.target = self.target;
- self.collectionView.action = self.action;
+ ((MPFunctionsCollectionView *)self.collectionView).target = self.target;
+ ((MPFunctionsCollectionView *)self.collectionView).action = self.action;
}
+
static void *MPCollectionViewHoverItemChangeContext = @"MPCollectionViewHoverItemChangeContext";
+
- (void)setView:(NSView *)view
{
[super setView:view];
}
+
- (void)setTarget:(id)target
{
_target = target;
if (self.collectionView) {
- self.collectionView.target = target;
+ ((MPFunctionsCollectionView *)self.collectionView).target = target;
}
}
+
- (void)setAction:(SEL)action
{
_action = action;
if (self.collectionView) {
- self.collectionView.action = action;
+ ((MPFunctionsCollectionView *)self.collectionView).action = action;
}
}
+
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == MPCollectionViewHoverItemChangeContext) {
- self.currentDescription = self.collectionView.hoverItem.templateName;
+ self.currentDescription = ((MPFunctionsCollectionView *)self.collectionView).hoverItem.templateName;
} else {
[super observeValueForKeyPath:keyPath
ofObject:object
diff --git a/MathKit/MPLayout.h b/MathKit/MPLayout.h
index fff345e..8c85be6 100644
--- a/MathKit/MPLayout.h
+++ b/MathKit/MPLayout.h
@@ -6,12 +6,82 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
+
+/*!
+ @header
+ This file contains the MPLayout class.
+
+ The MPLayout class renders an expression to be displayed in an
+ @link //apple_ref/occ/cl/MPExpressionView@/link. There are multiple
+ subclasses of MPLayout the most important of which are @link
+ //apple_ref/occ/cl/MPExpressionLayout@/link and @link
+ //apple_ref/occ/cl/MPFunctionLayout@/link 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
+ @link //apple_ref/occ/cl/MPFunctionLayout@/link 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
+ empty box 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 kMPEmptyBox... or
+ kMPEmptyBoxDrawing... macros. These macros must not be used outside
+ of the MPLayout class or any of it subclasses.
+ */
+
+
+/*!
+ @define kMPEmptyBoxWidth
+ @abstract The width of the empty box.
+ */
#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 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 kMPEmptyBoxDrawingWidth
+ @abstract The with the empty box is rendered with.
+ */
#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 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))
@@ -19,58 +89,548 @@
@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 MPLayout 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
#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
+ MPLayout class.
+
+ @return A newly initialized MPLayout instance.
+ */
- (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 @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/childLayoutAtIndex:@/link
+ method.
+
+ @param parent
+ The receiver's parent.
+
+ @return A newly initialized MPLayout instance.
+ */
- (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;
+
+/*!
+ @method normalFontWithSize:
+ @abstract Returns a NSFont object that represents the normal
+ font in the specified size.
+
+ @discussion Instead of this method you want to use @link
+ //apple_ref/occ/instm/MPLayout/font@/link instead in most
+ cases.
+
+ @param size
+ The size of the font.
+
+ @return A NSFont object that can be used to render textual
+ content of the receiver in the specified size.
+ */
+- (NSFont *)normalFontWithSize:(CGFloat)size;
+
+
+/*!
+ @method specialFontWithSize:
+ @abstract Returns a NSFont object that represents the special
+ font in the specified size.
+
+ @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 NSFont object that can be used to render special
+ textual content of the receiver in the specified
+ size.
+ */
+- (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
+/*!
+ @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;
+
#pragma mark Cache Methods
-// Querying Caches
+/*!
+ @methodgroup Cache Methods
+ */
+
+
+/*!
+ @method chacableObjectForIndex:generator:
+ @abstract Returns the cached object for the specified index or
+ creates one if it does not exist.
+
+ @discussion This method only returns nil if the
+ generator returns nil.
+
+ @param index
+ The index of the cached object to retrieve.
+
+ @param generator
+ This block is executed if there is no cached object at
+ index. The result of this block is then cached at
+ the index.
+
+ @return The cached object for index or the result of the
+ generator if there is no cached object.
+ */
- (id)cachableObjectForIndex:(NSUInteger)index
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
+ replacementLength - range.length places. If
+ replacementLength is greater than
+ range.length 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
+ range.
+ */
- (void)clearCacheInRange:(NSRange)range
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 invalidate
+ message to its parent. The cached information about the
+ receiver's children is not discarded. Use @link
+ //apple_ref/occ/instm/MPLayout/clearCacheInRange:replacementLength:@/link
+ for that purpose.
+ */
- (void)invalidate;
+
+/*!
+ @method childLayoutAtIndexPath:
+ @abstract Returns the child layout at the specified index path.
+
+ @discussion If the indexPath 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;
-#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 CTLineRef representing the rendered string.
+ */
- (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 emphasize 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 CTLineRef representing the rendered string.
+ */
- (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 @link
+ //apple_ref/occ/instm/MPLayout/createLineForString:@/link
+ or @link
+ //apple_ref/occ/instm/MPLayout/createLineForString:emphasize:@/link
+ over this method.
+
+ @param aString
+ The string to be rendered.
+
+ @param font
+ The font to render aString in.
+
+ @return A CTLineRef representing the rendered string.
+ */
- (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 usesSmallSize
+ @abstract This property indicates whether the receiver uses the small font
+ size.
+
+ @discussion This property is used to determine the @link
+ //apple_ref/occ/instm/MPLayout/contextInferredFontSize@/link.
+ If this value is YES 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;
+
+
+/*!
+ @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 @link
+ //apple_ref/occ/instm/MPLayout/invalidate@/link method. If
+ there is no cached value this method will generate a new cached
+ value by calling the @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link
+ method.
+
+ @return The receiver's 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
+ rangePath.
+ */
- (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 NO from its @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link
+ method this method also draws every single child of the receiver.
+ The location the children are drawn at is determined by the
+ @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link
+ method.
+
+ @param point
+ The point the receiver is to be drawn at.
+ */
- (void)drawAtPoint:(NSPoint)point;
@end
+
+
+/*!
+ @category MPLayout (MPSubclassImplement)
+ @abstract The methods in this category must be implemented by any concrete
+ subclass of MPLayout.
+ */
@interface MPLayout (MPSubclassImplement)
+
+/*!
+ @method numberOfChildren
+ @abstract Returns the number of sublayouts of the receiver.
+
+ @discussion This method must be implemented by subclasses.
+ */
- (NSUInteger)numberOfChildren;
+
+
+/*!
+ @method drawsChildrenManually
+ @abstract Returns whether the receiver wants to take over responsibility of
+ drawing its children.
+
+ @discussion If you return YES from this method
+ MPLayout does not draw the receiver's children. The
+ receiver's //apple_ref/occ/intfm/MPLayout/draw
+ method has to implement that functionality. If you return
+ NO from this method the receiver must not draw its
+ children in the //apple_ref/occ/intfm/MPLayout/draw
+ method.
+
+ This method may be implemented by subclasses to opt in and change
+ the default behaviour.
+
+ @return YES if the receiver draws its children
+ automatically, NO otherwise. The default
+ implementation returns NO.
+ */
- (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 index 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 @link
+ //apple_ref/occ/cl/MPFunction@/link instance it should
+ always return a valid MPLayout instance.
+ 2. If the receiver represents a @link
+ //apple_ref/occ/cl/MPExpression@/link instance it
+ returns only a valid MPLayout instance if the
+ represented expression's element at index 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 MPLayout instance representing the respective
+ child of the object that is represented by the receiver, or
+ nil 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
+ MPLayout class. This method is only called if the
+ receiving MPLayout 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 range has a
+ length of 0 the returned rectangle should be the
+ bounds of the caret (which has a width of 0). 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 YES from the @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link
+ 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;
+
+
+/*!
+ @method indexPathForMousePoint:
+ @abstract Performs hit testing.
+
+ @discussion This method tests the specified point 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;
-- (void)draw; // To be implemented
+
+
+/*!
+ @method draw
+ @abstract Draws the receiver.
+
+ @discussion If the receiver returns YES from its @link
+ //apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link
+ 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
\ No newline at end of file
diff --git a/MathKit/MPLayout.m b/MathKit/MPLayout.m
index 16da726..e101604 100644
--- a/MathKit/MPLayout.m
+++ b/MathKit/MPLayout.m
@@ -11,20 +11,25 @@
#import "MPRangePath.h"
#import "NSIndexPath+MPAdditions.h"
+
+
@interface MPLayout ()
-// Querying and Storing Caches
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
@end
+
+
@implementation MPLayout {
NSMutableArray *_cache;
NSRect _cachedBounds;
}
#pragma mark Creation Methods
+
+
- (id)init
{
self = [super init];
@@ -35,6 +40,7 @@
return self;
}
+
- (instancetype)initWithParent:(MPLayout *)parent
{
self = [self init];
@@ -44,41 +50,51 @@
return self;
}
+
#pragma mark Properties
+
+
- (NSFont *)normalFontWithSize:(CGFloat)size
{
return [NSFont fontWithName:@"CMU Serif"
size:size];
}
+
- (NSFont *)specialFontWithSize:(CGFloat)size
{
return [NSFont fontWithName:@"CMU Serif Italic"
size:size];
}
+
- (CGFloat)contextInferredFontSize
{
return self.usesSmallSize ? self.smallFontSize : self.normalFontSize;
}
+
- (CGFloat)normalFontSize
{
return 18.0;
}
+
- (CGFloat)smallFontSize
{
return 12.0;
}
+
- (NSFont *)font
{
return [self normalFontWithSize:self.contextInferredFontSize];
}
+
#pragma mark Cache Tree
-// Querying and Storing Caches
+
+
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
{
if (index >= _cache.count) {
@@ -87,6 +103,7 @@
return _cache[index] != [NSNull null];
}
+
- (id)cachableObjectForIndex:(NSUInteger)index
generator:(id (^)())generator
{
@@ -99,6 +116,7 @@
return object;
}
+
- (void)ensureCacheSizeForIndex:(NSUInteger)index
{
while (index >= _cache.count) {
@@ -106,7 +124,7 @@
}
}
-// Clearing Caches
+
- (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength
{
@@ -119,12 +137,14 @@
[self invalidate];
}
+
- (void)invalidate
{
_cachedBounds = NSZeroRect;
[self.parent invalidate];
}
+
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.length == 0) {
@@ -134,19 +154,24 @@
return [child childLayoutAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
}
+
#pragma mark Calculation and Drawing Methods
+
+
- (CTLineRef)createLineForString:(NSString *)aString
{
return [self createLineForString:aString
usingFont:self.font];
}
+
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize
{
return [self createLineForString:aString
usingFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
}
+
- (CTLineRef)createLineForString:(NSString *)aString
usingFont:(NSFont *)font
{
@@ -158,6 +183,7 @@
return line;
}
+
- (NSRect)bounds
{
if (NSEqualRects(_cachedBounds, NSZeroRect)) {
@@ -166,6 +192,7 @@
return _cachedBounds;
}
+
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath
{
if (rangePath.location.length == 1) {
@@ -180,11 +207,13 @@
return bounds;
}
+
- (BOOL)drawsChildrenManually
{
return NO;
}
+
- (void)drawAtPoint:(NSPoint)point
{
NSAffineTransform *transform = [NSAffineTransform transform];
diff --git a/MathKit/MPMathRules.h b/MathKit/MPMathRules.h
index 78afc0d..5bc2474 100644
--- a/MathKit/MPMathRules.h
+++ b/MathKit/MPMathRules.h
@@ -7,25 +7,49 @@
//
+/*!
+ @header
+ This file contains the MPMathRules class.
+ */
-FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey;
-FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey;
+
+/*!
+ @const MPMathRulesIsUsingDegreesKey
+ @abstract Predefined key that is used to store the properties of the
+ MPMathRules class in the NSUserDefaults
+ database.
+ */
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
+
@class MPMathRules;
+/*!
+ @class MPMathRules
+ @abstract The math rules class stores global settings concerning
+ mathematical interpretations
+
+ @discussion The MPMathRules class is a singletone class. There
+ can only exist one instance (the shared instance) at any time.
+ */
@interface MPMathRules : NSObject
+/*!
+ @method sharedRules
+ @abstract Returnes the shared MPMathRules instance.
+ */
+ (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 (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0
+/*!
+ @property isUsingDegrees
+ @abstract Specifies whether trigonometric functions use degree values.
+
+ @discussion If set to NO radians are used instead. The default
+ value is NO.
+ */
@property (nonatomic) BOOL isUsingDegrees;
@end
diff --git a/MathKit/MPMathRules.m b/MathKit/MPMathRules.m
index e8654a7..598152d 100644
--- a/MathKit/MPMathRules.m
+++ b/MathKit/MPMathRules.m
@@ -8,14 +8,16 @@
#import "MPMathRules.h"
-NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey";
-NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey";
+
NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey";
+
+
@implementation MPMathRules
static MPMathRules *sharedRules;
+
+ (MPMathRules *)sharedRules
{
if (!sharedRules) {
@@ -24,6 +26,7 @@ static MPMathRules *sharedRules;
return sharedRules;
}
+
- (instancetype)init
{
if (sharedRules) {
@@ -31,53 +34,21 @@ static MPMathRules *sharedRules;
}
self = [super init];
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;
}
-- (void)setUsingUserDefaultValues:(BOOL)usingUserDefaultValues
+
+- (BOOL)isUsingDegrees
{
- _usingUserDefaultValues = usingUserDefaultValues;
- // Save the current values
- self.maximumOperatorChainLength = self.maximumOperatorChainLength;
- self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication;
- self.isUsingDegrees = self.isUsingDegrees;
+ return [[NSUserDefaults standardUserDefaults] boolForKey:MPMathRulesIsUsingDegreesKey];
}
-- (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
{
- _isUsingDegrees = isUsingDegrees;
- if (self.isUsingUserDefaultValues) {
- [[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
+ [[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
forKey:MPMathRulesIsUsingDegreesKey];
- }
}
@end
diff --git a/MathKit/MPNegatedTerm.h b/MathKit/MPNegatedTerm.h
index 7c4b305..5c39556 100644
--- a/MathKit/MPNegatedTerm.h
+++ b/MathKit/MPNegatedTerm.h
@@ -9,6 +9,12 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPNegatedTerm class.
+ */
+
+
@class MPNegatedTerm;
@@ -20,7 +26,6 @@
*/
@interface MPNegatedTerm : MPTerm
-
/*!
@method initWithTerm:
@abstract Initializes a new negated term.
@@ -34,8 +39,8 @@
/*!
- @property term
- @abstract The receiver's term.
+ @property term
+ @abstract The receiver's term.
*/
@property (readonly, nonatomic, strong) MPTerm *term;
diff --git a/MathKit/MPNegatedTerm.m b/MathKit/MPNegatedTerm.m
index d7b1d27..1e6df48 100644
--- a/MathKit/MPNegatedTerm.m
+++ b/MathKit/MPNegatedTerm.m
@@ -8,6 +8,8 @@
#import "MPNegatedTerm.h"
+
+
@implementation MPNegatedTerm
- (instancetype)initWithTerm:(MPTerm *)term
@@ -20,6 +22,7 @@
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
NSDecimalNumber *value = [self.term evaluate:error];
diff --git a/MathKit/MPNumber.h b/MathKit/MPNumber.h
index e9d1f1e..feaf477 100644
--- a/MathKit/MPNumber.h
+++ b/MathKit/MPNumber.h
@@ -9,14 +9,44 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPNumber class.
+ */
+
+
@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.
+ 3.4). Numbers that have periods or are irrational
+ are not implemented by this class.
+ */
@interface MPNumber : MPTerm
+/*!
+ @method initWithNumber:
+ @abstract Initializes a number term with the specified number.
+
+ @param number
+ The number that the term should evaluate to. Must not be
+ nil.
+
+ @return A new MPNumberTerm instance.
+ */
- (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */
+
+/*!
+ @property number
+ @abstract The receiver's number.
+ */
@property (readonly, nonatomic, strong) NSDecimalNumber *number;
@end
diff --git a/MathKit/MPNumber.m b/MathKit/MPNumber.m
index 4d69957..4daa7d9 100644
--- a/MathKit/MPNumber.m
+++ b/MathKit/MPNumber.m
@@ -8,9 +8,7 @@
#import "MPNumber.h"
-#import "MPParsedExpression.h"
-#import "MPToken.h"
@implementation MPNumber
@@ -24,6 +22,7 @@
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
return self.number;
diff --git a/MathKit/MPParenthesisFunction.h b/MathKit/MPParenthesisFunction.h
index 813847b..42664b9 100644
--- a/MathKit/MPParenthesisFunction.h
+++ b/MathKit/MPParenthesisFunction.h
@@ -9,6 +9,12 @@
#import "MPFunction.h"
+/*!
+ @header
+ This file contains the MPParenthesisFunction class.
+ */
+
+
@class MPParenthesisFunction, MPExpression;
diff --git a/MathKit/MPParenthesisFunction.m b/MathKit/MPParenthesisFunction.m
index 19bd785..fe33eba 100644
--- a/MathKit/MPParenthesisFunction.m
+++ b/MathKit/MPParenthesisFunction.m
@@ -18,16 +18,19 @@
MPFunctionAccessorImplementation(Expression, _expression)
+
- (NSArray *)childrenAccessors
{
return @[@"expression"];
}
+
- (Class)functionTermClass
{
return [MPParenthesisTerm class];
}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"(%@)", self.expression.description];
diff --git a/MathKit/MPParenthesisFunctionLayout.h b/MathKit/MPParenthesisFunctionLayout.h
index a83d78a..1e6a5de 100644
--- a/MathKit/MPParenthesisFunctionLayout.h
+++ b/MathKit/MPParenthesisFunctionLayout.h
@@ -9,12 +9,32 @@
#import "MPFunctionLayout.h"
+/*!
+ @header
+ This file contains the MPParenthesisFunctionLayout class.
+ */
+
+
@class MPParenthesisFunctionLayout, MPParenthesisFunction;
+/*!
+ @class MPParenthesisFunctionLayout
+ @abstract A parenthesis function layout displays a @link
+ //apple_ref/occ/cl/MPParenthesisFunction@/link.
+
+ @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
+/*!
+ @method parenthesisFunction
+ @abstract Returns the @link
+ //apple_ref/occ/cl/MPParenthesisFunction@/link represented
+ by the receiver.
+ */
- (MPParenthesisFunction *)parenthesisFunction;
@end
diff --git a/MathKit/MPParenthesisFunctionLayout.m b/MathKit/MPParenthesisFunctionLayout.m
index 00f83f7..fd400b6 100644
--- a/MathKit/MPParenthesisFunctionLayout.m
+++ b/MathKit/MPParenthesisFunctionLayout.m
@@ -10,9 +10,12 @@
#import "MPParenthesisFunction.h"
+
#define MPParenthesisFunctionOpeningParensOffset 2
#define MPParenthesisFunctionClosingParensOffset 0
+
+
@interface MPParenthesisFunctionLayout ()
- (NSBezierPath *)openingParens;
@@ -25,6 +28,8 @@
@end
+
+
@implementation MPParenthesisFunctionLayout
- (MPParenthesisFunction *)parenthesisFunction
@@ -32,6 +37,7 @@
return (MPParenthesisFunction *)self.function;
}
+
- (NSBezierPath *)openingParens
{
NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{
@@ -60,6 +66,7 @@
return parens;
}
+
- (NSBezierPath *)closingParens
{
NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{
@@ -88,6 +95,7 @@
return parens;
}
+
- (NSBezierPath *)transformedOpeningParens
{
NSBezierPath *parens = self.openingParens.copy;
@@ -95,6 +103,7 @@
return parens;
}
+
- (NSBezierPath *)transformedClosingParens
{
NSBezierPath *parens = self.closingParens.copy;
@@ -102,6 +111,7 @@
return parens;
}
+
- (NSAffineTransform *)parenthesisTransform
{
NSAffineTransform *transform = [NSAffineTransform transform];
@@ -110,6 +120,7 @@
return transform;
}
+
- (CGFloat)scaleFactor
{
NSRect parensBounds = self.openingParens.bounds;
@@ -122,22 +133,25 @@
return expressionHeight / parensHeight;
}
+
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0);
}
+
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{
-#warning Missing Implementation
return [NSIndexPath indexPathWithIndex:0];
}
+
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndex:0];
}
+
- (NSRect)generateBounds
{
NSRect openingParensBounds = self.transformedOpeningParens.bounds;
@@ -159,6 +173,7 @@
return bounds;
}
+
- (void)draw
{
NSBezierPath *openingParens = self.transformedOpeningParens;
diff --git a/MathKit/MPParenthesisTerm.h b/MathKit/MPParenthesisTerm.h
index 185ea47..098a3a1 100644
--- a/MathKit/MPParenthesisTerm.h
+++ b/MathKit/MPParenthesisTerm.h
@@ -9,10 +9,24 @@
#import "MPFunctionTerm.h"
+/*!
+ @header
+ This file contains the MPParenthesisTerm class.
+ */
+
+
@class MPParenthesisTerm;
+/*!
+ @class MPParenthesisTerm
+ @abstract Represents a @link
+ //apple_ref/occ/cl/MPParenthesisFunction@/link.
+
+ @discussion A parenthesis function encapsulates a term and thus prioritizes
+ it in evaluation.
+ */
@interface MPParenthesisTerm : MPFunctionTerm
@end
diff --git a/MathKit/MPParenthesisTerm.m b/MathKit/MPParenthesisTerm.m
index 8e100e1..42f7de6 100644
--- a/MathKit/MPParenthesisTerm.m
+++ b/MathKit/MPParenthesisTerm.m
@@ -10,6 +10,8 @@
#import "MPParsedExpression.h"
+
+
@implementation MPParenthesisTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
diff --git a/MathKit/MPParsedExpression.h b/MathKit/MPParsedExpression.h
index 273a4b0..7f7a6f4 100644
--- a/MathKit/MPParsedExpression.h
+++ b/MathKit/MPParsedExpression.h
@@ -7,8 +7,10 @@
//
-
-@class MPParsedExpression, MPTerm;
+/*!
+ @header
+ This file contains the MPParsedExpression class.
+ */
/*!
@@ -50,6 +52,10 @@ FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
+
+@class MPParsedExpression, MPTerm;
+
+
/*!
@class MPParsedExpression
@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
@link
- //apple_ref/occ/intfp/MPParsedExpression/term@/link.
+ //apple_ref/occ/instp/MPParsedExpression/term@/link.
@param error
If an error occured during evaluation it will be returned
diff --git a/MathKit/MPParsedExpression.m b/MathKit/MPParsedExpression.m
index 7e6793f..7e1ae91 100644
--- a/MathKit/MPParsedExpression.m
+++ b/MathKit/MPParsedExpression.m
@@ -9,13 +9,15 @@
#import "MPParsedExpression.h"
#import "MPToken.h"
-
#import "MPTerm.h"
+
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
+
+
@implementation MPParsedExpression
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
diff --git a/MathKit/MPPowerFunction.h b/MathKit/MPPowerFunction.h
index 846269b..336e4ea 100644
--- a/MathKit/MPPowerFunction.h
+++ b/MathKit/MPPowerFunction.h
@@ -9,6 +9,12 @@
#import "MPFunction.h"
+/*!
+ @header
+ This file contains the MPPowerFunction class.
+ */
+
+
@class MPPowerFunction, MPExpression;
diff --git a/MathKit/MPPowerFunction.m b/MathKit/MPPowerFunction.m
index 5953196..d5a373c 100644
--- a/MathKit/MPPowerFunction.m
+++ b/MathKit/MPPowerFunction.m
@@ -10,15 +10,19 @@
#import "MPExpression.h"
+
+
@implementation MPPowerFunction
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
+
- (NSArray *)childrenAccessors
{
return @[@"exponentExpression"];
}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"^%@", self.exponentExpression.description];
diff --git a/MathKit/MPPowerFunctionLayout.h b/MathKit/MPPowerFunctionLayout.h
index 1c9a2c0..81d3362 100644
--- a/MathKit/MPPowerFunctionLayout.h
+++ b/MathKit/MPPowerFunctionLayout.h
@@ -9,14 +9,46 @@
#import "MPFunctionLayout.h"
+/*!
+ @header
+ This file contains the MPPowerFunctionLayout class.
+ */
+
+
@class MPPowerFunctionLayout, MPPowerFunction;
+/*!
+ @class MPPowerFunctionLayout
+ @abstract A power function layout displays a @link
+ //apple_ref/occ/cl/MPPowerFunction@/link.
+
+ @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:
+ @link
+ //apple_ref/occ/instp/MPPowerFunctionLayout/baseBounds@/link.
+ */
@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;
+/*!
+ @method powerFunction
+ @abstract Returns the @link
+ //apple_ref/occ/cl/MPPowerFunction@/link represented by
+ the receiver.
+ */
- (MPPowerFunction *)powerFunction;
@end
diff --git a/MathKit/MPPowerFunctionLayout.m b/MathKit/MPPowerFunctionLayout.m
index e8c955d..ad1cda7 100644
--- a/MathKit/MPPowerFunctionLayout.m
+++ b/MathKit/MPPowerFunctionLayout.m
@@ -10,9 +10,12 @@
#import "MPPowerFunction.h"
+
#define kPowerFunctionExponentXOffset 1
#define kPowerFunctionTrailingOffset 2
+
+
@implementation MPPowerFunctionLayout
- (NSRect)baseBounds
@@ -23,16 +26,19 @@
return _baseBounds;
}
+
- (MPPowerFunction *)powerFunction
{
return (MPPowerFunction *)self.function;
}
+
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndex:0];
}
+
#warning Broken Power Layout
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
@@ -41,16 +47,19 @@
return NSMakePoint(kPowerFunctionExponentXOffset, y);
}
+
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return YES;
}
+
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{
return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
}
+
- (NSRect)generateBounds
{
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
@@ -59,9 +68,8 @@
return NSMakeRect(0, 0, width, height);
}
+
- (void)draw
-{
- [[NSBezierPath bezierPathWithRect:self.bounds] stroke];
-}
+{}
@end
diff --git a/MathKit/MPPowerTerm.h b/MathKit/MPPowerTerm.h
index 045114b..93f4e66 100644
--- a/MathKit/MPPowerTerm.h
+++ b/MathKit/MPPowerTerm.h
@@ -9,12 +9,33 @@
#import "MPFunctionTerm.h"
+/*!
+ @header
+ This file contains the MPPowerTerm class.
+ */
+
+
@class MPPowerTerm, MPTerm;
+/*!
+ @class MPPowerTerm
+ @abstract Represents a @link
+ //apple_ref/occ/cl/MPPowerFunction@/link.
+
+ @discussion A power function is evaluated using the C pow
+ function.
+ */
@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;
@end
diff --git a/MathKit/MPPowerTerm.m b/MathKit/MPPowerTerm.m
index e846775..fcada00 100644
--- a/MathKit/MPPowerTerm.m
+++ b/MathKit/MPPowerTerm.m
@@ -7,8 +7,11 @@
//
#import "MPPowerTerm.h"
+
#import "MPParsedExpression.h"
+
+
@implementation MPPowerTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
diff --git a/MathKit/MPProductTerm.h b/MathKit/MPProductTerm.h
index 4445a9a..b695aad 100644
--- a/MathKit/MPProductTerm.h
+++ b/MathKit/MPProductTerm.h
@@ -9,6 +9,12 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPProductTerm class.
+ */
+
+
@class MPProductTerm;
@@ -22,7 +28,6 @@
*/
@interface MPProductTerm : MPTerm
-
/*!
@method initWithFactors:
@abstract Initializes a new product term with the specified
diff --git a/MathKit/MPProductTerm.m b/MathKit/MPProductTerm.m
index c0002f7..a3a3f83 100644
--- a/MathKit/MPProductTerm.m
+++ b/MathKit/MPProductTerm.m
@@ -8,17 +8,7 @@
#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
@@ -33,6 +23,7 @@
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
NSDecimalNumber *value = [NSDecimalNumber one];
diff --git a/MathKit/MPRangePath.h b/MathKit/MPRangePath.h
index 3ceecde..48b58fa 100644
--- a/MathKit/MPRangePath.h
+++ b/MathKit/MPRangePath.h
@@ -11,6 +11,8 @@
/*!
@header
+ This file contains the MPRangePath class.
+
The MPRangePath combines an NSIndexPath with a
NSRange. 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
@@ -36,6 +38,7 @@
#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]
+
@class MPRangePath, MPExpression;
@@ -268,7 +271,6 @@
@interface MPExpression (MPRangeExtension)
-
/*!
@method subexpressionWithRangePath:
@abstract Creates a new expression with the symbols in the specified range
diff --git a/MathKit/MPRangePath.m b/MathKit/MPRangePath.m
index 7c34ae7..c03844a 100644
--- a/MathKit/MPRangePath.m
+++ b/MathKit/MPRangePath.m
@@ -12,7 +12,6 @@
@implementation MPRangePath
-
#pragma mark Creation Methods
diff --git a/MathKit/MPSumFunction.h b/MathKit/MPSumFunction.h
index ef0b7f0..dfde2ae 100644
--- a/MathKit/MPSumFunction.h
+++ b/MathKit/MPSumFunction.h
@@ -9,6 +9,12 @@
#import "MPFunction.h"
+/*!
+ @header
+ This file contains the MPSumFunction class.
+ */
+
+
@class MPSumFunction, MPExpression;
diff --git a/MathKit/MPSumFunction.m b/MathKit/MPSumFunction.m
index cedc69a..7bd6580 100644
--- a/MathKit/MPSumFunction.m
+++ b/MathKit/MPSumFunction.m
@@ -15,7 +15,6 @@
@implementation MPSumFunction
-
MPFunctionAccessorImplementation(StartExpression, _startExpression)
MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
MPFunctionAccessorImplementation(SumExpression, _sumExpression)
@@ -26,11 +25,13 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
return @[@"startExpression", @"targetExpression", @"sumExpression"];
}
+
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index
{
return index == 0;
}
+
- (Class)functionTermClass
{
return [MPSumFunctionTerm class];
diff --git a/MathKit/MPSumFunctionLayout.h b/MathKit/MPSumFunctionLayout.h
index e530a19..e2d0b2e 100644
--- a/MathKit/MPSumFunctionLayout.h
+++ b/MathKit/MPSumFunctionLayout.h
@@ -9,12 +9,33 @@
#import "MPFunctionLayout.h"
+/*!
+ @header
+ This file contains the MPSumFunctionLayout class.
+ */
+
+
@class MPSumFunctionLayout, MPSumFunction;
+/*!
+ @class MPSumFunctionLayout
+ @abstract A sum function layout displays a @link
+ //apple_ref/occ/cl/MPSumFunction@/link.
+
+ @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
+/*!
+ @method sumFunction
+ @abstract Returns the @link
+ //apple_ref/occ/cl/MPSumFunction@/link represented by the
+ receiver.
+ */
- (MPSumFunction *)sumFunction;
@end
diff --git a/MathKit/MPSumFunctionLayout.m b/MathKit/MPSumFunctionLayout.m
index ee195ab..525d727 100644
--- a/MathKit/MPSumFunctionLayout.m
+++ b/MathKit/MPSumFunctionLayout.m
@@ -9,14 +9,16 @@
#import "MPSumFunctionLayout.h"
#import "MPSumFunction.h"
-
#import "NSIndexPath+MPAdditions.h"
+
#define kSumFunctionStartExpressionOffset 0
#define kSumFunctionTargetExpressionOffset 0
#define kSumFunctionSumExpressionOffset 3
#define kSumFunctionTrailingOffset 5
+
+
@implementation MPSumFunctionLayout
- (MPSumFunction *)sumFunction
@@ -24,16 +26,19 @@
return (MPSumFunction *)self.function;
}
+
- (NSUInteger)indexOfLeadingChild
{
return 2;
}
+
- (NSUInteger)indexOfTrailingChild
{
return 2;
}
+
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
{
if (index != 2) {
@@ -42,21 +47,25 @@
return [super indexOfChildAfterChildAtIndex:index];
}
+
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
{
return 0;
}
+
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
{
return 1;
}
+
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndex:2];
}
+
- (CTLineRef)line
{
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
@@ -67,6 +76,7 @@
return line;
}
+
- (NSRect)localLineBounds
{
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
@@ -77,6 +87,7 @@
return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height);
}
+
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
NSRect childBounds = [self childLayoutAtIndex:index].bounds;
@@ -101,11 +112,13 @@
return offset;
}
+
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return (index == 0 || index == 1) ? YES : self.usesSmallSize;
}
+
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{
if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) {
@@ -115,6 +128,7 @@
}
}
+
- (NSRect)generateBounds
{
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
@@ -139,6 +153,7 @@
return bounds;
}
+
- (void)draw
{
// Get the current context
diff --git a/MathKit/MPSumFunctionTerm.h b/MathKit/MPSumFunctionTerm.h
index 6768030..1890b01 100644
--- a/MathKit/MPSumFunctionTerm.h
+++ b/MathKit/MPSumFunctionTerm.h
@@ -8,11 +8,30 @@
#import "MPFunctionTerm.h"
+/*!
+ @header
+ This file contains the MPSumFunctionTerm class.
+ */
+
@class MPSumFunctionTerm;
+/*!
+ @class MPSumFunctionTerm
+ @abstract Represents and evaluates a @link
+ //apple_ref/occ/cl/MPSumFunction@/link.
+
+ @discussion A sum function evaluates its sum term n 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 1 after every
+ iteration.
+ */
@interface MPSumFunctionTerm : MPFunctionTerm
@end
diff --git a/MathKit/MPSumFunctionTerm.m b/MathKit/MPSumFunctionTerm.m
index 23b09f1..8c985cd 100644
--- a/MathKit/MPSumFunctionTerm.m
+++ b/MathKit/MPSumFunctionTerm.m
@@ -10,6 +10,8 @@
#import "MPParsedExpression.h"
+
+
@implementation MPSumFunctionTerm
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
diff --git a/MathKit/MPSumTerm.h b/MathKit/MPSumTerm.h
index d99737e..dd53de8 100644
--- a/MathKit/MPSumTerm.h
+++ b/MathKit/MPSumTerm.h
@@ -9,6 +9,12 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPSumTerm class.
+ */
+
+
@class MPSumTerm;
diff --git a/MathKit/MPSumTerm.m b/MathKit/MPSumTerm.m
index 25e16b6..3ec8fc1 100644
--- a/MathKit/MPSumTerm.m
+++ b/MathKit/MPSumTerm.m
@@ -8,10 +8,7 @@
#import "MPSumTerm.h"
-#import "MPParsedExpression.h"
-#import "MPProductTerm.h"
-#import "MPToken.h"
@implementation MPSumTerm
@@ -26,6 +23,7 @@
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
NSDecimalNumber *value = [NSDecimalNumber zero];
diff --git a/MathKit/MPTerm.h b/MathKit/MPTerm.h
index 45accb8..a11b761 100644
--- a/MathKit/MPTerm.h
+++ b/MathKit/MPTerm.h
@@ -9,6 +9,8 @@
/*!
@header
+ This file contains the MPTerm class.
+
The MPTerm class is used to evaluate a mathematical expression. A
term is a purely mathematical representation of a part of an expression. A term
can be defined with three rules:
@@ -66,6 +68,7 @@
#define ReturnNilIfNil(test) if (test == nil) return nil
+
@class MPTerm;
diff --git a/MathKit/MPTerm.m b/MathKit/MPTerm.m
index af567fd..00ff9c6 100644
--- a/MathKit/MPTerm.m
+++ b/MathKit/MPTerm.m
@@ -9,9 +9,10 @@
#import "MPTerm.h"
#import "MPParsedExpression.h"
-#import "MPSumTerm.h"
#import "MPEvaluationContext.h"
+
+
@implementation MPTerm
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
@@ -22,6 +23,7 @@
return result;
}
+
- (BOOL)defineVariable:(NSString *)variableName
value:(NSDecimalNumber *)value
error:(NSError *__autoreleasing *)error
@@ -39,6 +41,7 @@
return couldDefineVariable;
}
+
- (NSDecimalNumber *)valueForVariable:(NSString *)variableName
error:(NSError *__autoreleasing *)error
{
@@ -54,6 +57,7 @@
return value;
}
+
- (void)undefineVariable:(NSString *)variableName
{
[[MPEvaluationContext sharedContext] undefineVariable:variableName];
diff --git a/MathKit/MPToken.h b/MathKit/MPToken.h
index 6a8365b..de090c7 100644
--- a/MathKit/MPToken.h
+++ b/MathKit/MPToken.h
@@ -9,6 +9,8 @@
/*!
@header
+ This file contains the MPToken class and protocol.
+
One way to represent a mathematical expression using the @link
MPExpression@/link class is a sequence of tokens. A token is a logical
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
@abstract The type of a token identifies its behaviour in a mathematical
@@ -103,6 +101,10 @@ typedef enum {
+@class MPToken;
+@protocol MPToken;
+
+
/*!
@protocol MPToken
@abstract Tokens represent logical units in an expresion.
diff --git a/MathKit/MPVariable.h b/MathKit/MPVariable.h
index 9f884cc..882bddf 100644
--- a/MathKit/MPVariable.h
+++ b/MathKit/MPVariable.h
@@ -9,14 +9,44 @@
#import "MPTerm.h"
+/*!
+ @header
+ This file contains the MPVariable class.
+ */
+
+
@class MPVariable;
+/*!
+ @class MPVariable
+ @abstract This class represents a variable.
+
+ @discussion Variables are evaluated in the @link
+ //apple_ref/occ/cl/MPEvaluationContext@/link and generate
+ errors if they are not defined.
+ */
@interface MPVariable : MPTerm
+/*!
+ @method initWithVariableName:
+ @abstract Initializes a MPVariable with the specified
+ variableName
+
+ @param variableName
+ The name of the variable. Must not be nil and must
+ be at least one character long.
+
+ @return A new MPVariable instance.
+ */
- (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */
+
+/*!
+ @property variableName
+ @abstract The receiver's variable name.
+ */
@property (readonly, nonatomic, strong) NSString *variableName;
@end
diff --git a/MathKit/MPVariable.m b/MathKit/MPVariable.m
index 3f8a2a1..c56382f 100644
--- a/MathKit/MPVariable.m
+++ b/MathKit/MPVariable.m
@@ -8,12 +8,7 @@
#import "MPVariable.h"
-#import "MPParsedExpression.h"
-#import "MPToken.h"
-
-#import "MPExpression.h"
-#import "MPEvaluationContext.h"
@implementation MPVariable
@@ -22,11 +17,13 @@
self = [super init];
if (self) {
NSAssert(variableName != nil, @"variableName must not be nil.");
+ NSAssert(variableName.length > 0, @"variableName must be at least one character long.");
_variableName = variableName;
}
return self;
}
+
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
{
return [self valueForVariable:self.variableName
diff --git a/MathKit/MPWhiteView.h b/MathKit/MPWhiteView.h
index 5de222e..eea665d 100644
--- a/MathKit/MPWhiteView.h
+++ b/MathKit/MPWhiteView.h
@@ -7,10 +7,20 @@
//
+/*!
+ @header
+ This file contains the MPWhiteView class.
+ */
+
+
@class MPWhiteView;
+/*!
+ @class MPWhiteView
+ @abstract A NSView that draws a white square in its frame.
+ */
@interface MPWhiteView : NSView
@end
diff --git a/MathKit/MathKit.h b/MathKit/MathKit.h
index b650f34..90d723e 100644
--- a/MathKit/MathKit.h
+++ b/MathKit/MathKit.h
@@ -6,20 +6,19 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
-#import "MPExpression.h"
#import "MPExpression.h"
#import "NSString+MPExpressionElement.h"
#import "MPFunction.h"
-
-#import "MPSumFunction.h"
-#import "MPParenthesisFunction.h"
-#import "MPPowerFunction.h"
-#import "MPFractionFunction.h"
-
#import "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 "MPMathRules.h"
@@ -27,5 +26,13 @@
#import "NSIndexPath+MPAdditions.h"
#import "NSRegularExpression+MPParsingAdditions.h"
+#import "MPExpressionStorage.h"
#import "MPExpressionView.h"
-#import "MPExpressionStorage.h"
\ No newline at end of file
+#import "MPLayout.h"
+#import "MPExpressionLayout.h"
+#import "MPFunctionLayout.h"
+
+#import "MPFractionFunctionLayout.h"
+#import "MPParenthesisFunctionLayout.h"
+#import "MPPowerFunctionLayout.h"
+#import "MPSumFunctionLayout.h"
\ No newline at end of file
diff --git a/MathKit/NSIndexPath+MPAdditions.h b/MathKit/NSIndexPath+MPAdditions.h
index 93b9000..141e0e7 100644
--- a/MathKit/NSIndexPath+MPAdditions.h
+++ b/MathKit/NSIndexPath+MPAdditions.h
@@ -7,6 +7,12 @@
//
+/*!
+ @header
+ This file contains the NSIndexPath(MPAdditions) category.
+ */
+
+
/*!
@category NSIndexPath (MPAdditions)
@@ -15,7 +21,6 @@
*/
@interface NSIndexPath (MPAdditions)
-
/*!
@property firstIndex
@abstract The first index from the receiver.
diff --git a/MathKit/NSIndexPath+MPAdditions.m b/MathKit/NSIndexPath+MPAdditions.m
index 032f1ed..4a8751f 100644
--- a/MathKit/NSIndexPath+MPAdditions.m
+++ b/MathKit/NSIndexPath+MPAdditions.m
@@ -12,7 +12,6 @@
@implementation NSIndexPath (MPAdditions)
-
- (NSUInteger)firstIndex
{
return [self indexAtPosition:0];
diff --git a/MathKit/NSRegularExpression+MPParsingAdditions.h b/MathKit/NSRegularExpression+MPParsingAdditions.h
index 351a127..5194dc0 100644
--- a/MathKit/NSRegularExpression+MPParsingAdditions.h
+++ b/MathKit/NSRegularExpression+MPParsingAdditions.h
@@ -7,6 +7,13 @@
//
+/*!
+ @header
+ This file contains the NSRegularExpression(MPParsingAdditions)
+ category.
+ */
+
+
/*!
@category NSRegularExpression (MPParsingAdditions)
@@ -15,7 +22,6 @@
*/
@interface NSRegularExpression (MPParsingAdditions)
-
/*!
@method firstMathInString:
@abstract Returns the first match of the regular expression within the
diff --git a/MathKit/NSString+MPExpressionElement.h b/MathKit/NSString+MPExpressionElement.h
index be63994..d4baab0 100644
--- a/MathKit/NSString+MPExpressionElement.h
+++ b/MathKit/NSString+MPExpressionElement.h
@@ -9,11 +9,18 @@
#import "MPExpression.h"
+/*!
+ @header
+ This file contains the NSString(MPExpressionElement) category.
+ */
+
+
/*!
@category NSString (MPExpressionElement)
- @abstract This category adds @link MPExpressionElement@/link
- protocol conformance to the NSString class.
+ @abstract This category adds @link
+ //apple_ref/occ/intf/MPExpressionElement@/link protocol
+ conformance to the NSString class.
*/
@interface NSString (MPExpressionElement)
diff --git a/MathPad.xcodeproj/project.pbxproj b/MathPad.xcodeproj/project.pbxproj
index f6f3823..e330a88 100644
--- a/MathPad.xcodeproj/project.pbxproj
+++ b/MathPad.xcodeproj/project.pbxproj
@@ -253,8 +253,6 @@
3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = ""; };
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = ""; };
- 3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = ""; };
- 3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = ""; };
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; };
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";
sourceTree = "";
};
- 3BC4661819B3692A0033F13A /* Temp */ = {
- isa = PBXGroup;
- children = (
- 3BC4661519B365070033F13A /* MPArrayCache.h */,
- 3BC4661619B365070033F13A /* MPArrayCache.m */,
- );
- name = Temp;
- sourceTree = "";
- };
3BC46B4B19B38CB60033F13A /* Base Expression Classes */ = {
isa = PBXGroup;
children = (
@@ -510,22 +499,22 @@
3B6338671A3A4C2B00698BFB /* MPParsedExpression.m */,
3B63387A1A3A4C7E00698BFB /* MPTerm.h */,
3B63387B1A3A4C7E00698BFB /* MPTerm.m */,
- 3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
- 3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
3B6338781A3A4C7E00698BFB /* MPSumTerm.h */,
3B6338791A3A4C7E00698BFB /* MPSumTerm.m */,
3B63386A1A3A4C2B00698BFB /* MPProductTerm.h */,
3B63386B1A3A4C2B00698BFB /* MPProductTerm.m */,
- 3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */,
- 3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */,
+ 3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
+ 3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
+ 3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */,
+ 3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */,
3B6338881A3A4CAF00698BFB /* MPElementaryFunctionTerm.h */,
3B6338891A3A4CAF00698BFB /* MPElementaryFunctionTerm.m */,
3B6338621A3A4C2B00698BFB /* MPNumber.h */,
3B6338631A3A4C2B00698BFB /* MPNumber.m */,
3B63387C1A3A4C7E00698BFB /* MPVariable.h */,
3B63387D1A3A4C7E00698BFB /* MPVariable.m */,
- 3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */,
- 3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */,
+ 3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */,
+ 3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */,
3B6338901A3A4CCA00698BFB /* MPEvaluationContext.h */,
3B6338911A3A4CCA00698BFB /* MPEvaluationContext.m */,
3B6338941A3A4CD100698BFB /* MPMathRules.h */,
@@ -538,14 +527,14 @@
3BEFF75F1A17FF5C00301C0C /* Functions */ = {
isa = PBXGroup;
children = (
- 3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */,
- 3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */,
+ 3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */,
+ 3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */,
3B6338641A3A4C2B00698BFB /* MPParenthesisTerm.h */,
3B6338651A3A4C2B00698BFB /* MPParenthesisTerm.m */,
3B6338681A3A4C2B00698BFB /* MPPowerTerm.h */,
3B6338691A3A4C2B00698BFB /* MPPowerTerm.m */,
- 3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */,
- 3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */,
+ 3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */,
+ 3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */,
);
name = Functions;
sourceTree = "";
@@ -598,7 +587,6 @@
3BF9977418DE623E009CF6C4 /* MathPad */ = {
isa = PBXGroup;
children = (
- 3BC4661819B3692A0033F13A /* Temp */,
3BF9978018DE623E009CF6C4 /* MPDocument.h */,
3BF9978118DE623E009CF6C4 /* MPDocument.m */,
3B87E353190082E200259938 /* Resources */,
diff --git a/MathPad/MPArrayCache.h b/MathPad/MPArrayCache.h
deleted file mode 100644
index f8fb8ef..0000000
--- a/MathPad/MPArrayCache.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// MPArrayCache.h
-// MathPad
-//
-// Created by Kim Wittenburg on 31.08.14.
-// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
-//
-
-#import
-
-// 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
diff --git a/MathPad/MPArrayCache.m b/MathPad/MPArrayCache.m
deleted file mode 100644
index 50116c4..0000000
--- a/MathPad/MPArrayCache.m
+++ /dev/null
@@ -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
diff --git a/MathPad/MPDocument.h b/MathPad/MPDocument.h
index 425a88a..8fef23c 100644
--- a/MathPad/MPDocument.h
+++ b/MathPad/MPDocument.h
@@ -8,11 +8,39 @@
#import
+
+
+/*!
+ @class MPDocument
+ @abstract This class is the document class that displays the MathPad user
+ interface.
+ */
@interface MPDocument : NSDocument
+
+/*!
+ @property expressionView
+ @abstract The main expression view.
+ */
@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;
+
+/*!
+ @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;
@end
diff --git a/MathPad/MPDocument.m b/MathPad/MPDocument.m
index 03d4d83..b7d3453 100644
--- a/MathPad/MPDocument.m
+++ b/MathPad/MPDocument.m
@@ -8,9 +8,11 @@
#import "MPDocument.h"
-#import "MPParsedExpression.h"
-@implementation MPDocument
+
+@implementation MPDocument {
+ MPExpression *loadedExpression;
+}
- (id)init
{
@@ -21,46 +23,47 @@
return self;
}
+
- (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";
}
+
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
self.expressionView.target = self;
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
{
return YES;
}
+
- (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.
- // 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;
+ return [NSKeyedArchiver archivedDataWithRootObject:self.expressionView.expressionStorage];
}
+
- (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.
- // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
- // 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;
+ MPExpression *expression = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+ loadedExpression = expression;
return YES;
}
+
#pragma mark Actions
+
+
- (IBAction)evaluateExpression:(id)sender {
NSArray *errors;
MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors];
@@ -74,4 +77,5 @@
self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @"";
}
+
@end