Added Lots of Documentation
Added some nice to haves Improved and Unified General Code Layout
This commit is contained in:
@@ -9,15 +9,63 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPElementaryFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPElementaryFunctionTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPElementaryFunctionTerm
|
||||
@abstract A <code>MPElementaryFunction</code> 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 <code>"sin"</code>, <code>"cos"</code>, <code>"tan"</code>,
|
||||
<code>"asin"</code>, <code>"arcsin"</code>, <code>"acos"</code>,
|
||||
<code>"arccos"</code>, <code>"atan"</code>,
|
||||
<code>"arctan"</code>, <code>"log"</code>, <code>"lg"</code> and
|
||||
<code>"ln"</code>.
|
||||
|
||||
@param term
|
||||
The term that should be evaluated using <code>function</code>.
|
||||
|
||||
@return A new <code>MPElementaryFunctionTerm</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithFunctionIdentifier:(NSString *)function
|
||||
term:(MPTerm *)term; /* designated initializer */
|
||||
|
||||
|
||||
/*!
|
||||
@property functionIdentifier
|
||||
@abstract The identifier of the receiver.
|
||||
|
||||
@discussion The identifier of a function is the string of letters it is
|
||||
mathematically defined of (e.g. <code>"sin"</code> for the sine
|
||||
function).
|
||||
*/
|
||||
@property (readonly, nonatomic, copy) NSString *functionIdentifier;
|
||||
|
||||
|
||||
/*!
|
||||
@property term
|
||||
@abstract The receiver's term.
|
||||
|
||||
@discussion Depending on the actual function the value of the
|
||||
<code>term</code> may be converted from radians into degrees.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) MPTerm *term;
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
|
||||
@@ -7,21 +7,117 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPEvaluationContext</code> class.
|
||||
|
||||
The evaluation context is organized in so called levels. Every level of a
|
||||
context contains a unique set of variables. Only the highest level is
|
||||
accessible. Before any lower level can be accessed the higher ones have to be
|
||||
discarded. A higher level can not define variables that are already defined in
|
||||
a lower level. Similarly variables can only be undefined if they are defined at
|
||||
the current level. Discarding (popping) a level always undefines all variables
|
||||
defined on that level.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPEvaluationContext;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPEvaluationContext
|
||||
@abstract The evaluation context maintains a map of variables and
|
||||
associates them with a certain level.
|
||||
|
||||
@discussion Different levels are nested using <code>@link
|
||||
//apple_ref/occ/instm/MPEvaluationContext/push@/link</code> and
|
||||
<code>@link
|
||||
//apple_ref/occ/instm/MPEvaluationContext/pop@/link</code>
|
||||
messages.
|
||||
|
||||
<code>MPEvaluationContext</code> is a singletone class. There can
|
||||
only exist one instance (the shared instance) at any time.
|
||||
*/
|
||||
@interface MPEvaluationContext : NSObject
|
||||
|
||||
/*!
|
||||
@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 <code>YES</code> if the variable was defined successfully,
|
||||
<code>NO</code> if it is already defined in a lower level.
|
||||
*/
|
||||
- (BOOL)defineVariable:(NSString *)variable
|
||||
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 <code>variable</code> has not been defined previously this
|
||||
method returns <code>nil</code>.
|
||||
|
||||
@param variable
|
||||
The variable whose value is to be retrieved.
|
||||
|
||||
@return The value <code>variable</code> was defined with or
|
||||
<code>nil</code> if it was not defined.
|
||||
*/
|
||||
- (NSDecimalNumber *)valueForVariable:(NSString *)variable;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpression</code> class and the
|
||||
<code>MPExpressionElement</code> protocol.
|
||||
|
||||
The <code>MPExpression</code> class is used to represent a mathematical
|
||||
expression. It is used in the storage layer of the MathKit Expression System. An
|
||||
instance of the <code>MPExpression</code> class stores all contents related to
|
||||
@@ -108,6 +111,58 @@
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementException
|
||||
@abstract Name for an exception that is raised if an invalid element is
|
||||
added to an expression.
|
||||
|
||||
@discussion This exception may be raised during initialization of an
|
||||
expression or when the expression is mutated. This exception
|
||||
contains the invalid element in its <code>userInfo</code>
|
||||
dictionary. You can query it using the
|
||||
<code>MPIllegalElementExceptionElementKey</code> key.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementExceptionElementKey
|
||||
@abstract Predefined key for an invalid element that caused a
|
||||
<code>MPIllegalElementException</code> to be raised.
|
||||
|
||||
@discussion The invalid element can be of any type. Numbers and structs are
|
||||
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
|
||||
instance respectively.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
|
||||
|
||||
|
||||
/*!
|
||||
@typedef MPReferenceFrame
|
||||
@abstract A reference frame specifies the way an <i>item</i> is to be
|
||||
interpreted.
|
||||
|
||||
@constant MPElementReferenceFrame
|
||||
Specifies that items should be interpreted as elements.
|
||||
|
||||
@constant MPSymbolReferenceFrame
|
||||
Specifies that items should be interpreted as symbols.
|
||||
|
||||
@constant MPTokenReferenceFrame
|
||||
Specifies that items should be interpreted as tokens.
|
||||
*/
|
||||
typedef enum {
|
||||
MPElementReferenceFrame,
|
||||
MPSymbolReferenceFrame,
|
||||
MPTokenReferenceFrame
|
||||
} MPReferenceFrame;
|
||||
|
||||
|
||||
|
||||
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
|
||||
@protocol MPExpressionElement, MPToken;
|
||||
|
||||
|
||||
/*!
|
||||
@protocol MPExpressionElement
|
||||
@abstract This protocol defines the functionality an element in an
|
||||
@@ -161,58 +216,6 @@
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementException
|
||||
@abstract Name for an exception that is raised if an invalid element is
|
||||
added to an expression.
|
||||
|
||||
@discussion This exception may be raised during initialization of an
|
||||
expression or when the expression is mutated. This exception
|
||||
contains the invalid element in its <code>userInfo</code>
|
||||
dictionary. You can query it using the
|
||||
<code>MPIllegalElementExceptionElementKey</code> key.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
|
||||
|
||||
|
||||
/*!
|
||||
@const MPIllegalElementExceptionElementKey
|
||||
@abstract Predefined key for an invalid element that caused a
|
||||
<code>MPIllegalElementException</code> to be raised.
|
||||
|
||||
@discussion The invalid element can be of any type. Numbers and structs are
|
||||
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
|
||||
instance respectively.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
|
||||
|
||||
|
||||
/*!
|
||||
@typedef MPReferenceFrame
|
||||
@abstract A reference frame specifies the way an <i>item</i> is to be
|
||||
interpreted.
|
||||
|
||||
@constant MPElementReferenceFrame
|
||||
Specifies that items should be interpreted as elements.
|
||||
|
||||
@constant MPSymbolReferenceFrame
|
||||
Specifies that items should be interpreted as symbols.
|
||||
|
||||
@constant MPTokenReferenceFrame
|
||||
Specifies that items should be interpreted as tokens.
|
||||
*/
|
||||
typedef enum {
|
||||
MPElementReferenceFrame,
|
||||
MPSymbolReferenceFrame,
|
||||
MPTokenReferenceFrame
|
||||
} MPReferenceFrame;
|
||||
|
||||
|
||||
|
||||
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
|
||||
@protocol MPExpressionElement, MPToken;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPExpression
|
||||
@abstract A <code>MPExpression</code> instance represents a mathematical
|
||||
|
||||
@@ -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<MPExpressionElement>)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) {
|
||||
|
||||
@@ -9,14 +9,53 @@
|
||||
#import "MPLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpressionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPExpressionLayout, MPExpression, MPFunctionLayout;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPExpressionLayout
|
||||
@abstract An expression layout draws an <code>@link
|
||||
//apple_ref/occ/cl/MPExpression@/link</code>.
|
||||
|
||||
@discussion For more information on the layout system see the documentation
|
||||
on the <code>@link //apple_ref/occ/cl/MPLayout@/link</code>
|
||||
class.
|
||||
*/
|
||||
@interface MPExpressionLayout : MPLayout
|
||||
|
||||
/*!
|
||||
@method initWithExpression:parent:
|
||||
@abstract Initializes an expression layout with the specified expression
|
||||
and parent layout.
|
||||
|
||||
@discussion To initialize a layout for the root expression of an expression
|
||||
tree specify <code>nil</code> as the <code>parent</code>.
|
||||
|
||||
@param expression
|
||||
The expression that sould be represented by the receiver. Must
|
||||
not be <code>ni</code>.
|
||||
|
||||
@param parent
|
||||
The parent layout of the receiver. Specify <code>nil</code> to
|
||||
initalize a root expression layout.
|
||||
|
||||
@return A newly initialized expression layout.
|
||||
*/
|
||||
- (instancetype)initWithExpression:(MPExpression *)expression
|
||||
parent:(MPFunctionLayout *)parent;
|
||||
|
||||
|
||||
/*!
|
||||
@property expression
|
||||
@abstract The expression represented by the receiver.
|
||||
*/
|
||||
@property (readonly, nonatomic, weak) MPExpression *expression;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpressionParser</code> class.
|
||||
|
||||
The <code>MPExpressionParser</code> converts a <code>@link
|
||||
MPExpression@/link</code> instance into a <code>@link
|
||||
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance. The rules that are
|
||||
|
||||
@@ -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<MPToken>)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<MPToken>)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<MPToken>)token
|
||||
{
|
||||
return [NSDecimalNumber decimalNumberWithString:token.stringValue
|
||||
locale:[NSLocale currentLocale]];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)parseVariable:(id<MPToken>)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];
|
||||
|
||||
@@ -9,13 +9,51 @@
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpressionStorage</code> 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 <code>@link
|
||||
//apple_ref/occ/instp/MPExpressionView/expressionStorage@/link</code>
|
||||
property of the <code>@link
|
||||
//apple_ref/occ/cl/MPExpressionView@/link</code> class is set.
|
||||
|
||||
When this property is set the receiver assumes that the
|
||||
displaying expression view has changed. It then adapts to the
|
||||
properties of the new expression view and discards the root
|
||||
layout.
|
||||
*/
|
||||
@property (nonatomic, weak) MPExpressionView *expressionView;
|
||||
|
||||
|
||||
/*!
|
||||
@property rootLayout
|
||||
@abstract The receiver's root layout.
|
||||
|
||||
@discussion The root layout is the root node of the layout tree that draws
|
||||
the receiver's contents. For more information see the
|
||||
documentation on the <code>@link
|
||||
//apple_ref/occ/cl/MPLayout@/link</code> class.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpressionTokenizer</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPExpressionTokenizer, MPExpression;
|
||||
|
||||
|
||||
@@ -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+)"
|
||||
|
||||
@@ -6,68 +6,195 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
// TODO: Undo/Redo + Delegate (evaluateExpressionView:evaluate...)
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPExpressionView</code> class.
|
||||
|
||||
<h2>The MathKit Expression System</h2>
|
||||
MathKit contains class es that make up the so called <i>expression system</i>.
|
||||
The expression system is divided into three layers: the model, the view and the
|
||||
controller layer. The <code>MPExpressionView</code> class represents the view
|
||||
layer and interacts with the Cocoa <code>NSView</code> system. The model is
|
||||
represented by the classes <code>@link
|
||||
//apple_ref/occ/cl/MPExpression@/link</code> and <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code>. The controller layer between the two
|
||||
consists of the class <code>@link //apple_ref/occ/cl/MPLayout@/link</code> and
|
||||
its subclasses. The purpose of the expression system is presenting expressions
|
||||
to the user and offering possibilities to manipulate and work with expressions.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPExpressionView, MPExpressionStorage, MPFunction, MPRangePath;
|
||||
//@protocol MPExpressionViewDelegate;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPExpressionView
|
||||
@abstract The <code>MPExpressionView</code> class is the front-end class of
|
||||
the MathKit expression system.
|
||||
|
||||
@discussion The class draws the expression managed by the the back-end class
|
||||
<code>@link //apple_ref/occ/cl/MPExpressionStorage@/link</code>
|
||||
and is the interface between Application Kit's view system and
|
||||
the MathKit expression system.
|
||||
*/
|
||||
@interface MPExpressionView : NSView <NSUserInterfaceValidations>
|
||||
|
||||
#pragma mark Creation Methods
|
||||
/*!
|
||||
@methodgroup Creation Methods
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method initWithFrame:
|
||||
@abstract Initializes a <code>MPExpressionView</code> instance.
|
||||
|
||||
@discussion This method sets up all layers of the expression system including
|
||||
an empty expression storage.
|
||||
|
||||
This method is the designated initializer of the
|
||||
<code>MPExpressionView</code> class.
|
||||
|
||||
@param frameRect
|
||||
The frame rectangle for the created view.
|
||||
|
||||
@return An initialized <code>MPExpressionView</code> or <code>nil</code>
|
||||
if the object could not be created.
|
||||
*/
|
||||
- (id)initWithFrame:(NSRect)frameRect;
|
||||
|
||||
|
||||
#pragma mark Properties
|
||||
/*!
|
||||
@methodgroup Properties
|
||||
*/
|
||||
|
||||
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
|
||||
|
||||
//@property (nonatomic, weak) id<MPExpressionViewDelegate> delegate;
|
||||
/*!
|
||||
@property expressionStorage
|
||||
@abstract The receiver's expression storage.
|
||||
|
||||
@discussion The expression storage maintains the contents of the receiver.
|
||||
User interactions change the underlying data. If the expression
|
||||
storage or the underlying expression change the expression
|
||||
storage updates the receiver.
|
||||
*/
|
||||
@property (nonatomic, strong) MPExpressionStorage *expressionStorage;
|
||||
|
||||
|
||||
/*!
|
||||
@property selection
|
||||
@abstract The receiver's selection.
|
||||
*/
|
||||
@property (nonatomic, strong) MPRangePath *selection;
|
||||
|
||||
|
||||
/*!
|
||||
@property mathError
|
||||
@abstract If set the receiver will display the localized description of the
|
||||
specified error.
|
||||
|
||||
@discussion There can only be one math error at a time. This property should
|
||||
not be set simutaneously with the <code>@link
|
||||
//apple_ref/occ/instp/MPExpressionView/syntaxErrors@/link</code>.
|
||||
*/
|
||||
@property (nonatomic, strong) NSError *mathError;
|
||||
|
||||
|
||||
/*!
|
||||
@property syntaxErrors
|
||||
@abstract If set the receiver will display a dropdown list of syntax
|
||||
errors.
|
||||
|
||||
@discussion The array must only contain <code>NSError</code> instances. In
|
||||
addition to that every instance must contain a valid
|
||||
<code>NSIndexPath</code> for the <code>@link
|
||||
//apple_ref/c/data/MPPathToExpressionKey@/link</code> in its
|
||||
<code>userDict</code> acompanied by a <code>NSRange</code>
|
||||
wrapped in a <code>NSValue</code> instance for the <code>@link
|
||||
//apple_ref/c/data/MPErrorRangeKey@/link</code>.
|
||||
|
||||
If the user selects an item from the dropdown list the part of
|
||||
the expression displayed by the receiver will be selected that is
|
||||
specified by the values in the corresponding <code>NSError</code>
|
||||
instance.
|
||||
*/
|
||||
@property (nonatomic, strong) NSArray *syntaxErrors;
|
||||
|
||||
|
||||
/*!
|
||||
@property 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 <i>enter</i>
|
||||
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 <NSObject>
|
||||
//@optional
|
||||
//
|
||||
//#pragma mark Editing
|
||||
//- (MPRangePath *)expressionView:(MPExpressionView *)expressionView willChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
|
||||
//- (void)expressionView:(MPExpressionView *)expressionView didChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
|
||||
//
|
||||
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldChangeSymbolsInRangePath:(MPRangePath *)rangePath replacementElements:(NSArray *)replacement;
|
||||
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldInsertFunction:(MPFunction *)function replacingRangePath:(MPRangePath *)currentSelection;
|
||||
//- (void)expressionViewDidChange:(MPExpressionView *)expressionView;
|
||||
//
|
||||
//- (BOOL)expressionViewShouldBeginEditing:(MPExpressionView *)expressionView;
|
||||
//- (BOOL)expressionViewShouldEndEditing:(MPExpressionView *)expressionView;
|
||||
//- (void)expressionViewDidBeginEditing:(MPExpressionView *)expressionView;
|
||||
//- (void)expressionDidEndEditing:(MPExpressionView *)expressionView;
|
||||
//
|
||||
//// TODO: Errors...
|
||||
//#pragma mark Evaluation
|
||||
//
|
||||
//- (BOOL)expressionViewShouldEvaluate:(MPExpressionView *)expressionView;
|
||||
//- (void)expressionViewDidEvaluate:(MPExpressionView *)expressionView;
|
||||
//
|
||||
//@end
|
||||
@@ -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<MPExpressionElement> symbol = [self.expressionStorage symbolAtIndex:self.selection.location.lastIndex-1];
|
||||
if ([symbol isEqual:@"p"]) {
|
||||
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], self.selection.length+1);
|
||||
characters = @"π";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([characters isEqualToString:@"*"]) {
|
||||
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<NSValidatedUserInterfaceItem>)anItem
|
||||
{
|
||||
BOOL degrees = [MPMathRules sharedRules].isUsingDegrees;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFactorialTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFactorialTerm;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFractionFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFractionFunction, MPExpression;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,12 +9,32 @@
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFractionFunctionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFractionFunctionLayout, MPFractionFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPFractionFunctionLayout
|
||||
@abstract A fraction function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
|
||||
|
||||
@discussion The nominator is displayed above the denominator. Between the two
|
||||
children a horizontal bar is drawn.
|
||||
*/
|
||||
@interface MPFractionFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@method fractionFunction
|
||||
@abstract Returns the <code>@link
|
||||
//apple_ref/occ/cl/MPFractionFunction@/link</code> represented by
|
||||
the receiver.
|
||||
*/
|
||||
- (MPFractionFunction *)fractionFunction;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,10 +9,24 @@
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFractionTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFractionTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPFractionTerm
|
||||
@abstract Represens a <code>@link
|
||||
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
|
||||
|
||||
@discussion A fraction is evaluating by dividing the nominator by the
|
||||
denominator.
|
||||
*/
|
||||
@interface MPFractionTerm : MPFunctionTerm
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPFractionTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
#import "MPToken.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFunction(MPToken) category</code>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category MPFunction (MPToken)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFunction</code> class.
|
||||
|
||||
<code>MPFunction</code> is a half abstract class that is designed to be
|
||||
subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called
|
||||
<i>functions</i>) need to regard the following guidelines:
|
||||
@@ -41,7 +43,6 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@define MPFunctionAccessorImplementation
|
||||
@abstract This macro implements the setter of a previously declared
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
@implementation MPFunction
|
||||
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
|
||||
|
||||
@@ -9,53 +9,334 @@
|
||||
#import "MPLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFunctionLayout</code> class.
|
||||
|
||||
The <code>MPFunctionLayout</code> class is an abstract class that implements
|
||||
many of the methods from the layout system. Concrete subclasses implement the
|
||||
actual layout of a specific function and its children. For that reason
|
||||
<code>MPFunctionLayout</code> features a private cache. In that cache any
|
||||
subclass can store objects so they do not need to be recreated everytime the
|
||||
function is rendered.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFunctionLayout, MPFunction, MPExpressionLayout;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPFunctionLayout
|
||||
@abstract A function layout represents a <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code>.
|
||||
|
||||
@discussion <code>MPFunctionLayout</code> is an abstract class that
|
||||
implements many required methods of the layout system. For more
|
||||
information about the layout system see the documentation on the
|
||||
<code>@link //apple_ref/occ/cl/MPLayout@/link</code> class.
|
||||
*/
|
||||
@interface MPFunctionLayout : MPLayout
|
||||
|
||||
/*!
|
||||
@method functionLayoutForFunction:parent:
|
||||
@abstract Creates a function layout that can draw the specified function.
|
||||
|
||||
@discussion This method is the preferred whay to construct function layouts.
|
||||
|
||||
@param function
|
||||
The function to create a layout for. Must not be
|
||||
<code>nil</code>.
|
||||
|
||||
@param parent
|
||||
The parent of the created function layout. May be
|
||||
<code>nil</code>.
|
||||
|
||||
@return A newly created function layout.
|
||||
*/
|
||||
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
|
||||
parent:(MPExpressionLayout *)parent;
|
||||
|
||||
@property (readonly, nonatomic, weak) MPFunction *function;
|
||||
|
||||
@end
|
||||
/*!
|
||||
@method initWithFunction:parent:
|
||||
@abstract Initializes the receiver with the specified function and parent.
|
||||
|
||||
@interface MPFunctionLayout (MPSubclassOverride)
|
||||
@discussion This method should not be called direcly. It is however possible
|
||||
do perform custom initialization in sublcasses by overriding this method.
|
||||
|
||||
This method is the designated initializer of the
|
||||
<code>MPFunctionLayout</code> class.
|
||||
|
||||
@param function
|
||||
The function represented by the receiver. Must not be
|
||||
<code>nil</code>.
|
||||
|
||||
@param parent
|
||||
The parent of the receiver or <code>nil</code> if it does not
|
||||
have one.
|
||||
|
||||
@return A newly initialized <code>MPFunctionLayout</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithFunction:(MPFunction *)function
|
||||
parent:(MPExpressionLayout *)parent;
|
||||
|
||||
#pragma mark Cache Methods
|
||||
|
||||
/*!
|
||||
@property function
|
||||
@abstract The function represented by the receiver.
|
||||
*/
|
||||
@property (readonly, nonatomic, weak) MPFunction *function;
|
||||
|
||||
|
||||
/*!
|
||||
@method lineForPrivateCacheIndex:generator:
|
||||
@abstract Returns the cached line for the specified private index.
|
||||
|
||||
@discussion This is a convenience method that calls <code>@link
|
||||
//apple_ref/occ/instm/MPFunctionLayout/objectForPrivateCacheIndex:generator:@/link</code>.
|
||||
See the documentation on that method for details.
|
||||
|
||||
@param index
|
||||
The index of the line to retrieve. Private cache indexes start at <code>0</code>.
|
||||
|
||||
@param generator
|
||||
A block that generates a line in case there is no cached one.
|
||||
|
||||
@return The cached line or the newly generated one if there was no cache value.
|
||||
*/
|
||||
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
||||
generator:(CTLineRef (^)())generator;
|
||||
|
||||
|
||||
/*!
|
||||
@method objectForPrivateCacheIndex:generator:
|
||||
@abstract Returns the cached value for the specified private index.
|
||||
|
||||
@discussion If there is no object in the private cache for the specified
|
||||
index the specified <code>generator</code> is invoked. The result
|
||||
of the <code>generator</code> is then stored at the specified
|
||||
<code>index</code> and returned.
|
||||
|
||||
@param index
|
||||
The index of the private cache item to retrieve. Private cache
|
||||
indexes start at <code>0</code>.
|
||||
|
||||
@param generator
|
||||
A block that generates an object in case there is no cached one.
|
||||
|
||||
@return The cached object or the newly generated one if there was no
|
||||
cache value.
|
||||
*/
|
||||
- (id)objectForPrivateCacheIndex:(NSUInteger)index
|
||||
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 <code>MPFunctionLayout</code>.
|
||||
*/
|
||||
@interface MPFunctionLayout (MPSubclassOverride)
|
||||
|
||||
/* Apart from the methods below it is recommended (although not required) to
|
||||
* implement a method in the following form:
|
||||
* - (CustomFunctionType *)customFunction
|
||||
* {
|
||||
* return (CustomFunctionType *)self.function;
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method childAtIndexUsesSmallSize:
|
||||
@abstract Determines whether the child expression at the specified index
|
||||
should be drawn using the small font.
|
||||
|
||||
@discussion This method may be overridden to change the default behaviour.
|
||||
|
||||
@param index
|
||||
The index of the child whose size is to be determined.
|
||||
|
||||
@return <code>YES</code> if the receiver's child at the specified
|
||||
<code>index</code> should use the small font, <code>NO</code>
|
||||
otherwise. The default implementation returns <code>NO</code>.
|
||||
*/
|
||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index; // May be implemented
|
||||
- (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 <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
|
||||
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 (<code>0</code> means before, <code>1</code>
|
||||
means after).
|
||||
*/
|
||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point;
|
||||
|
||||
|
||||
/*!
|
||||
@method generateBounds
|
||||
@abstract Implementation requirement from superclass. See <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
|
||||
for details.
|
||||
*/
|
||||
- (NSRect)generateBounds;
|
||||
|
||||
|
||||
/*!
|
||||
@method draw
|
||||
@abstract Implementation requirement from superclass. See <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/draw@/link</code>
|
||||
for details.
|
||||
*/
|
||||
- (void)draw;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfLeadingChild
|
||||
@abstract Identifies the index of the receiver's child that will get
|
||||
selected when the cursor <i>enters</i> the function in the
|
||||
direction of reading.
|
||||
|
||||
@discussion You may implement this method do override the default behaviour.
|
||||
|
||||
@return The index of the leading child. The default implementation
|
||||
returns <code>0</code>.
|
||||
*/
|
||||
- (NSUInteger)indexOfLeadingChild;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfTrailingChild
|
||||
@abstract Identifies the index of the receiver's child that will get
|
||||
selected when the cursor <i>enters</i> the function in the
|
||||
direction opposite to reading.
|
||||
|
||||
@discussion You may implement this method do override the default behaviour.
|
||||
|
||||
@return The index of the trailing child. The default implementation
|
||||
returns <code>0</code>.
|
||||
*/
|
||||
- (NSUInteger)indexOfTrailingChild;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfChildBeforeChildAtIndex:
|
||||
@abstract Identifies the index of the receiver's child that will get
|
||||
selected when the cursor <i>leaves</i> the child at the specified
|
||||
index in the direction opposite to reading.
|
||||
|
||||
@discussion Return <code>NSNotFound</code> from this method if the function
|
||||
should be <i>left</i> when the child at the specified index is
|
||||
<i>left</i>.
|
||||
|
||||
You may implement this method to override the default behaviour.
|
||||
|
||||
@param index
|
||||
The index of the child the caret <i>left</i>.
|
||||
|
||||
@return The index of the child that is to be <i>entered</i> or
|
||||
<code>NSNotFound</code> if there are no more children. The
|
||||
default implementation returns <code>NSNotFound</code>.
|
||||
*/
|
||||
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfChildAfterChildAtIndex:
|
||||
@abstract Identifies the index of the receiver's child that will get
|
||||
selected when the cursor <i>leaves</i> the child at the specified
|
||||
index in the direction of reading.
|
||||
|
||||
@discussion Return <code>NSNotFound</code> from this method if the function
|
||||
should be <i>left</i> when the child at the specified index is
|
||||
<i>left</i>.
|
||||
|
||||
You may implement this method to override the default behaviour.
|
||||
|
||||
@param index
|
||||
The index of the child the caret <i>left</i>.
|
||||
|
||||
@return The index of the child that is to be <i>entered</i> or
|
||||
<code>NSNotFound</code> if there are no more children.
|
||||
*/
|
||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfChildBelowChildAtIndex:
|
||||
@abstract Returns the index of the child that is graphically positioned
|
||||
below the child at the specified index.
|
||||
|
||||
@discussion If there are no other children below the one at the specified
|
||||
index this method should return <code>index</code>.
|
||||
|
||||
You may implement this method to override the default behaviour.
|
||||
|
||||
@param index
|
||||
The index of the child whose neighbor is to be determined.
|
||||
|
||||
@return The index of the child directly below the one at the specified
|
||||
index. The default implementation returns <code>index</code>.
|
||||
*/
|
||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexOfChildAboveChildAtIndex:
|
||||
@abstract Returns the index of the child that is graphically positioned
|
||||
above the child at the specified index.
|
||||
|
||||
@discussion If there are no other children above the one at the specified
|
||||
index this method should return <code>index</code>.
|
||||
|
||||
You may implement this method to override the default behaviour.
|
||||
|
||||
@param index
|
||||
The index of the child whose neighbor is to be determined.
|
||||
|
||||
@return The index of the child directly above the one at the specified
|
||||
index. The default implementation returns <code>index</code>.
|
||||
*/
|
||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexesOfRemainingChildren
|
||||
@abstract Returns the indexes of the receiver's children that should
|
||||
replace the receiver if it is deleted.
|
||||
|
||||
@discussion You may implement this method to override the default behaviour.
|
||||
|
||||
@return The indexes of the children that replace the receiver when it is
|
||||
deleted. The default implementation returns an empty index set.
|
||||
*/
|
||||
- (NSIndexSet *)indexesOfRemainingChildren;
|
||||
|
||||
@end
|
||||
@@ -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];
|
||||
|
||||
@@ -8,6 +8,28 @@
|
||||
|
||||
#import "MPTerm.h"
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFunctionTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@define MPEvaluateExpression
|
||||
@abstract Evaluates the receiver's child at the specified index and assigns
|
||||
the evaluation result to a variable.
|
||||
|
||||
@discussion If errors occur during evaluation this macro returns
|
||||
<code>nil</code>. Also there has to exist a <code>NSError *__autoreleasing *</code> pointer called <code>error</code>.
|
||||
|
||||
@param var
|
||||
The name of the variable that will be declared in this macro. It
|
||||
will be of the type <code>NSDecimalNumber *</code> and contain
|
||||
the result of the evaluation.
|
||||
|
||||
@param index
|
||||
The index of the child to be evaluated.
|
||||
*/
|
||||
#define MPEvaluateExpression(var, index) NSDecimalNumber *var = [[self expressionAtIndex:index] evaluate:error]; if (var == nil) return nil
|
||||
|
||||
|
||||
@@ -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 <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code> instances.
|
||||
*/
|
||||
@interface MPFunctionTerm : MPTerm
|
||||
|
||||
/*!
|
||||
@method initWithFunction:errors:
|
||||
@abstract Initializes a <code>MPFunctionTerm</code> from the specified
|
||||
<code>@link //apple_ref/occ/cl/MPFunction@/link</code> instance.
|
||||
|
||||
@discussion This method parses the children of the specified
|
||||
<code>function</code> and stores the respective parsed
|
||||
expressions. They are accessible using the <code>@link
|
||||
//apple_ref/occ/instm/MPFunctionTerm/expressionAtIndex:@/link</code>
|
||||
method.
|
||||
|
||||
Whether the children should contain a variable definition is
|
||||
determined using the <code>@link
|
||||
//apple_ref/occ/instm/MPFunction/expectsVariableDefinitionInChildAtIndex:@/link</code>
|
||||
|
||||
@param function
|
||||
The function to initialize the receiver with. This method parses
|
||||
the function's children. If any of them contain syntax errors
|
||||
they are returned indirectly through the <code>errors</code>
|
||||
parameter.
|
||||
|
||||
@param errors
|
||||
If any of the <code>function</code>'s children contain syntax
|
||||
errors they are returned indirectly through this parameter. In
|
||||
that case the method returns <code>nil</code>. If there are no
|
||||
errors the parameter is not modified. This parameter is never set
|
||||
to an empty array.
|
||||
|
||||
@return A new <code>MPFunctionTerm</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithFunction:(MPFunction *)function
|
||||
errors:(NSArray *__autoreleasing *)errors; /* designated initializer */
|
||||
|
||||
|
||||
/*!
|
||||
@method expressionAtIndex:
|
||||
@abstract Returns a <code>@link
|
||||
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance that
|
||||
represents the child at <code>anIndex</code> of the function that
|
||||
is represented by the receiver.
|
||||
|
||||
@param anIndex
|
||||
The index of the child.
|
||||
|
||||
@return A parsed expression that represents th
|
||||
*/
|
||||
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -7,19 +7,74 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPFunctionsViewController</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPFunctionsViewController, MPFunctionsCollectionView;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPFunctionsViewController
|
||||
@abstract Controls a view from which the user can select function to be
|
||||
inserted into a <code>@link
|
||||
//apple_ref/occ/cl/MPExpressionView@/link</code>.
|
||||
|
||||
@discussion The view contain a <code>NSCollectionView</code> displaying the
|
||||
prototypes an a <code>NSTextField</code> displaying a description
|
||||
of the currently selected prototype.
|
||||
*/
|
||||
@interface MPFunctionsViewController : NSViewController
|
||||
|
||||
- (id)init;
|
||||
/*!
|
||||
@property collectionView
|
||||
@abstract The <code>NSCollectionView</code> that displays the function
|
||||
prototypes.
|
||||
*/
|
||||
@property (weak) IBOutlet NSCollectionView *collectionView;
|
||||
|
||||
@property (weak) IBOutlet MPFunctionsCollectionView *collectionView;
|
||||
|
||||
/*!
|
||||
@property functionPrototypes
|
||||
@abstract Contains the <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code> objects that get
|
||||
inserted into an expression if selected.
|
||||
|
||||
@discussion Every object in the array must be an <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code> instance.
|
||||
*/
|
||||
@property (nonatomic, strong) NSArray *functionPrototypes;
|
||||
|
||||
|
||||
/*!
|
||||
@property 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,12 +6,82 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPLayout</code> class.
|
||||
|
||||
The <code>MPLayout</code> class renders an expression to be displayed in an
|
||||
<code>@link //apple_ref/occ/cl/MPExpressionView@/link</code>. There are multiple
|
||||
subclasses of <code>MPLayout</code> the most important of which are <code>@link
|
||||
//apple_ref/occ/cl/MPExpressionLayout@/link</code> and <code>@link
|
||||
//apple_ref/occ/cl/MPFunctionLayout@/link</code> for rendering expressions and
|
||||
functions respectively.
|
||||
|
||||
Both types of layouts can occur in two different sizes: normal and small. The
|
||||
root expression ueses the normal size. Depending on the concrete subclass of
|
||||
<code>@link //apple_ref/occ/cl/MPFunctionLayout@/link</code> that is used a
|
||||
function's children (and any subsequent children) may be rendered using the
|
||||
small size. Also there are two different fonts available for layouts: a normal
|
||||
and a special font. The special font should only be used to emphasize special
|
||||
content that uses the normal font.
|
||||
|
||||
If an expression is empty an empty box is rendered instead. That box (called the
|
||||
<i>empty box</i> uses the font metrics of the empty expression it represents.
|
||||
That means it uses different sizes depending on whether the expression uses a
|
||||
small or normal sized font. Also it is drawn a little smaller than the actual
|
||||
box's height is because it looks nicer. Depending on your needs you can query
|
||||
the metrics of the empty box using the <code>kMPEmptyBox...</code> or
|
||||
<code>kMPEmptyBoxDrawing...</code> macros. These macros must not be used outside
|
||||
of the <code>MPLayout</code> class or any of it subclasses.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@define kMPEmptyBoxWidth
|
||||
@abstract The width of the empty box.
|
||||
*/
|
||||
#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0)
|
||||
|
||||
|
||||
/*!
|
||||
@define 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 <code>MPLayout</code> class maintains a generic cache. It is
|
||||
used to store child layouts and actual cache information.
|
||||
|
||||
Every layout instance represents either an expression or a
|
||||
function. The instance's children represent the children of the
|
||||
represented object.
|
||||
*/
|
||||
@interface MPLayout : NSObject
|
||||
|
||||
#pragma mark Creation Methods
|
||||
/*!
|
||||
@methodgroup Creation Methods
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method init
|
||||
@abstract Initializes the receiver.
|
||||
|
||||
@discussion This method should only be used to initialize the expression
|
||||
layout that renders the root layout.
|
||||
|
||||
This method is the designated initializer of the
|
||||
<code>MPLayout</code> class.
|
||||
|
||||
@return A newly initialized <code>MPLayout</code> instance.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
|
||||
/*!
|
||||
@method initWithParent:
|
||||
@abstract Initializes the receiver with the specified parent.
|
||||
|
||||
@discussion This method should generally be used when initializing layout
|
||||
instances. In most cases it should be called at some point in the
|
||||
implementation of the <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/childLayoutAtIndex:@/link</code>
|
||||
method.
|
||||
|
||||
@param parent
|
||||
The receiver's parent.
|
||||
|
||||
@return A newly initialized <code>MPLayout</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithParent:(MPLayout *)parent;
|
||||
|
||||
#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 <code>NSFont</code> object that represents the normal
|
||||
font in the specified <code>size</code>.
|
||||
|
||||
@discussion Instead of this method you want to use <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/font@/link</code> instead in most
|
||||
cases.
|
||||
|
||||
@param size
|
||||
The size of the font.
|
||||
|
||||
@return A <code>NSFont</code> object that can be used to render textual
|
||||
content of the receiver in the specified <code>size</code>.
|
||||
*/
|
||||
- (NSFont *)normalFontWithSize:(CGFloat)size;
|
||||
|
||||
|
||||
/*!
|
||||
@method specialFontWithSize:
|
||||
@abstract Returns a <code>NSFont</code> object that represents the special
|
||||
font in the specified <code>size</code>.
|
||||
|
||||
@discussion Use fonts returned by this method only if you need to emphasize
|
||||
text that is surrounded by other text that uses the normal font.
|
||||
|
||||
@param size
|
||||
The size of the font.
|
||||
|
||||
@return A <code>NSFont</code> object that can be used to render special
|
||||
textual content of the receiver in the specified
|
||||
<code>size</code>.
|
||||
*/
|
||||
- (NSFont *)specialFontWithSize:(CGFloat)size;
|
||||
|
||||
|
||||
/*!
|
||||
@method contextInferredFontSize
|
||||
@abstract Returns the appropriate font size for the receiver based on the
|
||||
context it will be rendered in.
|
||||
*/
|
||||
- (CGFloat)contextInferredFontSize;
|
||||
|
||||
|
||||
/*!
|
||||
@method normalFontSize
|
||||
@abstract Returns the font size that is used in the root expression.
|
||||
*/
|
||||
- (CGFloat)normalFontSize;
|
||||
|
||||
|
||||
/*!
|
||||
@method smallFontSize
|
||||
@abstract Returns the font size that is used in expressions that are using
|
||||
the small size.
|
||||
*/
|
||||
- (CGFloat)smallFontSize;
|
||||
|
||||
|
||||
#pragma mark Cache Tree
|
||||
/*!
|
||||
@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 <code>index</code> or
|
||||
creates one if it does not exist.
|
||||
|
||||
@discussion This method only returns <code>nil</code> if the
|
||||
<code>generator</code> returns <code>nil</code>.
|
||||
|
||||
@param index
|
||||
The index of the cached object to retrieve.
|
||||
|
||||
@param generator
|
||||
This block is executed if there is no cached object at
|
||||
<code>index</code>. The result of this block is then cached at
|
||||
the <code>index</code>.
|
||||
|
||||
@return The cached object for <code>index</code> or the result of the
|
||||
<code>generator</code> if there is no cached object.
|
||||
*/
|
||||
- (id)cachableObjectForIndex:(NSUInteger)index
|
||||
generator:(id(^)())generator;
|
||||
|
||||
// Clearing Caches
|
||||
|
||||
/*!
|
||||
@method clearCacheInRange:replacementLength:
|
||||
@abstract Removes the objects in the specified from the cache and moves all
|
||||
objects at subsequent indexes.
|
||||
|
||||
@discussion The objects at subsequent indexes are moved
|
||||
<code>replacementLength - range.length</code> places. If
|
||||
<code>replacementLength</code> is greater than
|
||||
<code>range.length</code> they are moved to smaller indexes.
|
||||
|
||||
@param range
|
||||
The range of objects to be removed.
|
||||
|
||||
@param replacementLength
|
||||
The number of indexes replacing the ones specified by
|
||||
<code>range</code>.
|
||||
*/
|
||||
- (void)clearCacheInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
|
||||
|
||||
/*!
|
||||
@method invalidate
|
||||
@abstract Invalidates the receiver.
|
||||
|
||||
@discussion Invalidating the receiver causes him to discard all cached
|
||||
information about itself and send a <code>invalidate</code>
|
||||
message to its parent. The cached information about the
|
||||
receiver's children is <b>not</b> discarded. Use <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/clearCacheInRange:replacementLength:@/link</code>
|
||||
for that purpose.
|
||||
*/
|
||||
- (void)invalidate;
|
||||
|
||||
|
||||
/*!
|
||||
@method childLayoutAtIndexPath:
|
||||
@abstract Returns the child layout at the specified index path.
|
||||
|
||||
@discussion If the <code>indexPath</code> is empty the receiver is returned.
|
||||
If the child layout the index path specifies is not yet created
|
||||
this method should do so and add it to the cache of the
|
||||
respective parent layout.
|
||||
|
||||
@param indexPath
|
||||
The index path of the child.
|
||||
|
||||
@return The child at the specified index path.
|
||||
*/
|
||||
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
#pragma mark Calculation and Drawing Methods
|
||||
|
||||
#pragma mark Rendering Methods
|
||||
/*!
|
||||
@methodgroup Rendering Methods
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@method createLineForString:
|
||||
@abstract Renders the specified string.
|
||||
|
||||
@discussion This method uses the normal font and the context inferred font
|
||||
size to render the string. In most cases this is the appropriate
|
||||
method for rendering textual content in a layout.
|
||||
|
||||
@param aString
|
||||
The string to be rendered.
|
||||
|
||||
@return A <code>CTLineRef</code> representing the rendered string.
|
||||
*/
|
||||
- (CTLineRef)createLineForString:(NSString *)aString;
|
||||
|
||||
|
||||
/*!
|
||||
@method createLineForString:emphasize:
|
||||
@abstract Renders the specified string.
|
||||
|
||||
@discussion This method uses either the normal or the special font depending
|
||||
on the <code>emphasize</code> flag. It uses the context inferred
|
||||
font size.
|
||||
|
||||
@param aString
|
||||
The string to be rendered.
|
||||
|
||||
@param emphasize
|
||||
A flag indicating whether the special font should be used.
|
||||
|
||||
@return A <code>CTLineRef</code> representing the rendered string.
|
||||
*/
|
||||
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize;
|
||||
|
||||
|
||||
/*!
|
||||
@method createLineForString:usingFont:
|
||||
@abstract Renders the specified string in the specified font.
|
||||
|
||||
@discussion In most cases you do should prefer <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/createLineForString:@/link</code>
|
||||
or <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/createLineForString:emphasize:@/link</code>
|
||||
over this method.
|
||||
|
||||
@param aString
|
||||
The string to be rendered.
|
||||
|
||||
@param font
|
||||
The font to render <code>aString</code> in.
|
||||
|
||||
@return A <code>CTLineRef</code> representing the rendered string.
|
||||
*/
|
||||
- (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font;
|
||||
|
||||
|
||||
/*!
|
||||
@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 <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/contextInferredFontSize@/link</code>.
|
||||
If this value is <code>YES</code> all of the receiver's children
|
||||
should also use the small size. If not the children may or may
|
||||
not (depending on the actual layout implementation) use the small
|
||||
size.
|
||||
*/
|
||||
@property (nonatomic) BOOL usesSmallSize;
|
||||
|
||||
|
||||
/*!
|
||||
@method bounds
|
||||
@abstract Returns the receiver's bounds.
|
||||
|
||||
@discussion If the receiver has cached bounds it will return the cached
|
||||
value. The cached value can be removed using the <code>@link
|
||||
//apple_ref/occ/instm/MPLayout/invalidate@/link</code> method. If
|
||||
there is no cached value this method will generate a new cached
|
||||
value by calling the <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
|
||||
method.
|
||||
|
||||
@return The receiver's bounds.
|
||||
*/
|
||||
- (NSRect)bounds;
|
||||
|
||||
|
||||
/*!
|
||||
@method boundingRectForRangePath:
|
||||
@abstract Returns the rect relative to the origin of the receiver's bounds
|
||||
that contains the symbols identified by the specified range path.
|
||||
|
||||
@discussion Use this method to determine where the user's selection should be
|
||||
drawn at.
|
||||
|
||||
@param rangePath
|
||||
The range path whose bounds are needed.
|
||||
|
||||
@return The rectangle containing the symbols identified by
|
||||
<code>rangePath</code>.
|
||||
*/
|
||||
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */
|
||||
|
||||
|
||||
/*!
|
||||
@method drawAtPoint:
|
||||
@abstract Draws the receiver at the specified point.
|
||||
|
||||
@discussion If the receiver returns <code>NO</code> from its <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||
method this method also draws every single child of the receiver.
|
||||
The location the children are drawn at is determined by the
|
||||
<code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
|
||||
method.
|
||||
|
||||
@param point
|
||||
The point the receiver is to be drawn at.
|
||||
*/
|
||||
- (void)drawAtPoint:(NSPoint)point;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category MPLayout (MPSubclassImplement)
|
||||
@abstract The methods in this category must be implemented by any concrete
|
||||
subclass of <code>MPLayout</code>.
|
||||
*/
|
||||
@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 <code>YES</code> from this method
|
||||
<code>MPLayout</code> does not draw the receiver's children. The
|
||||
receiver's <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
||||
method has to implement that functionality. If you return
|
||||
<code>NO</code> from this method the receiver must not draw its
|
||||
children in the <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
||||
method.
|
||||
|
||||
This method may be implemented by subclasses to opt in and change
|
||||
the default behaviour.
|
||||
|
||||
@return <code>YES</code> if the receiver draws its children
|
||||
automatically, <code>NO</code> otherwise. The default
|
||||
implementation returns <code>NO</code>.
|
||||
*/
|
||||
- (BOOL)drawsChildrenManually;
|
||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
|
||||
- (NSRect)generateBounds; // To be implemented
|
||||
- (NSRect)boundingRectForRange:(NSRange)range; // To be implemented, use rangePath instead, this one has wrong origin
|
||||
|
||||
|
||||
/*!
|
||||
@method childLayoutAtIndex:
|
||||
@abstract Returns the receiver's child at the specified index.
|
||||
|
||||
@discussion As long as the specified <code>index</code> does not exceed the
|
||||
receiver's number of children, this method should always return a
|
||||
value based on the following criteria:
|
||||
1. If the receiver represents a <code>@link
|
||||
//apple_ref/occ/cl/MPFunction@/link</code> instance it should
|
||||
always return a valid <code>MPLayout</code> instance.
|
||||
2. If the receiver represents a <code>@link
|
||||
//apple_ref/occ/cl/MPExpression@/link</code> instance it
|
||||
returns only a valid <code>MPLayout</code> instance if the
|
||||
represented expression's element at <code>index</code> is a
|
||||
function.
|
||||
|
||||
This method should cache any generated child layout and use the
|
||||
cache whenever possible in order to reduce resources needed for
|
||||
rendering.
|
||||
|
||||
This method must be implemented by subclasses.
|
||||
|
||||
@param index
|
||||
The index of the requested child layout.
|
||||
|
||||
@return A <code>MPLayout</code> instance representing the respective
|
||||
child of the object that is represented by the receiver, or
|
||||
<code>nil</code> if the child represents a string element in an
|
||||
expression.
|
||||
*/
|
||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method generateBounds
|
||||
@abstract Calculates the receiever's bounds.
|
||||
|
||||
@discussion This method should not use any cached values. Caching of the
|
||||
calculated bounds is done automatically by the
|
||||
<code>MPLayout</code> class. This method is only called if the
|
||||
receiving <code>MPLayout</code> instance has been invalidated and
|
||||
the receiver's bounds have been requested.
|
||||
|
||||
This method must be implemented by subclasses.
|
||||
|
||||
@return The receiver's bounds.
|
||||
*/
|
||||
- (NSRect)generateBounds;
|
||||
|
||||
|
||||
/*!
|
||||
@method boundingRectForRange:
|
||||
@abstract Returns the rectangle that encloses the symbols in the specified
|
||||
range.
|
||||
|
||||
@discussion This method is called to calculate the rectangle that encloses
|
||||
the user's selection. If the specified <code>range</code> has a
|
||||
length of <code>0</code> the returned rectangle should be the
|
||||
bounds of the caret (which has a width of <code>0</code>). The
|
||||
height and y-origin of the returned rectangle should be equal to
|
||||
the respective values of the receiver.
|
||||
|
||||
This method must be implemented by subclasses.
|
||||
|
||||
@param range
|
||||
The range of the selection whose bounds should be calculated.
|
||||
|
||||
@return A rectangle enclosing the user's selection.
|
||||
*/
|
||||
- (NSRect)boundingRectForRange:(NSRange)range;
|
||||
|
||||
|
||||
/*!
|
||||
@method offsetOfChildLayoutAtIndex:
|
||||
@abstract Returns the location of the child layout at the specified index.
|
||||
|
||||
@discussion This method must be implemented by subclasses even if the
|
||||
subclass returns <code>YES</code> from the <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||
method.
|
||||
|
||||
@param index
|
||||
The index of the child whose location is to be calculated.
|
||||
|
||||
@return The location of the child layout at the specified index relative
|
||||
to the receiver.
|
||||
*/
|
||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
|
||||
|
||||
|
||||
/*!
|
||||
@method indexPathForMousePoint:
|
||||
@abstract Performs hit testing.
|
||||
|
||||
@discussion This method tests the specified <code>point</code> against all of
|
||||
its children and returns the index path (relative to the index
|
||||
path of the receiver) that identifies the location inside the
|
||||
expression tree the user selected. The specified point must be
|
||||
inside of the receiver and must be specified relatively to the
|
||||
receiver. If the specified point is not inside any of the
|
||||
receiver's children but in the receiver itself the returned index
|
||||
path must contain one index specifiying the location in the
|
||||
receiver.
|
||||
|
||||
This method must be implemented by subclasses.
|
||||
|
||||
@param point
|
||||
The point inside the receiver that is to be tested.
|
||||
|
||||
@return The index path that points to the point the user selected. All
|
||||
indexes in the path except for the last one are specified in the
|
||||
element reference frame. The last index is specified in the
|
||||
symbol reference frame.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
|
||||
- (void)draw; // To be implemented
|
||||
|
||||
|
||||
/*!
|
||||
@method draw
|
||||
@abstract Draws the receiver.
|
||||
|
||||
@discussion If the receiver returns <code>YES</code> from its <code>@link
|
||||
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||
method the implementation of this method must also draw the
|
||||
receiver's children.
|
||||
|
||||
This method must be implemented by subclasses. If the receiver
|
||||
does not have anything to draw except for its children you should
|
||||
implement this method with an empty method body.
|
||||
*/
|
||||
- (void)draw;
|
||||
|
||||
@end
|
||||
@@ -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];
|
||||
|
||||
@@ -7,25 +7,49 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPMathRules</code> class.
|
||||
*/
|
||||
|
||||
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey;
|
||||
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey;
|
||||
|
||||
/*!
|
||||
@const MPMathRulesIsUsingDegreesKey
|
||||
@abstract Predefined key that is used to store the properties of the
|
||||
<code>MPMathRules</code> class in the <code>NSUserDefaults</code>
|
||||
database.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
|
||||
|
||||
|
||||
|
||||
@class MPMathRules;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPMathRules
|
||||
@abstract The math rules class stores global settings concerning
|
||||
mathematical interpretations
|
||||
|
||||
@discussion The <code>MPMathRules</code> class is a singletone class. There
|
||||
can only exist one instance (the shared instance) at any time.
|
||||
*/
|
||||
@interface MPMathRules : NSObject
|
||||
|
||||
/*!
|
||||
@method sharedRules
|
||||
@abstract Returnes the shared <code>MPMathRules</code> 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 <code>NO</code> radians are used instead. The default
|
||||
value is <code>NO</code>.
|
||||
*/
|
||||
@property (nonatomic) BOOL isUsingDegrees;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPNegatedTerm</code> 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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,14 +9,44 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPNumber</code> 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.
|
||||
<code>3.4</code>). 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 <code>number</code>.
|
||||
|
||||
@param number
|
||||
The number that the term should evaluate to. Must not be
|
||||
<code>nil</code>.
|
||||
|
||||
@return A new <code>MPNumberTerm</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */
|
||||
|
||||
|
||||
/*!
|
||||
@property number
|
||||
@abstract The receiver's number.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSDecimalNumber *number;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPParenthesisFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPParenthesisFunction, MPExpression;
|
||||
|
||||
|
||||
@@ -18,16 +18,19 @@
|
||||
|
||||
MPFunctionAccessorImplementation(Expression, _expression)
|
||||
|
||||
|
||||
- (NSArray *)childrenAccessors
|
||||
{
|
||||
return @[@"expression"];
|
||||
}
|
||||
|
||||
|
||||
- (Class)functionTermClass
|
||||
{
|
||||
return [MPParenthesisTerm class];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"(%@)", self.expression.description];
|
||||
|
||||
@@ -9,12 +9,32 @@
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPParenthesisFunctionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPParenthesisFunctionLayout, MPParenthesisFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPParenthesisFunctionLayout
|
||||
@abstract A parenthesis function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
|
||||
|
||||
@discussion The child of the parenthesis function is encapsulated by standard
|
||||
parenthesis that are scaled to the smae height as the child.
|
||||
*/
|
||||
@interface MPParenthesisFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@method parenthesisFunction
|
||||
@abstract Returns the <code>@link
|
||||
//apple_ref/occ/cl/MPParenthesisFunction@/link</code> represented
|
||||
by the receiver.
|
||||
*/
|
||||
- (MPParenthesisFunction *)parenthesisFunction;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,10 +9,24 @@
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPParenthesisTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPParenthesisTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPParenthesisTerm
|
||||
@abstract Represents a <code>@link
|
||||
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
|
||||
|
||||
@discussion A parenthesis function encapsulates a term and thus prioritizes
|
||||
it in evaluation.
|
||||
*/
|
||||
@interface MPParenthesisTerm : MPFunctionTerm
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPParenthesisTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
//
|
||||
|
||||
|
||||
|
||||
@class MPParsedExpression, MPTerm;
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPParsedExpression</code> class.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
@@ -50,6 +52,10 @@ FOUNDATION_EXPORT NSString *const MPPathToExpressionKey;
|
||||
FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
|
||||
|
||||
|
||||
|
||||
@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
|
||||
<code>@link
|
||||
//apple_ref/occ/intfp/MPParsedExpression/term@/link</code>.
|
||||
//apple_ref/occ/instp/MPParsedExpression/term@/link</code>.
|
||||
|
||||
@param error
|
||||
If an error occured during evaluation it will be returned
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPPowerFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPPowerFunction, MPExpression;
|
||||
|
||||
|
||||
@@ -10,15 +10,19 @@
|
||||
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPPowerFunction
|
||||
|
||||
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
|
||||
|
||||
|
||||
- (NSArray *)childrenAccessors
|
||||
{
|
||||
return @[@"exponentExpression"];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"^%@", self.exponentExpression.description];
|
||||
|
||||
@@ -9,14 +9,46 @@
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPPowerFunctionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPPowerFunctionLayout, MPPowerFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPPowerFunctionLayout
|
||||
@abstract A power function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
|
||||
|
||||
@discussion A power function layout draws its child at the top right of the
|
||||
base. Because of this the power function layout has a special
|
||||
property that gets set during the drawing of an expression:
|
||||
<code>@link
|
||||
//apple_ref/occ/instp/MPPowerFunctionLayout/baseBounds@/link</code>.
|
||||
*/
|
||||
@interface MPPowerFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@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 <code>@link
|
||||
//apple_ref/occ/cl/MPPowerFunction@/link</code> represented by
|
||||
the receiver.
|
||||
*/
|
||||
- (MPPowerFunction *)powerFunction;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,12 +9,33 @@
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPPowerTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPPowerTerm, MPTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPPowerTerm
|
||||
@abstract Represents a <code>@link
|
||||
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
|
||||
|
||||
@discussion A power function is evaluated using the C <code>pow</code>
|
||||
function.
|
||||
*/
|
||||
@interface MPPowerTerm : MPFunctionTerm
|
||||
|
||||
/*!
|
||||
@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
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
//
|
||||
|
||||
#import "MPPowerTerm.h"
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPPowerTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPProductTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPProductTerm;
|
||||
|
||||
@@ -22,7 +28,6 @@
|
||||
*/
|
||||
@interface MPProductTerm : MPTerm
|
||||
|
||||
|
||||
/*!
|
||||
@method initWithFactors:
|
||||
@abstract Initializes a new product term with the specified
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPRangePath</code> class.
|
||||
|
||||
The <code>MPRangePath</code> combines an <code>NSIndexPath</code> with a
|
||||
<code>NSRange</code>. A range path is used in a tree structure to identify a
|
||||
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
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
@implementation MPRangePath
|
||||
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPFunction.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPSumFunction</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPSumFunction, MPExpression;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,12 +9,33 @@
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPSumFunctionLayout</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPSumFunctionLayout, MPSumFunction;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPSumFunctionLayout
|
||||
@abstract A sum function layout displays a <code>@link
|
||||
//apple_ref/occ/cl/MPSumFunction@/link</code>.
|
||||
|
||||
@discussion A sum is drawn using a capital greeg sigma (∑) The three children
|
||||
are placed around it: The start expression below, target
|
||||
expression above and sum expression to the right of it.
|
||||
*/
|
||||
@interface MPSumFunctionLayout : MPFunctionLayout
|
||||
|
||||
/*!
|
||||
@method sumFunction
|
||||
@abstract Returns the <code>@link
|
||||
//apple_ref/occ/cl/MPSumFunction@/link</code> represented by the
|
||||
receiver.
|
||||
*/
|
||||
- (MPSumFunction *)sumFunction;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,11 +8,30 @@
|
||||
|
||||
#import "MPFunctionTerm.h"
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPSumFunctionTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPSumFunctionTerm;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPSumFunctionTerm
|
||||
@abstract Represents and evaluates a <code>@link
|
||||
//apple_ref/occ/cl/MPSumFunction@/link</code>.
|
||||
|
||||
@discussion A sum function evaluates its sum term <code>n</code> times. How
|
||||
often it actually is evaluated depends on the boundary
|
||||
expressions (start and target). Both are inclusive.
|
||||
|
||||
A sum function also features a variable that contains the current
|
||||
value of the iteration. The variable is defined in the start
|
||||
expression and incremented by <code>1</code> after every
|
||||
iteration.
|
||||
*/
|
||||
@interface MPSumFunctionTerm : MPFunctionTerm
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#import "MPParsedExpression.h"
|
||||
|
||||
|
||||
|
||||
@implementation MPSumFunctionTerm
|
||||
|
||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPSumTerm</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPSumTerm;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPTerm</code> class.
|
||||
|
||||
The <code>MPTerm</code> class is used to evaluate a mathematical expression. A
|
||||
term is a purely mathematical representation of a part of an expression. A term
|
||||
can be defined with three rules:
|
||||
@@ -66,6 +68,7 @@
|
||||
#define ReturnNilIfNil(test) if (test == nil) return nil
|
||||
|
||||
|
||||
|
||||
@class MPTerm;
|
||||
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPToken</code> class and protocol.
|
||||
|
||||
One way to represent a mathematical expression using the <code>@link
|
||||
MPExpression@/link</code> class is a sequence of tokens. A token is a logical
|
||||
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.
|
||||
|
||||
@@ -9,14 +9,44 @@
|
||||
#import "MPTerm.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPVariable</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPVariable;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPVariable
|
||||
@abstract This class represents a variable.
|
||||
|
||||
@discussion Variables are evaluated in the <code>@link
|
||||
//apple_ref/occ/cl/MPEvaluationContext@/link</code> and generate
|
||||
errors if they are not defined.
|
||||
*/
|
||||
@interface MPVariable : MPTerm
|
||||
|
||||
/*!
|
||||
@method initWithVariableName:
|
||||
@abstract Initializes a <code>MPVariable</code> with the specified
|
||||
<code>variableName</code>
|
||||
|
||||
@param variableName
|
||||
The name of the variable. Must not be <code>nil</code> and must
|
||||
be at least one character long.
|
||||
|
||||
@return A new <code>MPVariable</code> instance.
|
||||
*/
|
||||
- (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */
|
||||
|
||||
|
||||
/*!
|
||||
@property variableName
|
||||
@abstract The receiver's variable name.
|
||||
*/
|
||||
@property (readonly, nonatomic, strong) NSString *variableName;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,10 +7,20 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>MPWhiteView</code> class.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@class MPWhiteView;
|
||||
|
||||
|
||||
/*!
|
||||
@class MPWhiteView
|
||||
@abstract A <code>NSView</code> that draws a white square in its frame.
|
||||
*/
|
||||
@interface MPWhiteView : NSView
|
||||
|
||||
@end
|
||||
|
||||
@@ -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 "MPExpressionView.h"
|
||||
#import "MPExpressionStorage.h"
|
||||
#import "MPExpressionView.h"
|
||||
#import "MPLayout.h"
|
||||
#import "MPExpressionLayout.h"
|
||||
#import "MPFunctionLayout.h"
|
||||
|
||||
#import "MPFractionFunctionLayout.h"
|
||||
#import "MPParenthesisFunctionLayout.h"
|
||||
#import "MPPowerFunctionLayout.h"
|
||||
#import "MPSumFunctionLayout.h"
|
||||
@@ -7,6 +7,12 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>NSIndexPath(MPAdditions)</code> category.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category NSIndexPath (MPAdditions)
|
||||
@@ -15,7 +21,6 @@
|
||||
*/
|
||||
@interface NSIndexPath (MPAdditions)
|
||||
|
||||
|
||||
/*!
|
||||
@property firstIndex
|
||||
@abstract The first index from the receiver.
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
@implementation NSIndexPath (MPAdditions)
|
||||
|
||||
|
||||
- (NSUInteger)firstIndex
|
||||
{
|
||||
return [self indexAtPosition:0];
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
//
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>NSRegularExpression(MPParsingAdditions)</code>
|
||||
category.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category NSRegularExpression (MPParsingAdditions)
|
||||
@@ -15,7 +22,6 @@
|
||||
*/
|
||||
@interface NSRegularExpression (MPParsingAdditions)
|
||||
|
||||
|
||||
/*!
|
||||
@method firstMathInString:
|
||||
@abstract Returns the first match of the regular expression within the
|
||||
|
||||
@@ -9,11 +9,18 @@
|
||||
#import "MPExpression.h"
|
||||
|
||||
|
||||
/*!
|
||||
@header
|
||||
This file contains the <code>NSString(MPExpressionElement)</code> category.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@category NSString (MPExpressionElement)
|
||||
@abstract This category adds <code>@link MPExpressionElement@/link</code>
|
||||
protocol conformance to the <code>NSString</code> class.
|
||||
@abstract This category adds <code>@link
|
||||
//apple_ref/occ/intf/MPExpressionElement@/link</code> protocol
|
||||
conformance to the <code>NSString</code> class.
|
||||
*/
|
||||
@interface NSString (MPExpressionElement) <MPExpressionElement>
|
||||
|
||||
|
||||
@@ -253,8 +253,6 @@
|
||||
3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = "<group>"; };
|
||||
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = "<group>"; };
|
||||
3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = "<group>"; };
|
||||
3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = "<group>"; };
|
||||
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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 = "<group>";
|
||||
};
|
||||
3BC4661819B3692A0033F13A /* Temp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3BC4661519B365070033F13A /* MPArrayCache.h */,
|
||||
3BC4661619B365070033F13A /* MPArrayCache.m */,
|
||||
);
|
||||
name = Temp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
@@ -598,7 +587,6 @@
|
||||
3BF9977418DE623E009CF6C4 /* MathPad */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3BC4661819B3692A0033F13A /* Temp */,
|
||||
3BF9978018DE623E009CF6C4 /* MPDocument.h */,
|
||||
3BF9978118DE623E009CF6C4 /* MPDocument.m */,
|
||||
3B87E353190082E200259938 /* Resources */,
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// MPArrayCache.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 31.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// A wrapper around NSCache to support index-based caches. Indexes are
|
||||
// automatically adjusted to represent array-like behaviours.
|
||||
@interface MPArrayCache : NSObject {
|
||||
@private
|
||||
NSCache *_cache;
|
||||
}
|
||||
|
||||
- (id)init; /* designated initializer */
|
||||
|
||||
- (void)cacheObject:(id)object
|
||||
forIndex:(NSUInteger)index;
|
||||
- (id)cachedObjectForIndex:(NSUInteger)index;
|
||||
|
||||
- (void)clearCacheAtIndex:(NSUInteger)index
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
- (void)clearCacheInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
|
||||
- (void)replaceCachedObjectAtIndex:(NSUInteger)index
|
||||
withObjects:(NSArray *)objects;
|
||||
- (void)replaceCachedObjectsInRange:(NSRange)range
|
||||
withObjects:(NSArray *)objects;
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
@@ -8,11 +8,39 @@
|
||||
|
||||
#import <MathKit/MathKit.h>
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
@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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user