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"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPElementaryFunction</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPElementaryFunctionTerm;
|
@class MPElementaryFunctionTerm;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPElementaryFunctionTerm
|
||||||
|
@abstract A <code>MPElementaryFunction</code> implements various
|
||||||
|
mathematical functions that have a valid text representation.
|
||||||
|
*/
|
||||||
@interface MPElementaryFunctionTerm : MPTerm
|
@interface MPElementaryFunctionTerm : MPTerm
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithFunctionIdentifier:term:
|
||||||
|
@abstract Initializes a function term using the specified textual
|
||||||
|
representation of the function and a term as its value.
|
||||||
|
|
||||||
|
@param function
|
||||||
|
The textual representation of the function to use. Valid values
|
||||||
|
are <code>"sin"</code>, <code>"cos"</code>, <code>"tan"</code>,
|
||||||
|
<code>"asin"</code>, <code>"arcsin"</code>, <code>"acos"</code>,
|
||||||
|
<code>"arccos"</code>, <code>"atan"</code>,
|
||||||
|
<code>"arctan"</code>, <code>"log"</code>, <code>"lg"</code> and
|
||||||
|
<code>"ln"</code>.
|
||||||
|
|
||||||
|
@param term
|
||||||
|
The term that should be evaluated using <code>function</code>.
|
||||||
|
|
||||||
|
@return A new <code>MPElementaryFunctionTerm</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithFunctionIdentifier:(NSString *)function
|
- (instancetype)initWithFunctionIdentifier:(NSString *)function
|
||||||
term:(MPTerm *)term; /* designated initializer */
|
term:(MPTerm *)term; /* designated initializer */
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property functionIdentifier
|
||||||
|
@abstract The identifier of the receiver.
|
||||||
|
|
||||||
|
@discussion The identifier of a function is the string of letters it is
|
||||||
|
mathematically defined of (e.g. <code>"sin"</code> for the sine
|
||||||
|
function).
|
||||||
|
*/
|
||||||
|
@property (readonly, nonatomic, copy) NSString *functionIdentifier;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property term
|
||||||
|
@abstract The receiver's term.
|
||||||
|
|
||||||
|
@discussion Depending on the actual function the value of the
|
||||||
|
<code>term</code> may be converted from radians into degrees.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, strong) MPTerm *term;
|
@property (readonly, nonatomic, strong) MPTerm *term;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -9,13 +9,10 @@
|
|||||||
#import "MPElementaryFunctionTerm.h"
|
#import "MPElementaryFunctionTerm.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
#import "MPPowerFunction.h"
|
|
||||||
#import "MPProductTerm.h"
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPMathRules.h"
|
#import "MPMathRules.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPElementaryFunctionTerm {
|
@implementation MPElementaryFunctionTerm {
|
||||||
NSDecimalNumber *(^_function)(NSDecimalNumber *);
|
NSDecimalNumber *(^_function)(NSDecimalNumber *);
|
||||||
}
|
}
|
||||||
@@ -53,16 +50,18 @@
|
|||||||
} else if ([function isEqualToString:@"ln"]) {
|
} else if ([function isEqualToString:@"ln"]) {
|
||||||
func = &log;
|
func = &log;
|
||||||
} else {
|
} else {
|
||||||
NSAssert(true, @"function must be one of (sin, cos, tan, asin, acos, atan, lg, log, ln).");
|
NSAssert(true, @"function must be one of (sin, cos, tan, asin, arcsin, acos, arccos, atan, arctan, lg, log, ln).");
|
||||||
}
|
}
|
||||||
[self setFunction:func
|
[self setFunction:func
|
||||||
takesArcValue:takesArc
|
takesArcValue:takesArc
|
||||||
returnsArcValue:returnsArc];
|
returnsArcValue:returnsArc];
|
||||||
_term = term;
|
_term = term;
|
||||||
|
_functionIdentifier = function.copy;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setFunction:(double (*)(double))function
|
- (void)setFunction:(double (*)(double))function
|
||||||
takesArcValue:(BOOL)takesArc
|
takesArcValue:(BOOL)takesArc
|
||||||
returnsArcValue:(BOOL)returnsArc
|
returnsArcValue:(BOOL)returnsArc
|
||||||
@@ -80,6 +79,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees
|
- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees
|
||||||
{
|
{
|
||||||
if ([MPMathRules sharedRules].isUsingDegrees) {
|
if ([MPMathRules sharedRules].isUsingDegrees) {
|
||||||
@@ -90,6 +90,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants
|
- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants
|
||||||
{
|
{
|
||||||
if ([MPMathRules sharedRules].isUsingDegrees) {
|
if ([MPMathRules sharedRules].isUsingDegrees) {
|
||||||
@@ -100,13 +101,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSDecimalNumber *value = [self.term evaluate:error];
|
NSDecimalNumber *value = [self.term evaluate:error];
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return _function(value);
|
NSDecimalNumber *result = _function(value);
|
||||||
|
if ([result isEqualToNumber:[NSDecimalNumber notANumber]]) {
|
||||||
|
result = nil;
|
||||||
|
if (error) {
|
||||||
|
NSString *errorDescription = [NSString stringWithFormat:NSLocalizedString(@"%@ is not defined for the value %@.", nil), self.functionIdentifier, [value descriptionWithLocale:[NSLocale currentLocale]]];
|
||||||
|
*error = [NSError errorWithDomain:MPMathKitErrorDomain
|
||||||
|
code:102
|
||||||
|
userInfo:@{NSLocalizedDescriptionKey: errorDescription}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -7,21 +7,117 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPEvaluationContext</code> class.
|
||||||
|
|
||||||
|
The evaluation context is organized in so called levels. Every level of a
|
||||||
|
context contains a unique set of variables. Only the highest level is
|
||||||
|
accessible. Before any lower level can be accessed the higher ones have to be
|
||||||
|
discarded. A higher level can not define variables that are already defined in
|
||||||
|
a lower level. Similarly variables can only be undefined if they are defined at
|
||||||
|
the current level. Discarding (popping) a level always undefines all variables
|
||||||
|
defined on that level.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPEvaluationContext;
|
@class MPEvaluationContext;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPEvaluationContext
|
||||||
|
@abstract The evaluation context maintains a map of variables and
|
||||||
|
associates them with a certain level.
|
||||||
|
|
||||||
|
@discussion Different levels are nested using <code>@link
|
||||||
|
//apple_ref/occ/instm/MPEvaluationContext/push@/link</code> and
|
||||||
|
<code>@link
|
||||||
|
//apple_ref/occ/instm/MPEvaluationContext/pop@/link</code>
|
||||||
|
messages.
|
||||||
|
|
||||||
|
<code>MPEvaluationContext</code> is a singletone class. There can
|
||||||
|
only exist one instance (the shared instance) at any time.
|
||||||
|
*/
|
||||||
@interface MPEvaluationContext : NSObject
|
@interface MPEvaluationContext : NSObject
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method sharedContext
|
||||||
|
@abstract Returns the shared evaluation context.
|
||||||
|
*/
|
||||||
+ (MPEvaluationContext *)sharedContext;
|
+ (MPEvaluationContext *)sharedContext;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method push
|
||||||
|
@abstract Pushes a new level on context.
|
||||||
|
*/
|
||||||
- (void)push;
|
- (void)push;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method pop
|
||||||
|
@abstract Pops a level off the context.
|
||||||
|
|
||||||
|
@discussion Popping a level also undefines all variables that were defined in
|
||||||
|
that level.
|
||||||
|
*/
|
||||||
- (void)pop;
|
- (void)pop;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method defineVariable:value:
|
||||||
|
@abstract Defines a variable with the specified value at the current level.
|
||||||
|
|
||||||
|
@discussion The variable can only be defined if it has not been defined
|
||||||
|
previously in a lower level. If the variable has been defined at
|
||||||
|
the current level its value will be overwritten to the specified
|
||||||
|
one.
|
||||||
|
|
||||||
|
@param variable
|
||||||
|
The name of the variable. Typically this is a one-letter string.
|
||||||
|
|
||||||
|
@param value
|
||||||
|
The value of the variable.
|
||||||
|
|
||||||
|
@return <code>YES</code> if the variable was defined successfully,
|
||||||
|
<code>NO</code> if it is already defined in a lower level.
|
||||||
|
*/
|
||||||
- (BOOL)defineVariable:(NSString *)variable
|
- (BOOL)defineVariable:(NSString *)variable
|
||||||
value:(NSDecimalNumber *)value;
|
value:(NSDecimalNumber *)value;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method undefineVariable:
|
||||||
|
@abstract Undefines a variable that has previously been defined.
|
||||||
|
|
||||||
|
@discussion Variables can only be undefined if they have been defined at the
|
||||||
|
same level. If the variable to be undefined was defined at a
|
||||||
|
different level this method does nothing.
|
||||||
|
|
||||||
|
Normally there is very little reason to call this method since
|
||||||
|
all variables defined on one level are automatically undefined
|
||||||
|
when the level is popped.
|
||||||
|
|
||||||
|
@param variable
|
||||||
|
The variable to be undefined.
|
||||||
|
*/
|
||||||
- (void)undefineVariable:(NSString *)variable;
|
- (void)undefineVariable:(NSString *)variable;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method valueForVariable:
|
||||||
|
@abstract Returns the value for the specified variable.
|
||||||
|
|
||||||
|
@discussion If the <code>variable</code> has not been defined previously this
|
||||||
|
method returns <code>nil</code>.
|
||||||
|
|
||||||
|
@param variable
|
||||||
|
The variable whose value is to be retrieved.
|
||||||
|
|
||||||
|
@return The value <code>variable</code> was defined with or
|
||||||
|
<code>nil</code> if it was not defined.
|
||||||
|
*/
|
||||||
- (NSDecimalNumber *)valueForVariable:(NSString *)variable;
|
- (NSDecimalNumber *)valueForVariable:(NSString *)variable;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,13 +8,21 @@
|
|||||||
|
|
||||||
#import "MPEvaluationContext.h"
|
#import "MPEvaluationContext.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPEvaluationContext ()
|
@interface MPEvaluationContext ()
|
||||||
|
|
||||||
@property (nonatomic, strong) NSMutableArray *stack;
|
@property (nonatomic, strong) NSMutableArray *stack;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPEvaluationContext
|
@implementation MPEvaluationContext
|
||||||
|
|
||||||
static MPEvaluationContext *sharedContext;
|
static MPEvaluationContext *sharedContext;
|
||||||
|
|
||||||
|
|
||||||
+ (MPEvaluationContext *)sharedContext
|
+ (MPEvaluationContext *)sharedContext
|
||||||
{
|
{
|
||||||
if (!sharedContext) {
|
if (!sharedContext) {
|
||||||
@@ -23,6 +31,7 @@ static MPEvaluationContext *sharedContext;
|
|||||||
return sharedContext;
|
return sharedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if (sharedContext) {
|
if (sharedContext) {
|
||||||
@@ -40,16 +49,19 @@ static MPEvaluationContext *sharedContext;
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)push
|
- (void)push
|
||||||
{
|
{
|
||||||
[self.stack addObject:[[NSMutableDictionary alloc] init]];
|
[self.stack addObject:[[NSMutableDictionary alloc] init]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)pop
|
- (void)pop
|
||||||
{
|
{
|
||||||
[self.stack removeLastObject];
|
[self.stack removeLastObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)defineVariable:(NSString *)variable
|
- (BOOL)defineVariable:(NSString *)variable
|
||||||
value:(NSDecimalNumber *)value
|
value:(NSDecimalNumber *)value
|
||||||
{
|
{
|
||||||
@@ -62,17 +74,20 @@ static MPEvaluationContext *sharedContext;
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)undefineVariable:(NSString *)variable
|
- (void)undefineVariable:(NSString *)variable
|
||||||
{
|
{
|
||||||
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
NSMutableDictionary *currentBindings = self.stack.lastObject;
|
||||||
[currentBindings removeObjectForKey:variable];
|
[currentBindings removeObjectForKey:variable];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)isVariableDefined:(NSString *)variable
|
- (BOOL)isVariableDefined:(NSString *)variable
|
||||||
{
|
{
|
||||||
return [self valueForVariable:variable] != nil;
|
return [self valueForVariable:variable] != nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)valueForVariable:(NSString *)variable
|
- (NSDecimalNumber *)valueForVariable:(NSString *)variable
|
||||||
{
|
{
|
||||||
NSUInteger currentIndex = self.stack.count;
|
NSUInteger currentIndex = self.stack.count;
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPExpression</code> class and the
|
||||||
|
<code>MPExpressionElement</code> protocol.
|
||||||
|
|
||||||
The <code>MPExpression</code> class is used to represent a mathematical
|
The <code>MPExpression</code> class is used to represent a mathematical
|
||||||
expression. It is used in the storage layer of the MathKit Expression System. An
|
expression. It is used in the storage layer of the MathKit Expression System. An
|
||||||
instance of the <code>MPExpression</code> class stores all contents related to
|
instance of the <code>MPExpression</code> class stores all contents related to
|
||||||
@@ -108,6 +111,58 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@const MPIllegalElementException
|
||||||
|
@abstract Name for an exception that is raised if an invalid element is
|
||||||
|
added to an expression.
|
||||||
|
|
||||||
|
@discussion This exception may be raised during initialization of an
|
||||||
|
expression or when the expression is mutated. This exception
|
||||||
|
contains the invalid element in its <code>userInfo</code>
|
||||||
|
dictionary. You can query it using the
|
||||||
|
<code>MPIllegalElementExceptionElementKey</code> key.
|
||||||
|
*/
|
||||||
|
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@const MPIllegalElementExceptionElementKey
|
||||||
|
@abstract Predefined key for an invalid element that caused a
|
||||||
|
<code>MPIllegalElementException</code> to be raised.
|
||||||
|
|
||||||
|
@discussion The invalid element can be of any type. Numbers and structs are
|
||||||
|
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
|
||||||
|
instance respectively.
|
||||||
|
*/
|
||||||
|
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@typedef MPReferenceFrame
|
||||||
|
@abstract A reference frame specifies the way an <i>item</i> is to be
|
||||||
|
interpreted.
|
||||||
|
|
||||||
|
@constant MPElementReferenceFrame
|
||||||
|
Specifies that items should be interpreted as elements.
|
||||||
|
|
||||||
|
@constant MPSymbolReferenceFrame
|
||||||
|
Specifies that items should be interpreted as symbols.
|
||||||
|
|
||||||
|
@constant MPTokenReferenceFrame
|
||||||
|
Specifies that items should be interpreted as tokens.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MPElementReferenceFrame,
|
||||||
|
MPSymbolReferenceFrame,
|
||||||
|
MPTokenReferenceFrame
|
||||||
|
} MPReferenceFrame;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
|
||||||
|
@protocol MPExpressionElement, MPToken;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@protocol MPExpressionElement
|
@protocol MPExpressionElement
|
||||||
@abstract This protocol defines the functionality an element in an
|
@abstract This protocol defines the functionality an element in an
|
||||||
@@ -161,58 +216,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@const MPIllegalElementException
|
|
||||||
@abstract Name for an exception that is raised if an invalid element is
|
|
||||||
added to an expression.
|
|
||||||
|
|
||||||
@discussion This exception may be raised during initialization of an
|
|
||||||
expression or when the expression is mutated. This exception
|
|
||||||
contains the invalid element in its <code>userInfo</code>
|
|
||||||
dictionary. You can query it using the
|
|
||||||
<code>MPIllegalElementExceptionElementKey</code> key.
|
|
||||||
*/
|
|
||||||
FOUNDATION_EXPORT NSString *const MPIllegalElementException;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@const MPIllegalElementExceptionElementKey
|
|
||||||
@abstract Predefined key for an invalid element that caused a
|
|
||||||
<code>MPIllegalElementException</code> to be raised.
|
|
||||||
|
|
||||||
@discussion The invalid element can be of any type. Numbers and structs are
|
|
||||||
wrapped in an <code>NSNumber</code> or <code>NSValue</code>
|
|
||||||
instance respectively.
|
|
||||||
*/
|
|
||||||
FOUNDATION_EXPORT NSString *const MPIllegalElementExceptionElementKey;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@typedef MPReferenceFrame
|
|
||||||
@abstract A reference frame specifies the way an <i>item</i> is to be
|
|
||||||
interpreted.
|
|
||||||
|
|
||||||
@constant MPElementReferenceFrame
|
|
||||||
Specifies that items should be interpreted as elements.
|
|
||||||
|
|
||||||
@constant MPSymbolReferenceFrame
|
|
||||||
Specifies that items should be interpreted as symbols.
|
|
||||||
|
|
||||||
@constant MPTokenReferenceFrame
|
|
||||||
Specifies that items should be interpreted as tokens.
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
MPElementReferenceFrame,
|
|
||||||
MPSymbolReferenceFrame,
|
|
||||||
MPTokenReferenceFrame
|
|
||||||
} MPReferenceFrame;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPExpression, MPFunction, MPRangePath, MPParsedExpression;
|
|
||||||
@protocol MPExpressionElement, MPToken;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@class MPExpression
|
@class MPExpression
|
||||||
@abstract A <code>MPExpression</code> instance represents a mathematical
|
@abstract A <code>MPExpression</code> instance represents a mathematical
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NSString *const MPIllegalElementException = @"Illegal Element Exception";
|
NSString *const MPIllegalElementException = @"Illegal Element Exception";
|
||||||
NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey";
|
NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptionElementKey";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpression () {
|
@interface MPExpression () {
|
||||||
NSMutableArray * _elements;
|
NSMutableArray * _elements;
|
||||||
}
|
}
|
||||||
@@ -299,7 +299,6 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#warning If multiple equal expressions exist errors may occur...
|
|
||||||
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
|
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
|
||||||
{
|
{
|
||||||
return [_elements indexOfObject:element];
|
return [_elements indexOfObject:element];
|
||||||
@@ -656,7 +655,7 @@ NSString *const MPIllegalElementExceptionElementKey = @"MPIllegalElementExceptio
|
|||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
#warning Bad Implementation
|
#warning Incomplete Implementation
|
||||||
NSMutableString *description = [[NSMutableString alloc] init];
|
NSMutableString *description = [[NSMutableString alloc] init];
|
||||||
NSUInteger index = 0;
|
NSUInteger index = 0;
|
||||||
for (id element in _elements) {
|
for (id element in _elements) {
|
||||||
|
|||||||
@@ -9,14 +9,53 @@
|
|||||||
#import "MPLayout.h"
|
#import "MPLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPExpressionLayout</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPExpressionLayout, MPExpression, MPFunctionLayout;
|
@class MPExpressionLayout, MPExpression, MPFunctionLayout;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPExpressionLayout
|
||||||
|
@abstract An expression layout draws an <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpression@/link</code>.
|
||||||
|
|
||||||
|
@discussion For more information on the layout system see the documentation
|
||||||
|
on the <code>@link //apple_ref/occ/cl/MPLayout@/link</code>
|
||||||
|
class.
|
||||||
|
*/
|
||||||
@interface MPExpressionLayout : MPLayout
|
@interface MPExpressionLayout : MPLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithExpression:parent:
|
||||||
|
@abstract Initializes an expression layout with the specified expression
|
||||||
|
and parent layout.
|
||||||
|
|
||||||
|
@discussion To initialize a layout for the root expression of an expression
|
||||||
|
tree specify <code>nil</code> as the <code>parent</code>.
|
||||||
|
|
||||||
|
@param expression
|
||||||
|
The expression that sould be represented by the receiver. Must
|
||||||
|
not be <code>ni</code>.
|
||||||
|
|
||||||
|
@param parent
|
||||||
|
The parent layout of the receiver. Specify <code>nil</code> to
|
||||||
|
initalize a root expression layout.
|
||||||
|
|
||||||
|
@return A newly initialized expression layout.
|
||||||
|
*/
|
||||||
- (instancetype)initWithExpression:(MPExpression *)expression
|
- (instancetype)initWithExpression:(MPExpression *)expression
|
||||||
parent:(MPFunctionLayout *)parent;
|
parent:(MPFunctionLayout *)parent;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property expression
|
||||||
|
@abstract The expression represented by the receiver.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, weak) MPExpression *expression;
|
@property (readonly, nonatomic, weak) MPExpression *expression;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,21 +9,23 @@
|
|||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
|
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
#import "MPExpression.h"
|
#import "MPFunction.h"
|
||||||
#import "MPPowerFunction.h"
|
|
||||||
|
|
||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
#import "MPPowerFunctionLayout.h"
|
#import "MPPowerFunctionLayout.h"
|
||||||
|
|
||||||
#import "MPToken.h"
|
#import "MPToken.h"
|
||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionLayout (MPLineGeneration)
|
@interface MPExpressionLayout (MPLineGeneration)
|
||||||
|
|
||||||
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionLayout (MPLineGeneration)
|
@implementation MPExpressionLayout (MPLineGeneration)
|
||||||
|
|
||||||
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index
|
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index
|
||||||
@@ -60,9 +62,13 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionLayout
|
@implementation MPExpressionLayout
|
||||||
|
|
||||||
# pragma mark Creation Methods
|
# pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
- (instancetype)initWithExpression:(MPExpression *)expression
|
- (instancetype)initWithExpression:(MPExpression *)expression
|
||||||
parent:(MPFunctionLayout *)parent
|
parent:(MPFunctionLayout *)parent
|
||||||
{
|
{
|
||||||
@@ -73,12 +79,16 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)numberOfChildren
|
- (NSUInteger)numberOfChildren
|
||||||
{
|
{
|
||||||
return self.expression.countElements;
|
return self.expression.countElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
||||||
@@ -95,6 +105,7 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)boundsOfElementAtIndex:(NSUInteger)index
|
- (NSRect)boundsOfElementAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
id symbol = [self.expression elementAtIndex:index];
|
id symbol = [self.expression elementAtIndex:index];
|
||||||
@@ -109,7 +120,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Drawing Methods
|
#pragma mark Drawing Methods
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)generateBounds
|
- (NSRect)generateBounds
|
||||||
{
|
{
|
||||||
if (self.expression.countElements == 0) {
|
if (self.expression.countElements == 0) {
|
||||||
@@ -125,6 +139,7 @@
|
|||||||
return NSMakeRect(x, y, width, height);
|
return NSMakeRect(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)boundingRectForRange:(NSRange)range
|
- (NSRect)boundingRectForRange:(NSRange)range
|
||||||
{
|
{
|
||||||
NSUInteger startOffset;
|
NSUInteger startOffset;
|
||||||
@@ -184,6 +199,7 @@
|
|||||||
return NSMakeRect(x, self.bounds.origin.y, width, self.bounds.size.height);
|
return NSMakeRect(x, self.bounds.origin.y, width, self.bounds.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
CGFloat x = 0;
|
CGFloat x = 0;
|
||||||
@@ -193,6 +209,7 @@
|
|||||||
return NSMakePoint(x, 0);
|
return NSMakePoint(x, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
NSUInteger currentPosition = 0;
|
NSUInteger currentPosition = 0;
|
||||||
@@ -235,11 +252,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)drawsChildrenManually
|
- (BOOL)drawsChildrenManually
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)draw
|
- (void)draw
|
||||||
{
|
{
|
||||||
// Get the current context
|
// Get the current context
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPExpressionParser</code> class.
|
||||||
|
|
||||||
The <code>MPExpressionParser</code> converts a <code>@link
|
The <code>MPExpressionParser</code> converts a <code>@link
|
||||||
MPExpression@/link</code> instance into a <code>@link
|
MPExpression@/link</code> instance into a <code>@link
|
||||||
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance. The rules that are
|
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance. The rules that are
|
||||||
|
|||||||
@@ -8,25 +8,27 @@
|
|||||||
|
|
||||||
#import "MPExpressionParser.h"
|
#import "MPExpressionParser.h"
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPTerm.h"
|
|
||||||
#import "MPToken.h"
|
#import "MPToken.h"
|
||||||
|
#import "MPExpression.h"
|
||||||
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
|
#import "MPTerm.h"
|
||||||
#import "MPSumTerm.h"
|
#import "MPSumTerm.h"
|
||||||
#import "MPProductTerm.h"
|
#import "MPProductTerm.h"
|
||||||
#import "MPFactorialTerm.h"
|
|
||||||
#import "MPElementaryFunctionTerm.h"
|
|
||||||
#import "MPFunctionTerm.h"
|
|
||||||
#import "MPPowerTerm.h"
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
#import "MPNegatedTerm.h"
|
#import "MPNegatedTerm.h"
|
||||||
|
#import "MPFunctionTerm.h"
|
||||||
|
#import "MPElementaryFunctionTerm.h"
|
||||||
#import "MPNumber.h"
|
#import "MPNumber.h"
|
||||||
#import "MPVariable.h"
|
#import "MPVariable.h"
|
||||||
#import "MPFactorialTerm.h"
|
#import "MPFactorialTerm.h"
|
||||||
|
#import "MPPowerTerm.h"
|
||||||
|
|
||||||
|
|
||||||
#define success() state = 0
|
#define success() state = 0
|
||||||
#define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
|
#define fail() self.errorTokenIndex = self.currentTokenIndex; state = -1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionParser ()
|
@interface MPExpressionParser ()
|
||||||
|
|
||||||
@property (nonatomic, strong) NSArray *tokens;
|
@property (nonatomic, strong) NSArray *tokens;
|
||||||
@@ -46,6 +48,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionParser
|
@implementation MPExpressionParser
|
||||||
|
|
||||||
- (instancetype)initWithExpression:(MPExpression *)expression
|
- (instancetype)initWithExpression:(MPExpression *)expression
|
||||||
@@ -58,12 +62,14 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors
|
- (MPParsedExpression *)parse:(NSArray *__autoreleasing *)errors
|
||||||
{
|
{
|
||||||
return [self parseExpectingVariableDefinition:NO
|
return [self parseExpectingVariableDefinition:NO
|
||||||
errors:errors];
|
errors:errors];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag
|
- (MPParsedExpression *)parseExpectingVariableDefinition:(BOOL)flag
|
||||||
errors:(NSArray *__autoreleasing *)errors
|
errors:(NSArray *__autoreleasing *)errors
|
||||||
{
|
{
|
||||||
@@ -120,6 +126,7 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)runParserMachine
|
- (void)runParserMachine
|
||||||
{
|
{
|
||||||
NSInteger state = 1;
|
NSInteger state = 1;
|
||||||
@@ -251,6 +258,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)skipWhitespaces
|
- (void)skipWhitespaces
|
||||||
{
|
{
|
||||||
while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) {
|
while ([self currentToken] != nil && [self currentToken].tokenType == MPWhitespaceToken) {
|
||||||
@@ -258,6 +266,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (id<MPToken>)currentToken
|
- (id<MPToken>)currentToken
|
||||||
{
|
{
|
||||||
if (self.currentTokenIndex >= self.tokens.count) {
|
if (self.currentTokenIndex >= self.tokens.count) {
|
||||||
@@ -266,11 +275,13 @@
|
|||||||
return self.tokens[self.currentTokenIndex];
|
return self.tokens[self.currentTokenIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)nextToken
|
- (void)nextToken
|
||||||
{
|
{
|
||||||
self.currentTokenIndex++;
|
self.currentTokenIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMutableArray *)errors
|
- (NSMutableArray *)errors
|
||||||
{
|
{
|
||||||
if (!_errors) {
|
if (!_errors) {
|
||||||
@@ -279,6 +290,7 @@
|
|||||||
return _errors;
|
return _errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMutableArray *)summands
|
- (NSMutableArray *)summands
|
||||||
{
|
{
|
||||||
if (!_summands) {
|
if (!_summands) {
|
||||||
@@ -287,6 +299,7 @@
|
|||||||
return _summands;
|
return _summands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMutableArray *)factors
|
- (NSMutableArray *)factors
|
||||||
{
|
{
|
||||||
if (!_factors) {
|
if (!_factors) {
|
||||||
@@ -295,6 +308,7 @@
|
|||||||
return _factors;
|
return _factors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMutableArray *)functionStack
|
- (NSMutableArray *)functionStack
|
||||||
{
|
{
|
||||||
if (!_functionStack) {
|
if (!_functionStack) {
|
||||||
@@ -303,6 +317,7 @@
|
|||||||
return _functionStack;
|
return _functionStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMutableArray *)valueGroup
|
- (NSMutableArray *)valueGroup
|
||||||
{
|
{
|
||||||
if (!_valueGroup) {
|
if (!_valueGroup) {
|
||||||
@@ -311,6 +326,7 @@
|
|||||||
return _valueGroup;
|
return _valueGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)parseOperatorList:(id<MPToken>)token // Returns YES if list is overall negative
|
- (BOOL)parseOperatorList:(id<MPToken>)token // Returns YES if list is overall negative
|
||||||
{
|
{
|
||||||
NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]
|
NSString *operatorString = [[token.stringValue stringByReplacingOccurrencesOfString:@" " withString:@""]
|
||||||
@@ -318,17 +334,20 @@
|
|||||||
return (operatorString.length & 1) == 1;
|
return (operatorString.length & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)parseNumber:(id<MPToken>)token
|
- (NSDecimalNumber *)parseNumber:(id<MPToken>)token
|
||||||
{
|
{
|
||||||
return [NSDecimalNumber decimalNumberWithString:token.stringValue
|
return [NSDecimalNumber decimalNumberWithString:token.stringValue
|
||||||
locale:[NSLocale currentLocale]];
|
locale:[NSLocale currentLocale]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSString *)parseVariable:(id<MPToken>)token
|
- (NSString *)parseVariable:(id<MPToken>)token
|
||||||
{
|
{
|
||||||
return token.stringValue;
|
return token.stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)collapseSummand
|
- (void)collapseSummand
|
||||||
{
|
{
|
||||||
if (self.factors.count > 0) {
|
if (self.factors.count > 0) {
|
||||||
@@ -342,6 +361,7 @@
|
|||||||
self.summandNegative = NO;
|
self.summandNegative = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)collapseFactor
|
- (void)collapseFactor
|
||||||
{
|
{
|
||||||
if (self.valueGroup.count > 0) {
|
if (self.valueGroup.count > 0) {
|
||||||
@@ -360,6 +380,7 @@
|
|||||||
self.factorNegative = NO;
|
self.factorNegative = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)runSuffixMachine
|
- (void)runSuffixMachine
|
||||||
{
|
{
|
||||||
BOOL checkMore = YES;
|
BOOL checkMore = YES;
|
||||||
@@ -388,6 +409,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)addErrorWithCode:(NSInteger)code
|
- (void)addErrorWithCode:(NSInteger)code
|
||||||
localizedDescription:(NSString *)description
|
localizedDescription:(NSString *)description
|
||||||
useRange:(BOOL)flag
|
useRange:(BOOL)flag
|
||||||
@@ -408,6 +430,7 @@
|
|||||||
MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
|
MPErrorRangeKey: [NSValue valueWithRange:errorRange]}]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)errorOccured
|
- (BOOL)errorOccured
|
||||||
{
|
{
|
||||||
[self skipWhitespaces];
|
[self skipWhitespaces];
|
||||||
|
|||||||
@@ -9,13 +9,51 @@
|
|||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPExpressionStorage</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPExpressionStorage, MPExpressionView, MPExpressionLayout;
|
@class MPExpressionStorage, MPExpressionView, MPExpressionLayout;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPExpressionStorage
|
||||||
|
@abstract An expression storage manages the contents of an expression view
|
||||||
|
and notifies it when the underlying expression changes.
|
||||||
|
*/
|
||||||
@interface MPExpressionStorage : MPExpression
|
@interface MPExpressionStorage : MPExpression
|
||||||
|
|
||||||
@property (nonatomic, weak) MPExpressionView *expressionView; // Do not set
|
/*!
|
||||||
|
@property expressionView
|
||||||
|
@abstract The receiver's expression view.
|
||||||
|
|
||||||
|
@discussion The expression view is the view that displays the contents of the
|
||||||
|
expression storage. Normally you should not call the setter of
|
||||||
|
this property. It is automatically assigned when the <code>@link
|
||||||
|
//apple_ref/occ/instp/MPExpressionView/expressionStorage@/link</code>
|
||||||
|
property of the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpressionView@/link</code> class is set.
|
||||||
|
|
||||||
|
When this property is set the receiver assumes that the
|
||||||
|
displaying expression view has changed. It then adapts to the
|
||||||
|
properties of the new expression view and discards the root
|
||||||
|
layout.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, weak) MPExpressionView *expressionView;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property rootLayout
|
||||||
|
@abstract The receiver's root layout.
|
||||||
|
|
||||||
|
@discussion The root layout is the root node of the layout tree that draws
|
||||||
|
the receiver's contents. For more information see the
|
||||||
|
documentation on the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPLayout@/link</code> class.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,17 +9,22 @@
|
|||||||
#import "MPExpressionStorage.h"
|
#import "MPExpressionStorage.h"
|
||||||
|
|
||||||
#import "MPExpressionView.h"
|
#import "MPExpressionView.h"
|
||||||
#import "MPLayout.h"
|
|
||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
|
|
||||||
#import "MPRangePath.h"
|
#import "MPRangePath.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionStorage ()
|
@interface MPExpressionStorage ()
|
||||||
|
|
||||||
@property (nonatomic, strong) NSLayoutManager *layoutManager;
|
@property (nonatomic, strong) NSLayoutManager *layoutManager;
|
||||||
@property (nonatomic, strong) NSTextContainer *textContainer;
|
@property (nonatomic, strong) NSTextContainer *textContainer;
|
||||||
@property (nonatomic, strong) NSTextStorage *textStorage;
|
@property (nonatomic, strong) NSTextStorage *textStorage;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionStorage
|
@implementation MPExpressionStorage
|
||||||
|
|
||||||
- (instancetype)initWithElements:(NSArray *)elements
|
- (instancetype)initWithElements:(NSArray *)elements
|
||||||
@@ -31,6 +36,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setExpressionView:(MPExpressionView *)expressionView
|
- (void)setExpressionView:(MPExpressionView *)expressionView
|
||||||
{
|
{
|
||||||
_expressionView = expressionView;
|
_expressionView = expressionView;
|
||||||
@@ -38,6 +44,7 @@
|
|||||||
self.rootLayout.flipped = expressionView.flipped;
|
self.rootLayout.flipped = expressionView.flipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)changedElementsInRangePath:(MPRangePath *)rangePath
|
- (void)changedElementsInRangePath:(MPRangePath *)rangePath
|
||||||
replacementLength:(NSUInteger)replacementLength
|
replacementLength:(NSUInteger)replacementLength
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPExpressionTokenizer</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPExpressionTokenizer, MPExpression;
|
@class MPExpressionTokenizer, MPExpression;
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
@"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times
|
@"((?:\\d+%@(?!\\d+))|(?:(?:\\d*%@){2,}\\d*)|%@(?!\\d+))|" // Substitute with decimal separator 3 times
|
||||||
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times
|
@"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" // Substitute with decimal separator 2 times
|
||||||
@"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|"
|
@"(sin|cos|tan|asin|arcsin|acos|arccos|atan|arctan|lg|log|ln)|"
|
||||||
@"([A-Za-z])|"
|
@"([A-Za-zπ])|"
|
||||||
@"(!)|"
|
@"(!)|"
|
||||||
@"(=)|"
|
@"(=)|"
|
||||||
@"(\\s+)"
|
@"(\\s+)"
|
||||||
|
|||||||
@@ -6,68 +6,195 @@
|
|||||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
// TODO: Undo/Redo + Delegate (evaluateExpressionView:evaluate...)
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPExpressionView</code> class.
|
||||||
|
|
||||||
|
<h2>The MathKit Expression System</h2>
|
||||||
|
MathKit contains class es that make up the so called <i>expression system</i>.
|
||||||
|
The expression system is divided into three layers: the model, the view and the
|
||||||
|
controller layer. The <code>MPExpressionView</code> class represents the view
|
||||||
|
layer and interacts with the Cocoa <code>NSView</code> system. The model is
|
||||||
|
represented by the classes <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpression@/link</code> and <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code>. The controller layer between the two
|
||||||
|
consists of the class <code>@link //apple_ref/occ/cl/MPLayout@/link</code> and
|
||||||
|
its subclasses. The purpose of the expression system is presenting expressions
|
||||||
|
to the user and offering possibilities to manipulate and work with expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPExpressionView, MPExpressionStorage, MPFunction, MPRangePath;
|
@class MPExpressionView, MPExpressionStorage, MPFunction, MPRangePath;
|
||||||
//@protocol MPExpressionViewDelegate;
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPExpressionView
|
||||||
|
@abstract The <code>MPExpressionView</code> class is the front-end class of
|
||||||
|
the MathKit expression system.
|
||||||
|
|
||||||
|
@discussion The class draws the expression managed by the the back-end class
|
||||||
|
<code>@link //apple_ref/occ/cl/MPExpressionStorage@/link</code>
|
||||||
|
and is the interface between Application Kit's view system and
|
||||||
|
the MathKit expression system.
|
||||||
|
*/
|
||||||
@interface MPExpressionView : NSView <NSUserInterfaceValidations>
|
@interface MPExpressionView : NSView <NSUserInterfaceValidations>
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
/*!
|
||||||
|
@methodgroup Creation Methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithFrame:
|
||||||
|
@abstract Initializes a <code>MPExpressionView</code> instance.
|
||||||
|
|
||||||
|
@discussion This method sets up all layers of the expression system including
|
||||||
|
an empty expression storage.
|
||||||
|
|
||||||
|
This method is the designated initializer of the
|
||||||
|
<code>MPExpressionView</code> class.
|
||||||
|
|
||||||
|
@param frameRect
|
||||||
|
The frame rectangle for the created view.
|
||||||
|
|
||||||
|
@return An initialized <code>MPExpressionView</code> or <code>nil</code>
|
||||||
|
if the object could not be created.
|
||||||
|
*/
|
||||||
- (id)initWithFrame:(NSRect)frameRect;
|
- (id)initWithFrame:(NSRect)frameRect;
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Properties
|
#pragma mark Properties
|
||||||
|
/*!
|
||||||
|
@methodgroup Properties
|
||||||
|
*/
|
||||||
|
|
||||||
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
|
|
||||||
|
|
||||||
//@property (nonatomic, weak) id<MPExpressionViewDelegate> delegate;
|
/*!
|
||||||
|
@property expressionStorage
|
||||||
|
@abstract The receiver's expression storage.
|
||||||
|
|
||||||
|
@discussion The expression storage maintains the contents of the receiver.
|
||||||
|
User interactions change the underlying data. If the expression
|
||||||
|
storage or the underlying expression change the expression
|
||||||
|
storage updates the receiver.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, strong) MPExpressionStorage *expressionStorage;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property selection
|
||||||
|
@abstract The receiver's selection.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) MPRangePath *selection;
|
@property (nonatomic, strong) MPRangePath *selection;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property mathError
|
||||||
|
@abstract If set the receiver will display the localized description of the
|
||||||
|
specified error.
|
||||||
|
|
||||||
|
@discussion There can only be one math error at a time. This property should
|
||||||
|
not be set simutaneously with the <code>@link
|
||||||
|
//apple_ref/occ/instp/MPExpressionView/syntaxErrors@/link</code>.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) NSError *mathError;
|
@property (nonatomic, strong) NSError *mathError;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property syntaxErrors
|
||||||
|
@abstract If set the receiver will display a dropdown list of syntax
|
||||||
|
errors.
|
||||||
|
|
||||||
|
@discussion The array must only contain <code>NSError</code> instances. In
|
||||||
|
addition to that every instance must contain a valid
|
||||||
|
<code>NSIndexPath</code> for the <code>@link
|
||||||
|
//apple_ref/c/data/MPPathToExpressionKey@/link</code> in its
|
||||||
|
<code>userDict</code> acompanied by a <code>NSRange</code>
|
||||||
|
wrapped in a <code>NSValue</code> instance for the <code>@link
|
||||||
|
//apple_ref/c/data/MPErrorRangeKey@/link</code>.
|
||||||
|
|
||||||
|
If the user selects an item from the dropdown list the part of
|
||||||
|
the expression displayed by the receiver will be selected that is
|
||||||
|
specified by the values in the corresponding <code>NSError</code>
|
||||||
|
instance.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) NSArray *syntaxErrors;
|
@property (nonatomic, strong) NSArray *syntaxErrors;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property target
|
||||||
|
@abstract The target object to receive action messages from the receiver.
|
||||||
|
*/
|
||||||
@property (nonatomic, weak) id target;
|
@property (nonatomic, weak) id target;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property action
|
||||||
|
@abstract The receiver's action method to the specified selector.
|
||||||
|
|
||||||
|
@discussion The action method is invoked when the user presses <i>enter</i>
|
||||||
|
in the receiver. Typically this is understood as evaluation
|
||||||
|
request.
|
||||||
|
*/
|
||||||
@property (nonatomic) SEL action;
|
@property (nonatomic) SEL action;
|
||||||
|
|
||||||
//@property (nonatomic) BOOL editable;
|
|
||||||
//@property (nonatomic) BOOL selectable;
|
|
||||||
|
|
||||||
#pragma mark Actions
|
#pragma mark Actions
|
||||||
// Radians - Degrees
|
/*!
|
||||||
|
@methodgroup Actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method switchToRadians:
|
||||||
|
@abstract Tells the receiver that it should switch the interpretation of
|
||||||
|
trigonometric function values to radians.
|
||||||
|
|
||||||
|
@param sender
|
||||||
|
Typically the object that invoked the method.
|
||||||
|
*/
|
||||||
- (IBAction)switchToRadians:(id)sender;
|
- (IBAction)switchToRadians:(id)sender;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method switchToDegrees:
|
||||||
|
@abstract Tells the receiver that it should switch the interpretation of
|
||||||
|
trigonometric function values to degrees.
|
||||||
|
|
||||||
|
@param sender
|
||||||
|
Typically the object that invoked the method.
|
||||||
|
*/
|
||||||
- (IBAction)switchToDegrees:(id)sender;
|
- (IBAction)switchToDegrees:(id)sender;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method switchRadiansDegrees:
|
||||||
|
@abstract Tells the receiver that it should switch the interpretation of
|
||||||
|
trigonometric function values.
|
||||||
|
|
||||||
|
@discussion If the interpretation is currently degrees it is changed to
|
||||||
|
radians or vice versa.
|
||||||
|
|
||||||
|
@param sender
|
||||||
|
Typically the object that invoked the method.
|
||||||
|
*/
|
||||||
- (IBAction)switchRadiansDegrees:(id)sender;
|
- (IBAction)switchRadiansDegrees:(id)sender;
|
||||||
// Functions
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method toggleFunctionsPopover:
|
||||||
|
@abstract Tells the receiver to toggle the visibility of the functions
|
||||||
|
popover.
|
||||||
|
|
||||||
|
@discussion From the functions popover the user can select and insert
|
||||||
|
functions into the displayed expression
|
||||||
|
|
||||||
|
@param sender
|
||||||
|
Typically the object that invoked the method.
|
||||||
|
*/
|
||||||
- (IBAction)toggleFunctionsPopover:(id)sender;
|
- (IBAction)toggleFunctionsPopover:(id)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
//@protocol MPExpressionViewDelegate <NSObject>
|
|
||||||
//@optional
|
|
||||||
//
|
|
||||||
//#pragma mark Editing
|
|
||||||
//- (MPRangePath *)expressionView:(MPExpressionView *)expressionView willChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
|
|
||||||
//- (void)expressionView:(MPExpressionView *)expressionView didChangeSelectionFromRangePath:(MPRangePath *)oldSelection toRangePath:(MPRangePath *)newSelection;
|
|
||||||
//
|
|
||||||
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldChangeSymbolsInRangePath:(MPRangePath *)rangePath replacementElements:(NSArray *)replacement;
|
|
||||||
//- (BOOL)expressionView:(MPExpressionView *)expressionView shouldInsertFunction:(MPFunction *)function replacingRangePath:(MPRangePath *)currentSelection;
|
|
||||||
//- (void)expressionViewDidChange:(MPExpressionView *)expressionView;
|
|
||||||
//
|
|
||||||
//- (BOOL)expressionViewShouldBeginEditing:(MPExpressionView *)expressionView;
|
|
||||||
//- (BOOL)expressionViewShouldEndEditing:(MPExpressionView *)expressionView;
|
|
||||||
//- (void)expressionViewDidBeginEditing:(MPExpressionView *)expressionView;
|
|
||||||
//- (void)expressionDidEndEditing:(MPExpressionView *)expressionView;
|
|
||||||
//
|
|
||||||
//// TODO: Errors...
|
|
||||||
//#pragma mark Evaluation
|
|
||||||
//
|
|
||||||
//- (BOOL)expressionViewShouldEvaluate:(MPExpressionView *)expressionView;
|
|
||||||
//- (void)expressionViewDidEvaluate:(MPExpressionView *)expressionView;
|
|
||||||
//
|
|
||||||
//@end
|
|
||||||
@@ -8,26 +8,25 @@
|
|||||||
|
|
||||||
#import "MPExpressionView.h"
|
#import "MPExpressionView.h"
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
|
|
||||||
#import "MPPowerFunction.h"
|
|
||||||
#import "MPParenthesisFunction.h"
|
|
||||||
#import "MPFractionFunction.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
#import "MPRangePath.h"
|
|
||||||
#import "MPMathRules.h"
|
|
||||||
|
|
||||||
#import "MPExpressionStorage.h"
|
#import "MPExpressionStorage.h"
|
||||||
#import "MPExpressionLayout.h"
|
#import "MPExpressionLayout.h"
|
||||||
#import "MPFunctionLayout.h"
|
|
||||||
|
|
||||||
#import "MPFunctionsViewController.h"
|
#import "MPFunctionsViewController.h"
|
||||||
|
|
||||||
|
#import "MPFunctionLayout.h"
|
||||||
|
#import "MPFractionFunction.h"
|
||||||
|
#import "MPParenthesisFunction.h"
|
||||||
|
#import "MPPowerFunction.h"
|
||||||
|
|
||||||
|
#import "MPParsedExpression.h"
|
||||||
|
#import "MPMathRules.h"
|
||||||
|
#import "MPToken.h"
|
||||||
|
|
||||||
|
#import "MPRangePath.h"
|
||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPExpressionView ()
|
@interface MPExpressionView ()
|
||||||
|
|
||||||
@property (nonatomic, strong) NSButton *functionsButton;
|
@property (nonatomic, strong) NSButton *functionsButton;
|
||||||
@@ -58,6 +57,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@interface MPExpressionView (MPSelectionHelper)
|
@interface MPExpressionView (MPSelectionHelper)
|
||||||
|
|
||||||
- (void)restartCaretTimer;
|
- (void)restartCaretTimer;
|
||||||
- (void)updateCaret:(NSTimer *)timer;
|
- (void)updateCaret:(NSTimer *)timer;
|
||||||
|
|
||||||
@@ -101,6 +101,7 @@
|
|||||||
[self updateCaret:self.caretTimer];
|
[self updateCaret:self.caretTimer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)updateCaret:(NSTimer *)timer
|
- (void)updateCaret:(NSTimer *)timer
|
||||||
{
|
{
|
||||||
self.caretVisible = !self.caretVisible;
|
self.caretVisible = !self.caretVisible;
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
[self setNeedsDisplayInRect:updatedRect];
|
[self setNeedsDisplayInRect:updatedRect];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)selectionRect
|
- (NSRect)selectionRect
|
||||||
{
|
{
|
||||||
NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
|
NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
|
||||||
@@ -120,6 +122,7 @@
|
|||||||
return selectionRect;
|
return selectionRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath
|
- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath
|
||||||
byExtendingSelection:(BOOL)extendingSelection
|
byExtendingSelection:(BOOL)extendingSelection
|
||||||
selectWords:(BOOL)selectWords
|
selectWords:(BOOL)selectWords
|
||||||
@@ -186,6 +189,7 @@
|
|||||||
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath
|
- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath
|
||||||
byExtendingSelection:(BOOL)extendingSelection
|
byExtendingSelection:(BOOL)extendingSelection
|
||||||
selectWords:(BOOL)selectWords
|
selectWords:(BOOL)selectWords
|
||||||
@@ -257,6 +261,7 @@
|
|||||||
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath
|
- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath
|
||||||
newSelectionPath:(NSIndexPath *)newSelectionPath
|
newSelectionPath:(NSIndexPath *)newSelectionPath
|
||||||
{
|
{
|
||||||
@@ -312,9 +317,13 @@
|
|||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@implementation MPExpressionView
|
@implementation MPExpressionView
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
- (id)initWithFrame:(NSRect)frame
|
- (id)initWithFrame:(NSRect)frame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
@@ -324,6 +333,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aDecoder];
|
self = [super initWithCoder:aDecoder];
|
||||||
@@ -333,6 +343,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)initializeExpressionView
|
- (void)initializeExpressionView
|
||||||
{
|
{
|
||||||
// Setup the Expression Storage
|
// Setup the Expression Storage
|
||||||
@@ -348,6 +359,7 @@
|
|||||||
[self restartCaretTimer];
|
[self restartCaretTimer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)initializeButtons
|
- (void)initializeButtons
|
||||||
{
|
{
|
||||||
NSButton *functionsButton = self.functionsButton;
|
NSButton *functionsButton = self.functionsButton;
|
||||||
@@ -368,6 +380,7 @@
|
|||||||
constant:0]];
|
constant:0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)initializeErrorsViews
|
- (void)initializeErrorsViews
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -394,34 +407,42 @@
|
|||||||
views:variableBindings]];
|
views:variableBindings]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - NSView Configuration
|
#pragma mark - NSView Configuration
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)canBecomeKeyView
|
- (BOOL)canBecomeKeyView
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)isOpaque
|
- (BOOL)isOpaque
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)resetCursorRects
|
- (void)resetCursorRects
|
||||||
{
|
{
|
||||||
[self addCursorRect:self.bounds
|
[self addCursorRect:self.bounds
|
||||||
cursor:[NSCursor IBeamCursor]];
|
cursor:[NSCursor IBeamCursor]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)becomeFirstResponder
|
- (BOOL)becomeFirstResponder
|
||||||
{
|
{
|
||||||
[self restartCaretTimer];
|
[self restartCaretTimer];
|
||||||
return [super becomeFirstResponder];
|
return [super becomeFirstResponder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)resignFirstResponder
|
- (BOOL)resignFirstResponder
|
||||||
{
|
{
|
||||||
[self.caretTimer invalidate];
|
[self.caretTimer invalidate];
|
||||||
@@ -430,12 +451,14 @@
|
|||||||
return [super resignFirstResponder];
|
return [super resignFirstResponder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setFrame:(NSRect)frameRect
|
- (void)setFrame:(NSRect)frameRect
|
||||||
{
|
{
|
||||||
[self setNeedsLayout:YES];
|
[self setNeedsLayout:YES];
|
||||||
[super setFrame:frameRect];
|
[super setFrame:frameRect];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSSize)intrinsicContentSize
|
- (NSSize)intrinsicContentSize
|
||||||
{
|
{
|
||||||
NSSize size = self.expressionStorage.rootLayout.bounds.size;
|
NSSize size = self.expressionStorage.rootLayout.bounds.size;
|
||||||
@@ -443,7 +466,10 @@
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - Properties
|
||||||
|
|
||||||
|
|
||||||
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
|
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
|
||||||
{
|
{
|
||||||
_expressionStorage.expressionView = nil;
|
_expressionStorage.expressionView = nil;
|
||||||
@@ -452,6 +478,7 @@
|
|||||||
[self invalidateIntrinsicContentSize];
|
[self invalidateIntrinsicContentSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setSelection:(MPRangePath *)selection
|
- (void)setSelection:(MPRangePath *)selection
|
||||||
{
|
{
|
||||||
_selection = selection;
|
_selection = selection;
|
||||||
@@ -459,6 +486,7 @@
|
|||||||
self.needsDisplay = YES;
|
self.needsDisplay = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setSyntaxErrors:(NSArray *)syntaxErrors
|
- (void)setSyntaxErrors:(NSArray *)syntaxErrors
|
||||||
{
|
{
|
||||||
_syntaxErrors = syntaxErrors;
|
_syntaxErrors = syntaxErrors;
|
||||||
@@ -491,12 +519,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setMathError:(NSError *)mathError
|
- (void)setMathError:(NSError *)mathError
|
||||||
{
|
{
|
||||||
_mathError = mathError;
|
_mathError = mathError;
|
||||||
self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @"";
|
self.mathErrorTextField.stringValue = mathError != nil ? mathError.localizedDescription : @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSButton *)functionsButton
|
- (NSButton *)functionsButton
|
||||||
{
|
{
|
||||||
if (!_functionsButton) {
|
if (!_functionsButton) {
|
||||||
@@ -520,6 +550,7 @@
|
|||||||
return _functionsButton;
|
return _functionsButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPopUpButton *)syntaxErrorsPopUpButton
|
- (NSPopUpButton *)syntaxErrorsPopUpButton
|
||||||
{
|
{
|
||||||
if (!_syntaxErrorsPopUpButton) {
|
if (!_syntaxErrorsPopUpButton) {
|
||||||
@@ -539,6 +570,7 @@
|
|||||||
return _syntaxErrorsPopUpButton;
|
return _syntaxErrorsPopUpButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSTextField *)mathErrorTextField
|
- (NSTextField *)mathErrorTextField
|
||||||
{
|
{
|
||||||
if (!_mathErrorTextField) {
|
if (!_mathErrorTextField) {
|
||||||
@@ -555,6 +587,7 @@
|
|||||||
return _mathErrorTextField;
|
return _mathErrorTextField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)didSelectError:(NSPopUpButton *)sender
|
- (void)didSelectError:(NSPopUpButton *)sender
|
||||||
{
|
{
|
||||||
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
|
NSError *error = self.syntaxErrors[sender.indexOfSelectedItem-1];
|
||||||
@@ -564,7 +597,10 @@
|
|||||||
self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
|
self.selection = MPMakeRangePath(pathToExpression, errorRange.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Mouse Event Handling
|
#pragma mark - Mouse Event Handling
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent *)theEvent
|
- (void)mouseDown:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
||||||
@@ -577,6 +613,7 @@
|
|||||||
self.selection = MPMakeRangePath(selectionPath, 0);
|
self.selection = MPMakeRangePath(selectionPath, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseDragged:(NSEvent *)theEvent
|
- (void)mouseDragged:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
||||||
@@ -590,7 +627,10 @@
|
|||||||
newSelectionPath:mouseSelectionPath];
|
newSelectionPath:mouseSelectionPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Key Event Handling
|
#pragma mark Key Event Handling
|
||||||
|
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)theEvent
|
- (void)keyDown:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
NSString *characters = theEvent.characters;
|
NSString *characters = theEvent.characters;
|
||||||
@@ -617,6 +657,15 @@
|
|||||||
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
|
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
|
||||||
|
|
||||||
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
|
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
|
||||||
|
if ([characters isEqualTo:@"i"]) {
|
||||||
|
if (self.selection.location.lastIndex > 0) {
|
||||||
|
id<MPExpressionElement> symbol = [self.expressionStorage symbolAtIndex:self.selection.location.lastIndex-1];
|
||||||
|
if ([symbol isEqual:@"p"]) {
|
||||||
|
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], self.selection.length+1);
|
||||||
|
characters = @"π";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if ([characters isEqualToString:@"*"]) {
|
if ([characters isEqualToString:@"*"]) {
|
||||||
characters = @"⋅";
|
characters = @"⋅";
|
||||||
}
|
}
|
||||||
@@ -629,22 +678,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
|
||||||
- (void)switchToDegrees:(id)sender
|
- (void)switchToDegrees:(id)sender
|
||||||
{
|
{
|
||||||
[MPMathRules sharedRules].isUsingDegrees = YES;
|
[MPMathRules sharedRules].isUsingDegrees = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)switchToRadians:(id)sender
|
- (void)switchToRadians:(id)sender
|
||||||
{
|
{
|
||||||
[MPMathRules sharedRules].isUsingDegrees = NO;
|
[MPMathRules sharedRules].isUsingDegrees = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)switchRadiansDegrees:(id)sender
|
- (void)switchRadiansDegrees:(id)sender
|
||||||
{
|
{
|
||||||
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
|
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (IBAction)toggleFunctionsPopover:(id)sender
|
- (IBAction)toggleFunctionsPopover:(id)sender
|
||||||
{
|
{
|
||||||
if (self.functionsPopover == nil || self.functionsViewController == nil) {
|
if (self.functionsPopover == nil || self.functionsViewController == nil) {
|
||||||
@@ -663,6 +718,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)insertFunction:(MPFunction *)function
|
- (void)insertFunction:(MPFunction *)function
|
||||||
{
|
{
|
||||||
[self.functionsPopover close];
|
[self.functionsPopover close];
|
||||||
@@ -678,8 +734,10 @@
|
|||||||
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
|
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Editing Actions
|
#pragma mark Editing Actions
|
||||||
|
|
||||||
|
|
||||||
- (void)insertParenthesisFunction:(id)sender
|
- (void)insertParenthesisFunction:(id)sender
|
||||||
{
|
{
|
||||||
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
|
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
|
||||||
@@ -698,6 +756,7 @@
|
|||||||
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
|
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)insertClosingParenthesis
|
- (void)insertClosingParenthesis
|
||||||
{
|
{
|
||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
@@ -721,6 +780,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)insertPowerFunction
|
- (void)insertPowerFunction
|
||||||
{
|
{
|
||||||
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
|
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
|
||||||
@@ -739,6 +799,7 @@
|
|||||||
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
|
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)insertFractionFunction
|
- (void)insertFractionFunction
|
||||||
{
|
{
|
||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
@@ -792,6 +853,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)insertNewline:(id)sender
|
- (void)insertNewline:(id)sender
|
||||||
{
|
{
|
||||||
if (self.target && self.action) {
|
if (self.target && self.action) {
|
||||||
@@ -801,6 +863,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)deleteBackward:(id)sender
|
- (void)deleteBackward:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
@@ -853,6 +916,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)delete:(id)sender
|
- (void)delete:(id)sender
|
||||||
{
|
{
|
||||||
[self.expressionStorage replaceItemsInRangePath:self.selection
|
[self.expressionStorage replaceItemsInRangePath:self.selection
|
||||||
@@ -861,8 +925,10 @@
|
|||||||
self.selection = MPMakeRangePath(self.selection.location, 0);
|
self.selection = MPMakeRangePath(self.selection.location, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Selection Actions
|
#pragma mark Selection Actions
|
||||||
|
|
||||||
|
|
||||||
- (void)moveRight:(id)sender
|
- (void)moveRight:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
@@ -875,6 +941,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveLeft:(id)sender
|
- (void)moveLeft:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length > 0) {
|
if (self.selection.length > 0) {
|
||||||
@@ -887,6 +954,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveWordRight:(id)sender
|
- (void)moveWordRight:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *location = self.selection.maxRangePath;
|
NSIndexPath *location = self.selection.maxRangePath;
|
||||||
@@ -896,6 +964,7 @@
|
|||||||
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveWordLeft:(id)sender
|
- (void)moveWordLeft:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *location = self.selection.location;
|
NSIndexPath *location = self.selection.location;
|
||||||
@@ -905,17 +974,20 @@
|
|||||||
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
self.selection = MPMakeRangePath(newSelectionLocation, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveToBeginningOfLine:(id)sender
|
- (void)moveToBeginningOfLine:(id)sender
|
||||||
{
|
{
|
||||||
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
|
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveToEndOfLine:(id)sender
|
- (void)moveToEndOfLine:(id)sender
|
||||||
{
|
{
|
||||||
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
|
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
|
||||||
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
|
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveLeftAndModifySelection:(id)sender
|
- (void)moveLeftAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
@@ -935,6 +1007,7 @@
|
|||||||
self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location];
|
self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveRightAndModifySelection:(id)sender
|
- (void)moveRightAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
@@ -955,6 +1028,7 @@
|
|||||||
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveWordRightAndModifySelection:(id)sender
|
- (void)moveWordRightAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
@@ -977,6 +1051,7 @@
|
|||||||
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveWordLeftAndModifySelection:(id)sender
|
- (void)moveWordLeftAndModifySelection:(id)sender
|
||||||
{
|
{
|
||||||
if (self.selection.length == 0) {
|
if (self.selection.length == 0) {
|
||||||
@@ -999,6 +1074,7 @@
|
|||||||
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveUp:(id)sender
|
- (void)moveUp:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
|
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
|
||||||
@@ -1011,6 +1087,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)moveDown:(id)sender
|
- (void)moveDown:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
|
NSIndexPath *targetExpressionPath = [self.selection.location indexPathByRemovingLastIndex];
|
||||||
@@ -1023,13 +1100,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)selectAll:(id)sender
|
- (void)selectAll:(id)sender
|
||||||
{
|
{
|
||||||
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
|
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
|
||||||
self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
|
self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Drawing Methods
|
#pragma mark Drawing Methods
|
||||||
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
// Draw the background
|
// Draw the background
|
||||||
@@ -1066,6 +1147,7 @@
|
|||||||
|
|
||||||
#pragma mark - User Interface Validations
|
#pragma mark - User Interface Validations
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
||||||
{
|
{
|
||||||
BOOL degrees = [MPMathRules sharedRules].isUsingDegrees;
|
BOOL degrees = [MPMathRules sharedRules].isUsingDegrees;
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFactorialTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFactorialTerm;
|
@class MPFactorialTerm;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#import "MPFactorialTerm.h"
|
#import "MPFactorialTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFactorialTerm
|
@implementation MPFactorialTerm
|
||||||
|
|
||||||
- (instancetype)initWithTerm:(MPTerm *)term
|
- (instancetype)initWithTerm:(MPTerm *)term
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSDecimalNumber *value = [self.term evaluate:error];
|
NSDecimalNumber *value = [self.term evaluate:error];
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFractionFunction</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFractionFunction, MPExpression;
|
@class MPFractionFunction, MPExpression;
|
||||||
|
|
||||||
|
|||||||
@@ -11,21 +11,26 @@
|
|||||||
#import "MPFractionTerm.h"
|
#import "MPFractionTerm.h"
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFractionFunction
|
@implementation MPFractionFunction
|
||||||
|
|
||||||
MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression)
|
MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression)
|
||||||
MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression)
|
MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression)
|
||||||
|
|
||||||
|
|
||||||
- (NSArray *)childrenAccessors
|
- (NSArray *)childrenAccessors
|
||||||
{
|
{
|
||||||
return @[@"nominatorExpression", @"denominatorExpression"];
|
return @[@"nominatorExpression", @"denominatorExpression"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (Class)functionTermClass
|
- (Class)functionTermClass
|
||||||
{
|
{
|
||||||
return [MPFractionTerm class];
|
return [MPFractionTerm class];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression];
|
return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression];
|
||||||
|
|||||||
@@ -9,12 +9,32 @@
|
|||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFractionFunctionLayout</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFractionFunctionLayout, MPFractionFunction;
|
@class MPFractionFunctionLayout, MPFractionFunction;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPFractionFunctionLayout
|
||||||
|
@abstract A fraction function layout displays a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion The nominator is displayed above the denominator. Between the two
|
||||||
|
children a horizontal bar is drawn.
|
||||||
|
*/
|
||||||
@interface MPFractionFunctionLayout : MPFunctionLayout
|
@interface MPFractionFunctionLayout : MPFunctionLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method fractionFunction
|
||||||
|
@abstract Returns the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFractionFunction@/link</code> represented by
|
||||||
|
the receiver.
|
||||||
|
*/
|
||||||
- (MPFractionFunction *)fractionFunction;
|
- (MPFractionFunction *)fractionFunction;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#import "MPFractionFunction.h"
|
#import "MPFractionFunction.h"
|
||||||
|
|
||||||
|
|
||||||
#define kFractionFunctionLineWidth 1.0
|
#define kFractionFunctionLineWidth 1.0
|
||||||
#define kFractionFunctionHorizontalInset 2.0
|
#define kFractionFunctionHorizontalInset 2.0
|
||||||
#define kFractionFunctionNominatorOffset 0
|
#define kFractionFunctionNominatorOffset 0
|
||||||
@@ -17,6 +18,8 @@
|
|||||||
|
|
||||||
#define MPFractionMiddle (CTFontGetCapHeight((CTFontRef)self.font) / 2)
|
#define MPFractionMiddle (CTFontGetCapHeight((CTFontRef)self.font) / 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFractionFunctionLayout
|
@implementation MPFractionFunctionLayout
|
||||||
|
|
||||||
- (MPFractionFunction *)fractionFunction
|
- (MPFractionFunction *)fractionFunction
|
||||||
@@ -24,21 +27,25 @@
|
|||||||
return (MPFractionFunction *)self.function;
|
return (MPFractionFunction *)self.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren
|
- (NSIndexSet *)indexesOfRemainingChildren
|
||||||
{
|
{
|
||||||
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
NSRect bounds = self.bounds;
|
NSRect bounds = self.bounds;
|
||||||
@@ -54,11 +61,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
if (point.x < self.bounds.size.width / 2) {
|
if (point.x < self.bounds.size.width / 2) {
|
||||||
@@ -68,6 +77,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)generateBounds
|
- (NSRect)generateBounds
|
||||||
{
|
{
|
||||||
NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds;
|
NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds;
|
||||||
@@ -80,6 +90,7 @@
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)draw
|
- (void)draw
|
||||||
{
|
{
|
||||||
CGFloat y = MPFractionMiddle - kFractionFunctionLineWidth / 2;
|
CGFloat y = MPFractionMiddle - kFractionFunctionLineWidth / 2;
|
||||||
|
|||||||
@@ -9,10 +9,24 @@
|
|||||||
#import "MPFunctionTerm.h"
|
#import "MPFunctionTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFractionTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFractionTerm;
|
@class MPFractionTerm;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPFractionTerm
|
||||||
|
@abstract Represens a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFractionFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A fraction is evaluating by dividing the nominator by the
|
||||||
|
denominator.
|
||||||
|
*/
|
||||||
@interface MPFractionTerm : MPFunctionTerm
|
@interface MPFractionTerm : MPFunctionTerm
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFractionTerm
|
@implementation MPFractionTerm
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
#import "MPToken.h"
|
#import "MPToken.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFunction(MPToken) category</code>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@category MPFunction (MPToken)
|
@category MPFunction (MPToken)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPFunction</code> class.
|
||||||
|
|
||||||
<code>MPFunction</code> is a half abstract class that is designed to be
|
<code>MPFunction</code> is a half abstract class that is designed to be
|
||||||
subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called
|
subclassed. Subclasses of the <code>MPFunction</code> class (subsequently called
|
||||||
<i>functions</i>) need to regard the following guidelines:
|
<i>functions</i>) need to regard the following guidelines:
|
||||||
@@ -41,7 +43,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@define MPFunctionAccessorImplementation
|
@define MPFunctionAccessorImplementation
|
||||||
@abstract This macro implements the setter of a previously declared
|
@abstract This macro implements the setter of a previously declared
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
@implementation MPFunction
|
@implementation MPFunction
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,53 +9,334 @@
|
|||||||
#import "MPLayout.h"
|
#import "MPLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFunctionLayout</code> class.
|
||||||
|
|
||||||
|
The <code>MPFunctionLayout</code> class is an abstract class that implements
|
||||||
|
many of the methods from the layout system. Concrete subclasses implement the
|
||||||
|
actual layout of a specific function and its children. For that reason
|
||||||
|
<code>MPFunctionLayout</code> features a private cache. In that cache any
|
||||||
|
subclass can store objects so they do not need to be recreated everytime the
|
||||||
|
function is rendered.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFunctionLayout, MPFunction, MPExpressionLayout;
|
@class MPFunctionLayout, MPFunction, MPExpressionLayout;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPFunctionLayout
|
||||||
|
@abstract A function layout represents a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion <code>MPFunctionLayout</code> is an abstract class that
|
||||||
|
implements many required methods of the layout system. For more
|
||||||
|
information about the layout system see the documentation on the
|
||||||
|
<code>@link //apple_ref/occ/cl/MPLayout@/link</code> class.
|
||||||
|
*/
|
||||||
@interface MPFunctionLayout : MPLayout
|
@interface MPFunctionLayout : MPLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method functionLayoutForFunction:parent:
|
||||||
|
@abstract Creates a function layout that can draw the specified function.
|
||||||
|
|
||||||
|
@discussion This method is the preferred whay to construct function layouts.
|
||||||
|
|
||||||
|
@param function
|
||||||
|
The function to create a layout for. Must not be
|
||||||
|
<code>nil</code>.
|
||||||
|
|
||||||
|
@param parent
|
||||||
|
The parent of the created function layout. May be
|
||||||
|
<code>nil</code>.
|
||||||
|
|
||||||
|
@return A newly created function layout.
|
||||||
|
*/
|
||||||
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
|
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
|
||||||
parent:(MPExpressionLayout *)parent;
|
parent:(MPExpressionLayout *)parent;
|
||||||
|
|
||||||
@property (readonly, nonatomic, weak) MPFunction *function;
|
|
||||||
|
|
||||||
@end
|
/*!
|
||||||
|
@method initWithFunction:parent:
|
||||||
|
@abstract Initializes the receiver with the specified function and parent.
|
||||||
|
|
||||||
@interface MPFunctionLayout (MPSubclassOverride)
|
@discussion This method should not be called direcly. It is however possible
|
||||||
|
do perform custom initialization in sublcasses by overriding this method.
|
||||||
|
|
||||||
|
This method is the designated initializer of the
|
||||||
|
<code>MPFunctionLayout</code> class.
|
||||||
|
|
||||||
|
@param function
|
||||||
|
The function represented by the receiver. Must not be
|
||||||
|
<code>nil</code>.
|
||||||
|
|
||||||
|
@param parent
|
||||||
|
The parent of the receiver or <code>nil</code> if it does not
|
||||||
|
have one.
|
||||||
|
|
||||||
|
@return A newly initialized <code>MPFunctionLayout</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithFunction:(MPFunction *)function
|
- (instancetype)initWithFunction:(MPFunction *)function
|
||||||
parent:(MPExpressionLayout *)parent;
|
parent:(MPExpressionLayout *)parent;
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
|
||||||
|
/*!
|
||||||
|
@property function
|
||||||
|
@abstract The function represented by the receiver.
|
||||||
|
*/
|
||||||
|
@property (readonly, nonatomic, weak) MPFunction *function;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method lineForPrivateCacheIndex:generator:
|
||||||
|
@abstract Returns the cached line for the specified private index.
|
||||||
|
|
||||||
|
@discussion This is a convenience method that calls <code>@link
|
||||||
|
//apple_ref/occ/instm/MPFunctionLayout/objectForPrivateCacheIndex:generator:@/link</code>.
|
||||||
|
See the documentation on that method for details.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the line to retrieve. Private cache indexes start at <code>0</code>.
|
||||||
|
|
||||||
|
@param generator
|
||||||
|
A block that generates a line in case there is no cached one.
|
||||||
|
|
||||||
|
@return The cached line or the newly generated one if there was no cache value.
|
||||||
|
*/
|
||||||
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
||||||
generator:(CTLineRef (^)())generator;
|
generator:(CTLineRef (^)())generator;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method objectForPrivateCacheIndex:generator:
|
||||||
|
@abstract Returns the cached value for the specified private index.
|
||||||
|
|
||||||
|
@discussion If there is no object in the private cache for the specified
|
||||||
|
index the specified <code>generator</code> is invoked. The result
|
||||||
|
of the <code>generator</code> is then stored at the specified
|
||||||
|
<code>index</code> and returned.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the private cache item to retrieve. Private cache
|
||||||
|
indexes start at <code>0</code>.
|
||||||
|
|
||||||
|
@param generator
|
||||||
|
A block that generates an object in case there is no cached one.
|
||||||
|
|
||||||
|
@return The cached object or the newly generated one if there was no
|
||||||
|
cache value.
|
||||||
|
*/
|
||||||
- (id)objectForPrivateCacheIndex:(NSUInteger)index
|
- (id)objectForPrivateCacheIndex:(NSUInteger)index
|
||||||
generator:(id (^)())generator;
|
generator:(id (^)())generator;
|
||||||
|
|
||||||
// Should also implement accessor method for special function type:
|
@end
|
||||||
// - (MPCustomFunction *)customFunction
|
|
||||||
// {
|
|
||||||
// return (MPCustomFunction *)self.function;
|
|
||||||
// }
|
|
||||||
|
|
||||||
#pragma mark Size and Drawing Methods
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@category MPFunctionLayout (MPSubclassOverride)
|
||||||
|
@abstract The methods defined in this category must be implemented by any
|
||||||
|
concrete subclass of <code>MPFunctionLayout</code>.
|
||||||
|
*/
|
||||||
|
@interface MPFunctionLayout (MPSubclassOverride)
|
||||||
|
|
||||||
|
/* Apart from the methods below it is recommended (although not required) to
|
||||||
|
* implement a method in the following form:
|
||||||
|
* - (CustomFunctionType *)customFunction
|
||||||
|
* {
|
||||||
|
* return (CustomFunctionType *)self.function;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method childAtIndexUsesSmallSize:
|
||||||
|
@abstract Determines whether the child expression at the specified index
|
||||||
|
should be drawn using the small font.
|
||||||
|
|
||||||
|
@discussion This method may be overridden to change the default behaviour.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child whose size is to be determined.
|
||||||
|
|
||||||
|
@return <code>YES</code> if the receiver's child at the specified
|
||||||
|
<code>index</code> should use the small font, <code>NO</code>
|
||||||
|
otherwise. The default implementation returns <code>NO</code>.
|
||||||
|
*/
|
||||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index; // May be implemented
|
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index; // May be implemented
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index; // To be implemented
|
|
||||||
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
|
|
||||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point; // To be implemented
|
|
||||||
- (NSRect)generateBounds; // To be implemented
|
|
||||||
- (void)draw; // To be implemented
|
|
||||||
|
|
||||||
// Specify the child that is used when the cursor enters the function from the left or right respectively
|
|
||||||
- (NSUInteger)indexOfLeadingChild; // To be implemented
|
|
||||||
- (NSUInteger)indexOfTrailingChild; // To be implemented
|
|
||||||
|
|
||||||
// The index of the child before (left) or after (right) the child at @c index. return NSNotFound if there is no child before or after @c index.
|
/*!
|
||||||
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index; // May be implemented
|
@method offsetOfChildLayoutAtIndex:
|
||||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index; // May be implemented
|
@abstract Implementation requirement from superclass. See <code>@link
|
||||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index; // May be implemented
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
|
||||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index; // May be implemented
|
for details.
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren; // May be implemented
|
*/
|
||||||
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexPathForLocalMousePoint:
|
||||||
|
@abstract Performs hit testing.
|
||||||
|
|
||||||
|
@discussion This method is called when the user selected a point in a
|
||||||
|
function that is not in any of its children. The return value of
|
||||||
|
this method identifies the position relative to the function
|
||||||
|
represented by the receiver where the caret is to be placed.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses.
|
||||||
|
|
||||||
|
@param point
|
||||||
|
The location the user clicked at relative to the receiver.
|
||||||
|
|
||||||
|
@return An index path identifying the new caret position relative to the
|
||||||
|
function represented by the receiver. Use an index path
|
||||||
|
containing a single index to place the caret before or after the
|
||||||
|
represented function (<code>0</code> means before, <code>1</code>
|
||||||
|
means after).
|
||||||
|
*/
|
||||||
|
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method generateBounds
|
||||||
|
@abstract Implementation requirement from superclass. See <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
|
||||||
|
for details.
|
||||||
|
*/
|
||||||
|
- (NSRect)generateBounds;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method draw
|
||||||
|
@abstract Implementation requirement from superclass. See <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/draw@/link</code>
|
||||||
|
for details.
|
||||||
|
*/
|
||||||
|
- (void)draw;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfLeadingChild
|
||||||
|
@abstract Identifies the index of the receiver's child that will get
|
||||||
|
selected when the cursor <i>enters</i> the function in the
|
||||||
|
direction of reading.
|
||||||
|
|
||||||
|
@discussion You may implement this method do override the default behaviour.
|
||||||
|
|
||||||
|
@return The index of the leading child. The default implementation
|
||||||
|
returns <code>0</code>.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfLeadingChild;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfTrailingChild
|
||||||
|
@abstract Identifies the index of the receiver's child that will get
|
||||||
|
selected when the cursor <i>enters</i> the function in the
|
||||||
|
direction opposite to reading.
|
||||||
|
|
||||||
|
@discussion You may implement this method do override the default behaviour.
|
||||||
|
|
||||||
|
@return The index of the trailing child. The default implementation
|
||||||
|
returns <code>0</code>.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfTrailingChild;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfChildBeforeChildAtIndex:
|
||||||
|
@abstract Identifies the index of the receiver's child that will get
|
||||||
|
selected when the cursor <i>leaves</i> the child at the specified
|
||||||
|
index in the direction opposite to reading.
|
||||||
|
|
||||||
|
@discussion Return <code>NSNotFound</code> from this method if the function
|
||||||
|
should be <i>left</i> when the child at the specified index is
|
||||||
|
<i>left</i>.
|
||||||
|
|
||||||
|
You may implement this method to override the default behaviour.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child the caret <i>left</i>.
|
||||||
|
|
||||||
|
@return The index of the child that is to be <i>entered</i> or
|
||||||
|
<code>NSNotFound</code> if there are no more children. The
|
||||||
|
default implementation returns <code>NSNotFound</code>.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfChildAfterChildAtIndex:
|
||||||
|
@abstract Identifies the index of the receiver's child that will get
|
||||||
|
selected when the cursor <i>leaves</i> the child at the specified
|
||||||
|
index in the direction of reading.
|
||||||
|
|
||||||
|
@discussion Return <code>NSNotFound</code> from this method if the function
|
||||||
|
should be <i>left</i> when the child at the specified index is
|
||||||
|
<i>left</i>.
|
||||||
|
|
||||||
|
You may implement this method to override the default behaviour.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child the caret <i>left</i>.
|
||||||
|
|
||||||
|
@return The index of the child that is to be <i>entered</i> or
|
||||||
|
<code>NSNotFound</code> if there are no more children.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfChildBelowChildAtIndex:
|
||||||
|
@abstract Returns the index of the child that is graphically positioned
|
||||||
|
below the child at the specified index.
|
||||||
|
|
||||||
|
@discussion If there are no other children below the one at the specified
|
||||||
|
index this method should return <code>index</code>.
|
||||||
|
|
||||||
|
You may implement this method to override the default behaviour.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child whose neighbor is to be determined.
|
||||||
|
|
||||||
|
@return The index of the child directly below the one at the specified
|
||||||
|
index. The default implementation returns <code>index</code>.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexOfChildAboveChildAtIndex:
|
||||||
|
@abstract Returns the index of the child that is graphically positioned
|
||||||
|
above the child at the specified index.
|
||||||
|
|
||||||
|
@discussion If there are no other children above the one at the specified
|
||||||
|
index this method should return <code>index</code>.
|
||||||
|
|
||||||
|
You may implement this method to override the default behaviour.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child whose neighbor is to be determined.
|
||||||
|
|
||||||
|
@return The index of the child directly above the one at the specified
|
||||||
|
index. The default implementation returns <code>index</code>.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexesOfRemainingChildren
|
||||||
|
@abstract Returns the indexes of the receiver's children that should
|
||||||
|
replace the receiver if it is deleted.
|
||||||
|
|
||||||
|
@discussion You may implement this method to override the default behaviour.
|
||||||
|
|
||||||
|
@return The indexes of the children that replace the receiver when it is
|
||||||
|
deleted. The default implementation returns an empty index set.
|
||||||
|
*/
|
||||||
|
- (NSIndexSet *)indexesOfRemainingChildren;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -8,24 +8,28 @@
|
|||||||
|
|
||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
#import "MPExpression.h"
|
#import "MPExpressionLayout.h"
|
||||||
|
|
||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
#import "MPSumFunction.h"
|
#import "MPFractionFunction.h"
|
||||||
#import "MPParenthesisFunction.h"
|
#import "MPParenthesisFunction.h"
|
||||||
#import "MPPowerFunction.h"
|
#import "MPPowerFunction.h"
|
||||||
#import "MPFractionFunction.h"
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
#import "MPExpressionLayout.h"
|
#import "MPFractionFunctionLayout.h"
|
||||||
#import "MPSumFunctionLayout.h"
|
|
||||||
#import "MPParenthesisFunctionLayout.h"
|
#import "MPParenthesisFunctionLayout.h"
|
||||||
#import "MPPowerFunctionLayout.h"
|
#import "MPPowerFunctionLayout.h"
|
||||||
#import "MPFractionFunctionLayout.h"
|
#import "MPSumFunctionLayout.h"
|
||||||
|
|
||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFunctionLayout
|
@implementation MPFunctionLayout
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
|
+ (MPFunctionLayout *)functionLayoutForFunction:(MPFunction *)function
|
||||||
parent:(MPExpressionLayout *)parent
|
parent:(MPExpressionLayout *)parent
|
||||||
{
|
{
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
parent:parent];
|
parent:parent];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (instancetype)initWithFunction:(MPFunction *)function
|
- (instancetype)initWithFunction:(MPFunction *)function
|
||||||
parent:(MPExpressionLayout *)parent
|
parent:(MPExpressionLayout *)parent
|
||||||
{
|
{
|
||||||
@@ -53,7 +58,9 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
|
|
||||||
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
- (CTLineRef)lineForPrivateCacheIndex:(NSUInteger)index
|
||||||
generator:(CTLineRef (^)())generator
|
generator:(CTLineRef (^)())generator
|
||||||
{
|
{
|
||||||
@@ -67,6 +74,7 @@
|
|||||||
return (__bridge CTLineRef)(lineObject);
|
return (__bridge CTLineRef)(lineObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (id)objectForPrivateCacheIndex:(NSUInteger)index
|
- (id)objectForPrivateCacheIndex:(NSUInteger)index
|
||||||
generator:(id (^)())generator
|
generator:(id (^)())generator
|
||||||
{
|
{
|
||||||
@@ -74,11 +82,13 @@
|
|||||||
return [self cachableObjectForIndex:actualIndex generator:generator];
|
return [self cachableObjectForIndex:actualIndex generator:generator];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)numberOfChildren
|
- (NSUInteger)numberOfChildren
|
||||||
{
|
{
|
||||||
return self.function.numberOfChildren;
|
return self.function.numberOfChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return [self cachableObjectForIndex:index generator:^id{
|
return [self cachableObjectForIndex:index generator:^id{
|
||||||
@@ -91,11 +101,13 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
// A single index is used to communicate back wether the
|
// A single index is used to communicate back wether the
|
||||||
@@ -116,36 +128,43 @@
|
|||||||
return [self indexPathForLocalMousePoint:point];
|
return [self indexPathForLocalMousePoint:point];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfLeadingChild
|
- (NSUInteger)indexOfLeadingChild
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfTrailingChild
|
- (NSUInteger)indexOfTrailingChild
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return NSNotFound;
|
return NSNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return NSNotFound;
|
return NSNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren
|
- (NSIndexSet *)indexesOfRemainingChildren
|
||||||
{
|
{
|
||||||
return [NSIndexSet indexSet];
|
return [NSIndexSet indexSet];
|
||||||
|
|||||||
@@ -8,6 +8,28 @@
|
|||||||
|
|
||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFunctionTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define MPEvaluateExpression
|
||||||
|
@abstract Evaluates the receiver's child at the specified index and assigns
|
||||||
|
the evaluation result to a variable.
|
||||||
|
|
||||||
|
@discussion If errors occur during evaluation this macro returns
|
||||||
|
<code>nil</code>. Also there has to exist a <code>NSError *__autoreleasing *</code> pointer called <code>error</code>.
|
||||||
|
|
||||||
|
@param var
|
||||||
|
The name of the variable that will be declared in this macro. It
|
||||||
|
will be of the type <code>NSDecimalNumber *</code> and contain
|
||||||
|
the result of the evaluation.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child to be evaluated.
|
||||||
|
*/
|
||||||
#define MPEvaluateExpression(var, index) NSDecimalNumber *var = [[self expressionAtIndex:index] evaluate:error]; if (var == nil) return nil
|
#define MPEvaluateExpression(var, index) NSDecimalNumber *var = [[self expressionAtIndex:index] evaluate:error]; if (var == nil) return nil
|
||||||
|
|
||||||
|
|
||||||
@@ -15,11 +37,62 @@
|
|||||||
@class MPFunctionTerm, MPFunction, MPParsedExpression;
|
@class MPFunctionTerm, MPFunction, MPParsedExpression;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPFunctionTerm
|
||||||
|
@abstract A function term represents the term of a generic function.
|
||||||
|
|
||||||
|
@discussion This class is an abstract class that can not be evaluated. It is
|
||||||
|
the common superclass of all terms that represent <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code> instances.
|
||||||
|
*/
|
||||||
@interface MPFunctionTerm : MPTerm
|
@interface MPFunctionTerm : MPTerm
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithFunction:errors:
|
||||||
|
@abstract Initializes a <code>MPFunctionTerm</code> from the specified
|
||||||
|
<code>@link //apple_ref/occ/cl/MPFunction@/link</code> instance.
|
||||||
|
|
||||||
|
@discussion This method parses the children of the specified
|
||||||
|
<code>function</code> and stores the respective parsed
|
||||||
|
expressions. They are accessible using the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPFunctionTerm/expressionAtIndex:@/link</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
Whether the children should contain a variable definition is
|
||||||
|
determined using the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPFunction/expectsVariableDefinitionInChildAtIndex:@/link</code>
|
||||||
|
|
||||||
|
@param function
|
||||||
|
The function to initialize the receiver with. This method parses
|
||||||
|
the function's children. If any of them contain syntax errors
|
||||||
|
they are returned indirectly through the <code>errors</code>
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
@param errors
|
||||||
|
If any of the <code>function</code>'s children contain syntax
|
||||||
|
errors they are returned indirectly through this parameter. In
|
||||||
|
that case the method returns <code>nil</code>. If there are no
|
||||||
|
errors the parameter is not modified. This parameter is never set
|
||||||
|
to an empty array.
|
||||||
|
|
||||||
|
@return A new <code>MPFunctionTerm</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithFunction:(MPFunction *)function
|
- (instancetype)initWithFunction:(MPFunction *)function
|
||||||
errors:(NSArray *__autoreleasing *)errors; /* designated initializer */
|
errors:(NSArray *__autoreleasing *)errors; /* designated initializer */
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method expressionAtIndex:
|
||||||
|
@abstract Returns a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPParsedExpression@/link</code> instance that
|
||||||
|
represents the child at <code>anIndex</code> of the function that
|
||||||
|
is represented by the receiver.
|
||||||
|
|
||||||
|
@param anIndex
|
||||||
|
The index of the child.
|
||||||
|
|
||||||
|
@return A parsed expression that represents th
|
||||||
|
*/
|
||||||
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex;
|
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,10 +9,6 @@
|
|||||||
#import "MPFunctionTerm.h"
|
#import "MPFunctionTerm.h"
|
||||||
|
|
||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -64,6 +60,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex
|
- (MPParsedExpression *)expressionAtIndex:(NSUInteger)anIndex
|
||||||
{
|
{
|
||||||
return [self.parsedExpressions objectAtIndex:anIndex];
|
return [self.parsedExpressions objectAtIndex:anIndex];
|
||||||
|
|||||||
@@ -7,19 +7,74 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPFunctionsViewController</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFunctionsViewController, MPFunctionsCollectionView;
|
@class MPFunctionsViewController, MPFunctionsCollectionView;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPFunctionsViewController
|
||||||
|
@abstract Controls a view from which the user can select function to be
|
||||||
|
inserted into a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpressionView@/link</code>.
|
||||||
|
|
||||||
|
@discussion The view contain a <code>NSCollectionView</code> displaying the
|
||||||
|
prototypes an a <code>NSTextField</code> displaying a description
|
||||||
|
of the currently selected prototype.
|
||||||
|
*/
|
||||||
@interface MPFunctionsViewController : NSViewController
|
@interface MPFunctionsViewController : NSViewController
|
||||||
|
|
||||||
- (id)init;
|
/*!
|
||||||
|
@property collectionView
|
||||||
|
@abstract The <code>NSCollectionView</code> that displays the function
|
||||||
|
prototypes.
|
||||||
|
*/
|
||||||
|
@property (weak) IBOutlet NSCollectionView *collectionView;
|
||||||
|
|
||||||
@property (weak) IBOutlet MPFunctionsCollectionView *collectionView;
|
|
||||||
|
/*!
|
||||||
|
@property functionPrototypes
|
||||||
|
@abstract Contains the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code> objects that get
|
||||||
|
inserted into an expression if selected.
|
||||||
|
|
||||||
|
@discussion Every object in the array must be an <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code> instance.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) NSArray *functionPrototypes;
|
@property (nonatomic, strong) NSArray *functionPrototypes;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property currentDescription
|
||||||
|
@abstract The string that describes the function prototype that is
|
||||||
|
currently selected.
|
||||||
|
|
||||||
|
@discussion If this string is empty a placeholder value will be displayed.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) NSString *currentDescription;
|
@property (nonatomic, strong) NSString *currentDescription;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property target
|
||||||
|
@abstract The target object to receive action messages from the receiver.
|
||||||
|
*/
|
||||||
@property (nonatomic, weak) id target;
|
@property (nonatomic, weak) id target;
|
||||||
@property (nonatomic) SEL action; // 1 argument: The function to insert
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property action
|
||||||
|
@abstract The receiver's action method to the specified selector.
|
||||||
|
|
||||||
|
@discussion The action method is invoked when the user selects one of the
|
||||||
|
function prototypes in the collection view. The prototype that
|
||||||
|
was selected is passed as the one and only argument to
|
||||||
|
the specified selector.
|
||||||
|
*/
|
||||||
|
@property (nonatomic) SEL action;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,18 +8,21 @@
|
|||||||
|
|
||||||
#import "MPFunctionsViewController.h"
|
#import "MPFunctionsViewController.h"
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "NSString+MPExpressionElement.h"
|
|
||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
#import "MPSumFunction.h"
|
#import "MPFractionFunction.h"
|
||||||
#import "MPParenthesisFunction.h"
|
#import "MPParenthesisFunction.h"
|
||||||
#import "MPPowerFunction.h"
|
#import "MPPowerFunction.h"
|
||||||
#import "MPFractionFunction.h"
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
#import "NSString+MPExpressionElement.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPFunctionsCollectionView, MPFunctionTemplateView, MPFunctionTemplateItem;
|
@class MPFunctionsCollectionView, MPFunctionTemplateView, MPFunctionTemplateItem;
|
||||||
|
|
||||||
|
|
||||||
@interface MPFunctionsCollectionView : NSCollectionView
|
@interface MPFunctionsCollectionView : NSCollectionView
|
||||||
|
|
||||||
@property (nonatomic, weak) MPFunctionTemplateItem *hoverItem;
|
@property (nonatomic, weak) MPFunctionTemplateItem *hoverItem;
|
||||||
@@ -30,6 +33,7 @@
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPFunctionTemplateView : NSView
|
@interface MPFunctionTemplateView : NSView
|
||||||
|
|
||||||
@property (nonatomic, strong) MPFunction *functionTemplate;
|
@property (nonatomic, strong) MPFunction *functionTemplate;
|
||||||
@@ -42,16 +46,20 @@
|
|||||||
|
|
||||||
|
|
||||||
@interface MPFunctionTemplateItem : NSCollectionViewItem
|
@interface MPFunctionTemplateItem : NSCollectionViewItem
|
||||||
|
|
||||||
@property (nonatomic, copy) NSString *templateName;
|
@property (nonatomic, copy) NSString *templateName;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFunctionsCollectionView
|
@implementation MPFunctionsCollectionView
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent *)theEvent
|
- (void)mouseDown:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent *)theEvent
|
- (void)mouseUp:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
|
||||||
@@ -87,25 +95,30 @@
|
|||||||
[super updateTrackingAreas];
|
[super updateTrackingAreas];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseEntered:(NSEvent *)theEvent
|
- (void)mouseEntered:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
self.mouseOver = YES;
|
self.mouseOver = YES;
|
||||||
self.needsDisplay = YES;
|
self.needsDisplay = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)mouseExited:(NSEvent *)theEvent
|
- (void)mouseExited:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
self.mouseOver = NO;
|
self.mouseOver = NO;
|
||||||
self.needsDisplay = YES;
|
self.needsDisplay = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setFunctionTemplate:(MPFunction *)functionTemplate
|
- (void)setFunctionTemplate:(MPFunction *)functionTemplate
|
||||||
{
|
{
|
||||||
_functionTemplate = functionTemplate;
|
_functionTemplate = functionTemplate;
|
||||||
_functionTemplateLayout = nil;
|
_functionTemplateLayout = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@synthesize functionTemplateLayout = _functionTemplateLayout;
|
@synthesize functionTemplateLayout = _functionTemplateLayout;
|
||||||
|
|
||||||
- (MPFunctionLayout *)functionTemplateLayout
|
- (MPFunctionLayout *)functionTemplateLayout
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -117,16 +130,19 @@
|
|||||||
return _functionTemplateLayout;
|
return _functionTemplateLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSSize)intrinsicContentSize
|
- (NSSize)intrinsicContentSize
|
||||||
{
|
{
|
||||||
return [self.functionTemplateLayout bounds].size;
|
return [self.functionTemplateLayout bounds].size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)isOpaque
|
- (BOOL)isOpaque
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
if (self.mouseOver) {
|
if (self.mouseOver) {
|
||||||
@@ -150,11 +166,13 @@
|
|||||||
|
|
||||||
static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMouseOverContext";
|
static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMouseOverContext";
|
||||||
|
|
||||||
|
|
||||||
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
|
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
|
||||||
{
|
{
|
||||||
return [super awakeAfterUsingCoder:aDecoder];
|
return [super awakeAfterUsingCoder:aDecoder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
ofObject:(id)object
|
ofObject:(id)object
|
||||||
change:(NSDictionary *)change
|
change:(NSDictionary *)change
|
||||||
@@ -171,6 +189,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setRepresentedObject:(id)representedObject
|
- (void)setRepresentedObject:(id)representedObject
|
||||||
{
|
{
|
||||||
MPFunctionTemplateView *view = (MPFunctionTemplateView *)self.view;
|
MPFunctionTemplateView *view = (MPFunctionTemplateView *)self.view;
|
||||||
@@ -191,6 +210,8 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPFunctionsViewController
|
@implementation MPFunctionsViewController
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -199,6 +220,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
|||||||
bundle:[NSBundle bundleForClass:[self class]]];
|
bundle:[NSBundle bundleForClass:[self class]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
MPFunction *sumFunction = [[MPSumFunction alloc] init];
|
MPFunction *sumFunction = [[MPSumFunction alloc] init];
|
||||||
@@ -227,40 +249,45 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
|
|||||||
forKeyPath:@"hoverItem"
|
forKeyPath:@"hoverItem"
|
||||||
options:0
|
options:0
|
||||||
context:MPCollectionViewHoverItemChangeContext];
|
context:MPCollectionViewHoverItemChangeContext];
|
||||||
self.collectionView.target = self.target;
|
((MPFunctionsCollectionView *)self.collectionView).target = self.target;
|
||||||
self.collectionView.action = self.action;
|
((MPFunctionsCollectionView *)self.collectionView).action = self.action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void *MPCollectionViewHoverItemChangeContext = @"MPCollectionViewHoverItemChangeContext";
|
static void *MPCollectionViewHoverItemChangeContext = @"MPCollectionViewHoverItemChangeContext";
|
||||||
|
|
||||||
|
|
||||||
- (void)setView:(NSView *)view
|
- (void)setView:(NSView *)view
|
||||||
{
|
{
|
||||||
[super setView:view];
|
[super setView:view];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setTarget:(id)target
|
- (void)setTarget:(id)target
|
||||||
{
|
{
|
||||||
_target = target;
|
_target = target;
|
||||||
if (self.collectionView) {
|
if (self.collectionView) {
|
||||||
self.collectionView.target = target;
|
((MPFunctionsCollectionView *)self.collectionView).target = target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setAction:(SEL)action
|
- (void)setAction:(SEL)action
|
||||||
{
|
{
|
||||||
_action = action;
|
_action = action;
|
||||||
if (self.collectionView) {
|
if (self.collectionView) {
|
||||||
self.collectionView.action = action;
|
((MPFunctionsCollectionView *)self.collectionView).action = action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
ofObject:(id)object
|
ofObject:(id)object
|
||||||
change:(NSDictionary *)change
|
change:(NSDictionary *)change
|
||||||
context:(void *)context
|
context:(void *)context
|
||||||
{
|
{
|
||||||
if (context == MPCollectionViewHoverItemChangeContext) {
|
if (context == MPCollectionViewHoverItemChangeContext) {
|
||||||
self.currentDescription = self.collectionView.hoverItem.templateName;
|
self.currentDescription = ((MPFunctionsCollectionView *)self.collectionView).hoverItem.templateName;
|
||||||
} else {
|
} else {
|
||||||
[super observeValueForKeyPath:keyPath
|
[super observeValueForKeyPath:keyPath
|
||||||
ofObject:object
|
ofObject:object
|
||||||
|
|||||||
@@ -6,12 +6,82 @@
|
|||||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPLayout</code> class.
|
||||||
|
|
||||||
|
The <code>MPLayout</code> class renders an expression to be displayed in an
|
||||||
|
<code>@link //apple_ref/occ/cl/MPExpressionView@/link</code>. There are multiple
|
||||||
|
subclasses of <code>MPLayout</code> the most important of which are <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpressionLayout@/link</code> and <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunctionLayout@/link</code> for rendering expressions and
|
||||||
|
functions respectively.
|
||||||
|
|
||||||
|
Both types of layouts can occur in two different sizes: normal and small. The
|
||||||
|
root expression ueses the normal size. Depending on the concrete subclass of
|
||||||
|
<code>@link //apple_ref/occ/cl/MPFunctionLayout@/link</code> that is used a
|
||||||
|
function's children (and any subsequent children) may be rendered using the
|
||||||
|
small size. Also there are two different fonts available for layouts: a normal
|
||||||
|
and a special font. The special font should only be used to emphasize special
|
||||||
|
content that uses the normal font.
|
||||||
|
|
||||||
|
If an expression is empty an empty box is rendered instead. That box (called the
|
||||||
|
<i>empty box</i> uses the font metrics of the empty expression it represents.
|
||||||
|
That means it uses different sizes depending on whether the expression uses a
|
||||||
|
small or normal sized font. Also it is drawn a little smaller than the actual
|
||||||
|
box's height is because it looks nicer. Depending on your needs you can query
|
||||||
|
the metrics of the empty box using the <code>kMPEmptyBox...</code> or
|
||||||
|
<code>kMPEmptyBoxDrawing...</code> macros. These macros must not be used outside
|
||||||
|
of the <code>MPLayout</code> class or any of it subclasses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxWidth
|
||||||
|
@abstract The width of the empty box.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0)
|
#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0)
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxHeight
|
||||||
|
@abstract The actual height of the empty box.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))
|
#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxYOrigin
|
||||||
|
@abstract The actual vertical origin of the empty box.
|
||||||
|
|
||||||
|
@discussion The vertical origin is a negative value to be compliant with the
|
||||||
|
metrics of strings.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)))
|
#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)))
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxDrawingWidth
|
||||||
|
@abstract The with the empty box is rendered with.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth
|
#define kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxDrawingHeight
|
||||||
|
@abstract The height the empty box is drawn with.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font))
|
#define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font))
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@define kMPEmptyBoxDrawingYOrigin
|
||||||
|
@abstract The vertical origin the empty box is drawn at.
|
||||||
|
|
||||||
|
@discussion The vertical origin is a negative value to be compliant with the
|
||||||
|
metrics of strings.
|
||||||
|
*/
|
||||||
#define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2))
|
#define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2))
|
||||||
|
|
||||||
|
|
||||||
@@ -19,58 +89,548 @@
|
|||||||
@class MPLayout, MPRangePath;
|
@class MPLayout, MPRangePath;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPLayout
|
||||||
|
@abstract This is an abstract class that defines the basic methods that
|
||||||
|
must be implemented by other layout classes. It also defines some
|
||||||
|
useful helper methods.
|
||||||
|
|
||||||
|
@discussion The <code>MPLayout</code> class maintains a generic cache. It is
|
||||||
|
used to store child layouts and actual cache information.
|
||||||
|
|
||||||
|
Every layout instance represents either an expression or a
|
||||||
|
function. The instance's children represent the children of the
|
||||||
|
represented object.
|
||||||
|
*/
|
||||||
@interface MPLayout : NSObject
|
@interface MPLayout : NSObject
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
/*!
|
||||||
|
@methodgroup Creation Methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method init
|
||||||
|
@abstract Initializes the receiver.
|
||||||
|
|
||||||
|
@discussion This method should only be used to initialize the expression
|
||||||
|
layout that renders the root layout.
|
||||||
|
|
||||||
|
This method is the designated initializer of the
|
||||||
|
<code>MPLayout</code> class.
|
||||||
|
|
||||||
|
@return A newly initialized <code>MPLayout</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithParent:
|
||||||
|
@abstract Initializes the receiver with the specified parent.
|
||||||
|
|
||||||
|
@discussion This method should generally be used when initializing layout
|
||||||
|
instances. In most cases it should be called at some point in the
|
||||||
|
implementation of the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/childLayoutAtIndex:@/link</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
@param parent
|
||||||
|
The receiver's parent.
|
||||||
|
|
||||||
|
@return A newly initialized <code>MPLayout</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithParent:(MPLayout *)parent;
|
- (instancetype)initWithParent:(MPLayout *)parent;
|
||||||
|
|
||||||
#pragma mark Properties
|
|
||||||
- (NSFont *)normalFontWithSize:(CGFloat)size;
|
|
||||||
- (NSFont *)specialFontWithSize:(CGFloat)size;
|
|
||||||
- (CGFloat)contextInferredFontSize;
|
|
||||||
- (CGFloat)normalFontSize;
|
|
||||||
- (CGFloat)smallFontSize;
|
|
||||||
|
|
||||||
|
#pragma mark Properties
|
||||||
|
/*!
|
||||||
|
@methodgroup Properties
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method font
|
||||||
|
@abstract Returns the receiver's context inferred font.
|
||||||
|
|
||||||
|
@discussion The context inferred font is the normal font using the context
|
||||||
|
inferred font size. Normally this method is used for any textual
|
||||||
|
content of a layout.
|
||||||
|
|
||||||
|
@return The font the receiver should use for rendering textual content.
|
||||||
|
*/
|
||||||
- (NSFont *)font;
|
- (NSFont *)font;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method normalFontWithSize:
|
||||||
|
@abstract Returns a <code>NSFont</code> object that represents the normal
|
||||||
|
font in the specified <code>size</code>.
|
||||||
|
|
||||||
|
@discussion Instead of this method you want to use <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/font@/link</code> instead in most
|
||||||
|
cases.
|
||||||
|
|
||||||
|
@param size
|
||||||
|
The size of the font.
|
||||||
|
|
||||||
|
@return A <code>NSFont</code> object that can be used to render textual
|
||||||
|
content of the receiver in the specified <code>size</code>.
|
||||||
|
*/
|
||||||
|
- (NSFont *)normalFontWithSize:(CGFloat)size;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method specialFontWithSize:
|
||||||
|
@abstract Returns a <code>NSFont</code> object that represents the special
|
||||||
|
font in the specified <code>size</code>.
|
||||||
|
|
||||||
|
@discussion Use fonts returned by this method only if you need to emphasize
|
||||||
|
text that is surrounded by other text that uses the normal font.
|
||||||
|
|
||||||
|
@param size
|
||||||
|
The size of the font.
|
||||||
|
|
||||||
|
@return A <code>NSFont</code> object that can be used to render special
|
||||||
|
textual content of the receiver in the specified
|
||||||
|
<code>size</code>.
|
||||||
|
*/
|
||||||
|
- (NSFont *)specialFontWithSize:(CGFloat)size;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method contextInferredFontSize
|
||||||
|
@abstract Returns the appropriate font size for the receiver based on the
|
||||||
|
context it will be rendered in.
|
||||||
|
*/
|
||||||
|
- (CGFloat)contextInferredFontSize;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method normalFontSize
|
||||||
|
@abstract Returns the font size that is used in the root expression.
|
||||||
|
*/
|
||||||
|
- (CGFloat)normalFontSize;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method smallFontSize
|
||||||
|
@abstract Returns the font size that is used in expressions that are using
|
||||||
|
the small size.
|
||||||
|
*/
|
||||||
|
- (CGFloat)smallFontSize;
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Cache Tree
|
#pragma mark Cache Tree
|
||||||
|
/*!
|
||||||
|
@methodgroup Cache Tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property parent
|
||||||
|
@abstract The receiver's parent layout.
|
||||||
|
|
||||||
|
@discussion The parent layout is most likely the one that created the
|
||||||
|
receiver and is responsible for setting its attributes
|
||||||
|
appropriately based on the rendering context.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, weak) MPLayout *parent;
|
@property (readonly, nonatomic, weak) MPLayout *parent;
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Cache Methods
|
#pragma mark Cache Methods
|
||||||
// Querying Caches
|
/*!
|
||||||
|
@methodgroup Cache Methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method chacableObjectForIndex:generator:
|
||||||
|
@abstract Returns the cached object for the specified <code>index</code> or
|
||||||
|
creates one if it does not exist.
|
||||||
|
|
||||||
|
@discussion This method only returns <code>nil</code> if the
|
||||||
|
<code>generator</code> returns <code>nil</code>.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the cached object to retrieve.
|
||||||
|
|
||||||
|
@param generator
|
||||||
|
This block is executed if there is no cached object at
|
||||||
|
<code>index</code>. The result of this block is then cached at
|
||||||
|
the <code>index</code>.
|
||||||
|
|
||||||
|
@return The cached object for <code>index</code> or the result of the
|
||||||
|
<code>generator</code> if there is no cached object.
|
||||||
|
*/
|
||||||
- (id)cachableObjectForIndex:(NSUInteger)index
|
- (id)cachableObjectForIndex:(NSUInteger)index
|
||||||
generator:(id(^)())generator;
|
generator:(id(^)())generator;
|
||||||
|
|
||||||
// Clearing Caches
|
|
||||||
|
/*!
|
||||||
|
@method clearCacheInRange:replacementLength:
|
||||||
|
@abstract Removes the objects in the specified from the cache and moves all
|
||||||
|
objects at subsequent indexes.
|
||||||
|
|
||||||
|
@discussion The objects at subsequent indexes are moved
|
||||||
|
<code>replacementLength - range.length</code> places. If
|
||||||
|
<code>replacementLength</code> is greater than
|
||||||
|
<code>range.length</code> they are moved to smaller indexes.
|
||||||
|
|
||||||
|
@param range
|
||||||
|
The range of objects to be removed.
|
||||||
|
|
||||||
|
@param replacementLength
|
||||||
|
The number of indexes replacing the ones specified by
|
||||||
|
<code>range</code>.
|
||||||
|
*/
|
||||||
- (void)clearCacheInRange:(NSRange)range
|
- (void)clearCacheInRange:(NSRange)range
|
||||||
replacementLength:(NSUInteger)replacementLength;
|
replacementLength:(NSUInteger)replacementLength;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method invalidate
|
||||||
|
@abstract Invalidates the receiver.
|
||||||
|
|
||||||
|
@discussion Invalidating the receiver causes him to discard all cached
|
||||||
|
information about itself and send a <code>invalidate</code>
|
||||||
|
message to its parent. The cached information about the
|
||||||
|
receiver's children is <b>not</b> discarded. Use <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/clearCacheInRange:replacementLength:@/link</code>
|
||||||
|
for that purpose.
|
||||||
|
*/
|
||||||
- (void)invalidate;
|
- (void)invalidate;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method childLayoutAtIndexPath:
|
||||||
|
@abstract Returns the child layout at the specified index path.
|
||||||
|
|
||||||
|
@discussion If the <code>indexPath</code> is empty the receiver is returned.
|
||||||
|
If the child layout the index path specifies is not yet created
|
||||||
|
this method should do so and add it to the cache of the
|
||||||
|
respective parent layout.
|
||||||
|
|
||||||
|
@param indexPath
|
||||||
|
The index path of the child.
|
||||||
|
|
||||||
|
@return The child at the specified index path.
|
||||||
|
*/
|
||||||
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath;
|
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
#pragma mark Calculation and Drawing Methods
|
|
||||||
|
#pragma mark Rendering Methods
|
||||||
|
/*!
|
||||||
|
@methodgroup Rendering Methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method createLineForString:
|
||||||
|
@abstract Renders the specified string.
|
||||||
|
|
||||||
|
@discussion This method uses the normal font and the context inferred font
|
||||||
|
size to render the string. In most cases this is the appropriate
|
||||||
|
method for rendering textual content in a layout.
|
||||||
|
|
||||||
|
@param aString
|
||||||
|
The string to be rendered.
|
||||||
|
|
||||||
|
@return A <code>CTLineRef</code> representing the rendered string.
|
||||||
|
*/
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString;
|
- (CTLineRef)createLineForString:(NSString *)aString;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method createLineForString:emphasize:
|
||||||
|
@abstract Renders the specified string.
|
||||||
|
|
||||||
|
@discussion This method uses either the normal or the special font depending
|
||||||
|
on the <code>emphasize</code> flag. It uses the context inferred
|
||||||
|
font size.
|
||||||
|
|
||||||
|
@param aString
|
||||||
|
The string to be rendered.
|
||||||
|
|
||||||
|
@param emphasize
|
||||||
|
A flag indicating whether the special font should be used.
|
||||||
|
|
||||||
|
@return A <code>CTLineRef</code> representing the rendered string.
|
||||||
|
*/
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize;
|
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method createLineForString:usingFont:
|
||||||
|
@abstract Renders the specified string in the specified font.
|
||||||
|
|
||||||
|
@discussion In most cases you do should prefer <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/createLineForString:@/link</code>
|
||||||
|
or <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/createLineForString:emphasize:@/link</code>
|
||||||
|
over this method.
|
||||||
|
|
||||||
|
@param aString
|
||||||
|
The string to be rendered.
|
||||||
|
|
||||||
|
@param font
|
||||||
|
The font to render <code>aString</code> in.
|
||||||
|
|
||||||
|
@return A <code>CTLineRef</code> representing the rendered string.
|
||||||
|
*/
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font;
|
- (CTLineRef)createLineForString:(NSString *)aString usingFont:(NSFont *)font;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property flipped
|
||||||
|
@abstract This property indicates whether the receiver uses a flipped
|
||||||
|
layout.
|
||||||
|
|
||||||
|
@discussion The value of this property should be the same in the receiver,
|
||||||
|
its parent and all of its children.
|
||||||
|
*/
|
||||||
@property (nonatomic, getter = isFlipped) BOOL flipped;
|
@property (nonatomic, getter = isFlipped) BOOL flipped;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property usesSmallSize
|
||||||
|
@abstract This property indicates whether the receiver uses the small font
|
||||||
|
size.
|
||||||
|
|
||||||
|
@discussion This property is used to determine the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/contextInferredFontSize@/link</code>.
|
||||||
|
If this value is <code>YES</code> all of the receiver's children
|
||||||
|
should also use the small size. If not the children may or may
|
||||||
|
not (depending on the actual layout implementation) use the small
|
||||||
|
size.
|
||||||
|
*/
|
||||||
@property (nonatomic) BOOL usesSmallSize;
|
@property (nonatomic) BOOL usesSmallSize;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method bounds
|
||||||
|
@abstract Returns the receiver's bounds.
|
||||||
|
|
||||||
|
@discussion If the receiver has cached bounds it will return the cached
|
||||||
|
value. The cached value can be removed using the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout/invalidate@/link</code> method. If
|
||||||
|
there is no cached value this method will generate a new cached
|
||||||
|
value by calling the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/generateBounds@/link</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
@return The receiver's bounds.
|
||||||
|
*/
|
||||||
- (NSRect)bounds;
|
- (NSRect)bounds;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method boundingRectForRangePath:
|
||||||
|
@abstract Returns the rect relative to the origin of the receiver's bounds
|
||||||
|
that contains the symbols identified by the specified range path.
|
||||||
|
|
||||||
|
@discussion Use this method to determine where the user's selection should be
|
||||||
|
drawn at.
|
||||||
|
|
||||||
|
@param rangePath
|
||||||
|
The range path whose bounds are needed.
|
||||||
|
|
||||||
|
@return The rectangle containing the symbols identified by
|
||||||
|
<code>rangePath</code>.
|
||||||
|
*/
|
||||||
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */
|
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath; /* if rangePath.length is 0 the returned rect will have a width of 0 */
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method drawAtPoint:
|
||||||
|
@abstract Draws the receiver at the specified point.
|
||||||
|
|
||||||
|
@discussion If the receiver returns <code>NO</code> from its <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||||
|
method this method also draws every single child of the receiver.
|
||||||
|
The location the children are drawn at is determined by the
|
||||||
|
<code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/offsetOfChildLayoutAtIndex:@/link</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
@param point
|
||||||
|
The point the receiver is to be drawn at.
|
||||||
|
*/
|
||||||
- (void)drawAtPoint:(NSPoint)point;
|
- (void)drawAtPoint:(NSPoint)point;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@category MPLayout (MPSubclassImplement)
|
||||||
|
@abstract The methods in this category must be implemented by any concrete
|
||||||
|
subclass of <code>MPLayout</code>.
|
||||||
|
*/
|
||||||
@interface MPLayout (MPSubclassImplement)
|
@interface MPLayout (MPSubclassImplement)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method numberOfChildren
|
||||||
|
@abstract Returns the number of sublayouts of the receiver.
|
||||||
|
|
||||||
|
@discussion This method must be implemented by subclasses.
|
||||||
|
*/
|
||||||
- (NSUInteger)numberOfChildren;
|
- (NSUInteger)numberOfChildren;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method drawsChildrenManually
|
||||||
|
@abstract Returns whether the receiver wants to take over responsibility of
|
||||||
|
drawing its children.
|
||||||
|
|
||||||
|
@discussion If you return <code>YES</code> from this method
|
||||||
|
<code>MPLayout</code> does not draw the receiver's children. The
|
||||||
|
receiver's <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
||||||
|
method has to implement that functionality. If you return
|
||||||
|
<code>NO</code> from this method the receiver must not draw its
|
||||||
|
children in the <code>//apple_ref/occ/intfm/MPLayout/draw</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
This method may be implemented by subclasses to opt in and change
|
||||||
|
the default behaviour.
|
||||||
|
|
||||||
|
@return <code>YES</code> if the receiver draws its children
|
||||||
|
automatically, <code>NO</code> otherwise. The default
|
||||||
|
implementation returns <code>NO</code>.
|
||||||
|
*/
|
||||||
- (BOOL)drawsChildrenManually;
|
- (BOOL)drawsChildrenManually;
|
||||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
|
|
||||||
- (NSRect)generateBounds; // To be implemented
|
|
||||||
- (NSRect)boundingRectForRange:(NSRange)range; // To be implemented, use rangePath instead, this one has wrong origin
|
/*!
|
||||||
|
@method childLayoutAtIndex:
|
||||||
|
@abstract Returns the receiver's child at the specified index.
|
||||||
|
|
||||||
|
@discussion As long as the specified <code>index</code> does not exceed the
|
||||||
|
receiver's number of children, this method should always return a
|
||||||
|
value based on the following criteria:
|
||||||
|
1. If the receiver represents a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPFunction@/link</code> instance it should
|
||||||
|
always return a valid <code>MPLayout</code> instance.
|
||||||
|
2. If the receiver represents a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPExpression@/link</code> instance it
|
||||||
|
returns only a valid <code>MPLayout</code> instance if the
|
||||||
|
represented expression's element at <code>index</code> is a
|
||||||
|
function.
|
||||||
|
|
||||||
|
This method should cache any generated child layout and use the
|
||||||
|
cache whenever possible in order to reduce resources needed for
|
||||||
|
rendering.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the requested child layout.
|
||||||
|
|
||||||
|
@return A <code>MPLayout</code> instance representing the respective
|
||||||
|
child of the object that is represented by the receiver, or
|
||||||
|
<code>nil</code> if the child represents a string element in an
|
||||||
|
expression.
|
||||||
|
*/
|
||||||
|
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method generateBounds
|
||||||
|
@abstract Calculates the receiever's bounds.
|
||||||
|
|
||||||
|
@discussion This method should not use any cached values. Caching of the
|
||||||
|
calculated bounds is done automatically by the
|
||||||
|
<code>MPLayout</code> class. This method is only called if the
|
||||||
|
receiving <code>MPLayout</code> instance has been invalidated and
|
||||||
|
the receiver's bounds have been requested.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses.
|
||||||
|
|
||||||
|
@return The receiver's bounds.
|
||||||
|
*/
|
||||||
|
- (NSRect)generateBounds;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method boundingRectForRange:
|
||||||
|
@abstract Returns the rectangle that encloses the symbols in the specified
|
||||||
|
range.
|
||||||
|
|
||||||
|
@discussion This method is called to calculate the rectangle that encloses
|
||||||
|
the user's selection. If the specified <code>range</code> has a
|
||||||
|
length of <code>0</code> the returned rectangle should be the
|
||||||
|
bounds of the caret (which has a width of <code>0</code>). The
|
||||||
|
height and y-origin of the returned rectangle should be equal to
|
||||||
|
the respective values of the receiver.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses.
|
||||||
|
|
||||||
|
@param range
|
||||||
|
The range of the selection whose bounds should be calculated.
|
||||||
|
|
||||||
|
@return A rectangle enclosing the user's selection.
|
||||||
|
*/
|
||||||
|
- (NSRect)boundingRectForRange:(NSRange)range;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method offsetOfChildLayoutAtIndex:
|
||||||
|
@abstract Returns the location of the child layout at the specified index.
|
||||||
|
|
||||||
|
@discussion This method must be implemented by subclasses even if the
|
||||||
|
subclass returns <code>YES</code> from the <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||||
|
method.
|
||||||
|
|
||||||
|
@param index
|
||||||
|
The index of the child whose location is to be calculated.
|
||||||
|
|
||||||
|
@return The location of the child layout at the specified index relative
|
||||||
|
to the receiver.
|
||||||
|
*/
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method indexPathForMousePoint:
|
||||||
|
@abstract Performs hit testing.
|
||||||
|
|
||||||
|
@discussion This method tests the specified <code>point</code> against all of
|
||||||
|
its children and returns the index path (relative to the index
|
||||||
|
path of the receiver) that identifies the location inside the
|
||||||
|
expression tree the user selected. The specified point must be
|
||||||
|
inside of the receiver and must be specified relatively to the
|
||||||
|
receiver. If the specified point is not inside any of the
|
||||||
|
receiver's children but in the receiver itself the returned index
|
||||||
|
path must contain one index specifiying the location in the
|
||||||
|
receiver.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses.
|
||||||
|
|
||||||
|
@param point
|
||||||
|
The point inside the receiver that is to be tested.
|
||||||
|
|
||||||
|
@return The index path that points to the point the user selected. All
|
||||||
|
indexes in the path except for the last one are specified in the
|
||||||
|
element reference frame. The last index is specified in the
|
||||||
|
symbol reference frame.
|
||||||
|
*/
|
||||||
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
|
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point;
|
||||||
- (void)draw; // To be implemented
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method draw
|
||||||
|
@abstract Draws the receiver.
|
||||||
|
|
||||||
|
@discussion If the receiver returns <code>YES</code> from its <code>@link
|
||||||
|
//apple_ref/occ/instm/MPLayout(MPSubclassImplement)/drawsChildrenManually@/link</code>
|
||||||
|
method the implementation of this method must also draw the
|
||||||
|
receiver's children.
|
||||||
|
|
||||||
|
This method must be implemented by subclasses. If the receiver
|
||||||
|
does not have anything to draw except for its children you should
|
||||||
|
implement this method with an empty method body.
|
||||||
|
*/
|
||||||
|
- (void)draw;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -11,20 +11,25 @@
|
|||||||
#import "MPRangePath.h"
|
#import "MPRangePath.h"
|
||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPLayout ()
|
@interface MPLayout ()
|
||||||
|
|
||||||
// Querying and Storing Caches
|
|
||||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
|
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
|
||||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
|
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPLayout {
|
@implementation MPLayout {
|
||||||
NSMutableArray *_cache;
|
NSMutableArray *_cache;
|
||||||
NSRect _cachedBounds;
|
NSRect _cachedBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
@@ -35,6 +40,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (instancetype)initWithParent:(MPLayout *)parent
|
- (instancetype)initWithParent:(MPLayout *)parent
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
@@ -44,41 +50,51 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Properties
|
#pragma mark Properties
|
||||||
|
|
||||||
|
|
||||||
- (NSFont *)normalFontWithSize:(CGFloat)size
|
- (NSFont *)normalFontWithSize:(CGFloat)size
|
||||||
{
|
{
|
||||||
return [NSFont fontWithName:@"CMU Serif"
|
return [NSFont fontWithName:@"CMU Serif"
|
||||||
size:size];
|
size:size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSFont *)specialFontWithSize:(CGFloat)size
|
- (NSFont *)specialFontWithSize:(CGFloat)size
|
||||||
{
|
{
|
||||||
return [NSFont fontWithName:@"CMU Serif Italic"
|
return [NSFont fontWithName:@"CMU Serif Italic"
|
||||||
size:size];
|
size:size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CGFloat)contextInferredFontSize
|
- (CGFloat)contextInferredFontSize
|
||||||
{
|
{
|
||||||
return self.usesSmallSize ? self.smallFontSize : self.normalFontSize;
|
return self.usesSmallSize ? self.smallFontSize : self.normalFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CGFloat)normalFontSize
|
- (CGFloat)normalFontSize
|
||||||
{
|
{
|
||||||
return 18.0;
|
return 18.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CGFloat)smallFontSize
|
- (CGFloat)smallFontSize
|
||||||
{
|
{
|
||||||
return 12.0;
|
return 12.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSFont *)font
|
- (NSFont *)font
|
||||||
{
|
{
|
||||||
return [self normalFontWithSize:self.contextInferredFontSize];
|
return [self normalFontWithSize:self.contextInferredFontSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Cache Tree
|
#pragma mark Cache Tree
|
||||||
// Querying and Storing Caches
|
|
||||||
|
|
||||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
|
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
if (index >= _cache.count) {
|
if (index >= _cache.count) {
|
||||||
@@ -87,6 +103,7 @@
|
|||||||
return _cache[index] != [NSNull null];
|
return _cache[index] != [NSNull null];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (id)cachableObjectForIndex:(NSUInteger)index
|
- (id)cachableObjectForIndex:(NSUInteger)index
|
||||||
generator:(id (^)())generator
|
generator:(id (^)())generator
|
||||||
{
|
{
|
||||||
@@ -99,6 +116,7 @@
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index
|
- (void)ensureCacheSizeForIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
while (index >= _cache.count) {
|
while (index >= _cache.count) {
|
||||||
@@ -106,7 +124,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clearing Caches
|
|
||||||
- (void)clearCacheInRange:(NSRange)range
|
- (void)clearCacheInRange:(NSRange)range
|
||||||
replacementLength:(NSUInteger)replacementLength
|
replacementLength:(NSUInteger)replacementLength
|
||||||
{
|
{
|
||||||
@@ -119,12 +137,14 @@
|
|||||||
[self invalidate];
|
[self invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
_cachedBounds = NSZeroRect;
|
_cachedBounds = NSZeroRect;
|
||||||
[self.parent invalidate];
|
[self.parent invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath
|
- (MPLayout *)childLayoutAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
if (indexPath.length == 0) {
|
if (indexPath.length == 0) {
|
||||||
@@ -134,19 +154,24 @@
|
|||||||
return [child childLayoutAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
return [child childLayoutAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Calculation and Drawing Methods
|
#pragma mark Calculation and Drawing Methods
|
||||||
|
|
||||||
|
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString
|
- (CTLineRef)createLineForString:(NSString *)aString
|
||||||
{
|
{
|
||||||
return [self createLineForString:aString
|
return [self createLineForString:aString
|
||||||
usingFont:self.font];
|
usingFont:self.font];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize
|
- (CTLineRef)createLineForString:(NSString *)aString emphasize:(BOOL)emphasize
|
||||||
{
|
{
|
||||||
return [self createLineForString:aString
|
return [self createLineForString:aString
|
||||||
usingFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
|
usingFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CTLineRef)createLineForString:(NSString *)aString
|
- (CTLineRef)createLineForString:(NSString *)aString
|
||||||
usingFont:(NSFont *)font
|
usingFont:(NSFont *)font
|
||||||
{
|
{
|
||||||
@@ -158,6 +183,7 @@
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)bounds
|
- (NSRect)bounds
|
||||||
{
|
{
|
||||||
if (NSEqualRects(_cachedBounds, NSZeroRect)) {
|
if (NSEqualRects(_cachedBounds, NSZeroRect)) {
|
||||||
@@ -166,6 +192,7 @@
|
|||||||
return _cachedBounds;
|
return _cachedBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath
|
- (NSRect)boundingRectForRangePath:(MPRangePath *)rangePath
|
||||||
{
|
{
|
||||||
if (rangePath.location.length == 1) {
|
if (rangePath.location.length == 1) {
|
||||||
@@ -180,11 +207,13 @@
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)drawsChildrenManually
|
- (BOOL)drawsChildrenManually
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)drawAtPoint:(NSPoint)point
|
- (void)drawAtPoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
NSAffineTransform *transform = [NSAffineTransform transform];
|
||||||
|
|||||||
@@ -7,25 +7,49 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPMathRules</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey;
|
|
||||||
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey;
|
/*!
|
||||||
|
@const MPMathRulesIsUsingDegreesKey
|
||||||
|
@abstract Predefined key that is used to store the properties of the
|
||||||
|
<code>MPMathRules</code> class in the <code>NSUserDefaults</code>
|
||||||
|
database.
|
||||||
|
*/
|
||||||
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
|
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPMathRules;
|
@class MPMathRules;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPMathRules
|
||||||
|
@abstract The math rules class stores global settings concerning
|
||||||
|
mathematical interpretations
|
||||||
|
|
||||||
|
@discussion The <code>MPMathRules</code> class is a singletone class. There
|
||||||
|
can only exist one instance (the shared instance) at any time.
|
||||||
|
*/
|
||||||
@interface MPMathRules : NSObject
|
@interface MPMathRules : NSObject
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method sharedRules
|
||||||
|
@abstract Returnes the shared <code>MPMathRules</code> instance.
|
||||||
|
*/
|
||||||
+ (MPMathRules *)sharedRules;
|
+ (MPMathRules *)sharedRules;
|
||||||
|
|
||||||
@property (nonatomic, getter = isUsingUserDefaultValues) BOOL usingUserDefaultValues;
|
|
||||||
|
|
||||||
// Default value uses the key "..." in NSUserDefaults or ... if the key does not exist.
|
/*!
|
||||||
@property (nonatomic) NSUInteger maximumOperatorChainLength; // +--++-5 -> Chain length: 6, 2 default (sign of number and operator) (0 is invalid?)
|
@property isUsingDegrees
|
||||||
@property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0
|
@abstract Specifies whether trigonometric functions use degree values.
|
||||||
|
|
||||||
|
@discussion If set to <code>NO</code> radians are used instead. The default
|
||||||
|
value is <code>NO</code>.
|
||||||
|
*/
|
||||||
@property (nonatomic) BOOL isUsingDegrees;
|
@property (nonatomic) BOOL isUsingDegrees;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,14 +8,16 @@
|
|||||||
|
|
||||||
#import "MPMathRules.h"
|
#import "MPMathRules.h"
|
||||||
|
|
||||||
NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey";
|
|
||||||
NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey";
|
|
||||||
NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey";
|
NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPMathRules
|
@implementation MPMathRules
|
||||||
|
|
||||||
static MPMathRules *sharedRules;
|
static MPMathRules *sharedRules;
|
||||||
|
|
||||||
|
|
||||||
+ (MPMathRules *)sharedRules
|
+ (MPMathRules *)sharedRules
|
||||||
{
|
{
|
||||||
if (!sharedRules) {
|
if (!sharedRules) {
|
||||||
@@ -24,6 +26,7 @@ static MPMathRules *sharedRules;
|
|||||||
return sharedRules;
|
return sharedRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if (sharedRules) {
|
if (sharedRules) {
|
||||||
@@ -31,53 +34,21 @@ static MPMathRules *sharedRules;
|
|||||||
}
|
}
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_usingUserDefaultValues = YES;
|
|
||||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSNumber *userDefaultsMaximumOperatorChainLength = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthKey];
|
|
||||||
NSNumber *userDefaultsMaximumOperatorChainLengthInMultiplication = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
|
|
||||||
NSNumber *userDefaultsIsUsingDegrees = [userDefaults objectForKey:MPMathRulesIsUsingDegreesKey];
|
|
||||||
|
|
||||||
_maximumOperatorChainLength = userDefaultsMaximumOperatorChainLength != nil ? userDefaultsMaximumOperatorChainLength.unsignedIntegerValue : 2;
|
|
||||||
_maximumOperatorChainLengthInMultiplication = userDefaultsMaximumOperatorChainLengthInMultiplication != nil ? userDefaultsMaximumOperatorChainLengthInMultiplication.unsignedIntegerValue : 1;
|
|
||||||
_isUsingDegrees = userDefaultsIsUsingDegrees.boolValue;
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setUsingUserDefaultValues:(BOOL)usingUserDefaultValues
|
|
||||||
|
- (BOOL)isUsingDegrees
|
||||||
{
|
{
|
||||||
_usingUserDefaultValues = usingUserDefaultValues;
|
return [[NSUserDefaults standardUserDefaults] boolForKey:MPMathRulesIsUsingDegreesKey];
|
||||||
// Save the current values
|
|
||||||
self.maximumOperatorChainLength = self.maximumOperatorChainLength;
|
|
||||||
self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication;
|
|
||||||
self.isUsingDegrees = self.isUsingDegrees;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setMaximumOperatorChainLength:(NSUInteger)maximumOperatorChainLength
|
|
||||||
{
|
|
||||||
_maximumOperatorChainLength = maximumOperatorChainLength;
|
|
||||||
if (self.isUsingUserDefaultValues) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLength)
|
|
||||||
forKey:MPMathRulesMaximumOperatorChainLengthKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMaximumOperatorChainLengthInMultiplication:(NSUInteger)maximumOperatorChainLengthInMultiplication
|
|
||||||
{
|
|
||||||
_maximumOperatorChainLengthInMultiplication = maximumOperatorChainLengthInMultiplication;
|
|
||||||
if (self.isUsingUserDefaultValues) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLengthInMultiplication)
|
|
||||||
forKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setIsUsingDegrees:(BOOL)isUsingDegrees
|
- (void)setIsUsingDegrees:(BOOL)isUsingDegrees
|
||||||
{
|
{
|
||||||
_isUsingDegrees = isUsingDegrees;
|
|
||||||
if (self.isUsingUserDefaultValues) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
|
[[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
|
||||||
forKey:MPMathRulesIsUsingDegreesKey];
|
forKey:MPMathRulesIsUsingDegreesKey];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPNegatedTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPNegatedTerm;
|
@class MPNegatedTerm;
|
||||||
|
|
||||||
@@ -20,7 +26,6 @@
|
|||||||
*/
|
*/
|
||||||
@interface MPNegatedTerm : MPTerm
|
@interface MPNegatedTerm : MPTerm
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method initWithTerm:
|
@method initWithTerm:
|
||||||
@abstract Initializes a new negated term.
|
@abstract Initializes a new negated term.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#import "MPNegatedTerm.h"
|
#import "MPNegatedTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPNegatedTerm
|
@implementation MPNegatedTerm
|
||||||
|
|
||||||
- (instancetype)initWithTerm:(MPTerm *)term
|
- (instancetype)initWithTerm:(MPTerm *)term
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSDecimalNumber *value = [self.term evaluate:error];
|
NSDecimalNumber *value = [self.term evaluate:error];
|
||||||
|
|||||||
@@ -9,14 +9,44 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPNumber</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPNumber;
|
@class MPNumber;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPNumber
|
||||||
|
@abstract This class represents a number that evaluates to itself.
|
||||||
|
|
||||||
|
@discussion Numbers include integers as well as floating point numbers. They
|
||||||
|
have to be representable as a decimal number literal (e.g.
|
||||||
|
<code>3.4</code>). Numbers that have periods or are irrational
|
||||||
|
are not implemented by this class.
|
||||||
|
*/
|
||||||
@interface MPNumber : MPTerm
|
@interface MPNumber : MPTerm
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithNumber:
|
||||||
|
@abstract Initializes a number term with the specified <code>number</code>.
|
||||||
|
|
||||||
|
@param number
|
||||||
|
The number that the term should evaluate to. Must not be
|
||||||
|
<code>nil</code>.
|
||||||
|
|
||||||
|
@return A new <code>MPNumberTerm</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */
|
- (instancetype)initWithNumber:(NSDecimalNumber *)number; /* designated initializer */
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property number
|
||||||
|
@abstract The receiver's number.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, strong) NSDecimalNumber *number;
|
@property (readonly, nonatomic, strong) NSDecimalNumber *number;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
#import "MPNumber.h"
|
#import "MPNumber.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
@implementation MPNumber
|
@implementation MPNumber
|
||||||
|
|
||||||
@@ -24,6 +22,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return self.number;
|
return self.number;
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPParenthesisFunction</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPParenthesisFunction, MPExpression;
|
@class MPParenthesisFunction, MPExpression;
|
||||||
|
|
||||||
|
|||||||
@@ -18,16 +18,19 @@
|
|||||||
|
|
||||||
MPFunctionAccessorImplementation(Expression, _expression)
|
MPFunctionAccessorImplementation(Expression, _expression)
|
||||||
|
|
||||||
|
|
||||||
- (NSArray *)childrenAccessors
|
- (NSArray *)childrenAccessors
|
||||||
{
|
{
|
||||||
return @[@"expression"];
|
return @[@"expression"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (Class)functionTermClass
|
- (Class)functionTermClass
|
||||||
{
|
{
|
||||||
return [MPParenthesisTerm class];
|
return [MPParenthesisTerm class];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat:@"(%@)", self.expression.description];
|
return [NSString stringWithFormat:@"(%@)", self.expression.description];
|
||||||
|
|||||||
@@ -9,12 +9,32 @@
|
|||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPParenthesisFunctionLayout</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPParenthesisFunctionLayout, MPParenthesisFunction;
|
@class MPParenthesisFunctionLayout, MPParenthesisFunction;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPParenthesisFunctionLayout
|
||||||
|
@abstract A parenthesis function layout displays a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion The child of the parenthesis function is encapsulated by standard
|
||||||
|
parenthesis that are scaled to the smae height as the child.
|
||||||
|
*/
|
||||||
@interface MPParenthesisFunctionLayout : MPFunctionLayout
|
@interface MPParenthesisFunctionLayout : MPFunctionLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method parenthesisFunction
|
||||||
|
@abstract Returns the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPParenthesisFunction@/link</code> represented
|
||||||
|
by the receiver.
|
||||||
|
*/
|
||||||
- (MPParenthesisFunction *)parenthesisFunction;
|
- (MPParenthesisFunction *)parenthesisFunction;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
|
|
||||||
#import "MPParenthesisFunction.h"
|
#import "MPParenthesisFunction.h"
|
||||||
|
|
||||||
|
|
||||||
#define MPParenthesisFunctionOpeningParensOffset 2
|
#define MPParenthesisFunctionOpeningParensOffset 2
|
||||||
#define MPParenthesisFunctionClosingParensOffset 0
|
#define MPParenthesisFunctionClosingParensOffset 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPParenthesisFunctionLayout ()
|
@interface MPParenthesisFunctionLayout ()
|
||||||
|
|
||||||
- (NSBezierPath *)openingParens;
|
- (NSBezierPath *)openingParens;
|
||||||
@@ -25,6 +28,8 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPParenthesisFunctionLayout
|
@implementation MPParenthesisFunctionLayout
|
||||||
|
|
||||||
- (MPParenthesisFunction *)parenthesisFunction
|
- (MPParenthesisFunction *)parenthesisFunction
|
||||||
@@ -32,6 +37,7 @@
|
|||||||
return (MPParenthesisFunction *)self.function;
|
return (MPParenthesisFunction *)self.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSBezierPath *)openingParens
|
- (NSBezierPath *)openingParens
|
||||||
{
|
{
|
||||||
NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{
|
NSBezierPath *parens = [self objectForPrivateCacheIndex:0 generator:^id{
|
||||||
@@ -60,6 +66,7 @@
|
|||||||
return parens;
|
return parens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSBezierPath *)closingParens
|
- (NSBezierPath *)closingParens
|
||||||
{
|
{
|
||||||
NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{
|
NSBezierPath *parens = [self objectForPrivateCacheIndex:1 generator:^id{
|
||||||
@@ -88,6 +95,7 @@
|
|||||||
return parens;
|
return parens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSBezierPath *)transformedOpeningParens
|
- (NSBezierPath *)transformedOpeningParens
|
||||||
{
|
{
|
||||||
NSBezierPath *parens = self.openingParens.copy;
|
NSBezierPath *parens = self.openingParens.copy;
|
||||||
@@ -95,6 +103,7 @@
|
|||||||
return parens;
|
return parens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSBezierPath *)transformedClosingParens
|
- (NSBezierPath *)transformedClosingParens
|
||||||
{
|
{
|
||||||
NSBezierPath *parens = self.closingParens.copy;
|
NSBezierPath *parens = self.closingParens.copy;
|
||||||
@@ -102,6 +111,7 @@
|
|||||||
return parens;
|
return parens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSAffineTransform *)parenthesisTransform
|
- (NSAffineTransform *)parenthesisTransform
|
||||||
{
|
{
|
||||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
NSAffineTransform *transform = [NSAffineTransform transform];
|
||||||
@@ -110,6 +120,7 @@
|
|||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CGFloat)scaleFactor
|
- (CGFloat)scaleFactor
|
||||||
{
|
{
|
||||||
NSRect parensBounds = self.openingParens.bounds;
|
NSRect parensBounds = self.openingParens.bounds;
|
||||||
@@ -122,22 +133,25 @@
|
|||||||
return expressionHeight / parensHeight;
|
return expressionHeight / parensHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0);
|
return NSMakePoint(self.openingParens.bounds.size.width + MPParenthesisFunctionOpeningParensOffset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
#warning Missing Implementation
|
|
||||||
return [NSIndexPath indexPathWithIndex:0];
|
return [NSIndexPath indexPathWithIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren
|
- (NSIndexSet *)indexesOfRemainingChildren
|
||||||
{
|
{
|
||||||
return [NSIndexSet indexSetWithIndex:0];
|
return [NSIndexSet indexSetWithIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)generateBounds
|
- (NSRect)generateBounds
|
||||||
{
|
{
|
||||||
NSRect openingParensBounds = self.transformedOpeningParens.bounds;
|
NSRect openingParensBounds = self.transformedOpeningParens.bounds;
|
||||||
@@ -159,6 +173,7 @@
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)draw
|
- (void)draw
|
||||||
{
|
{
|
||||||
NSBezierPath *openingParens = self.transformedOpeningParens;
|
NSBezierPath *openingParens = self.transformedOpeningParens;
|
||||||
|
|||||||
@@ -9,10 +9,24 @@
|
|||||||
#import "MPFunctionTerm.h"
|
#import "MPFunctionTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPParenthesisTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPParenthesisTerm;
|
@class MPParenthesisTerm;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPParenthesisTerm
|
||||||
|
@abstract Represents a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPParenthesisFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A parenthesis function encapsulates a term and thus prioritizes
|
||||||
|
it in evaluation.
|
||||||
|
*/
|
||||||
@interface MPParenthesisTerm : MPFunctionTerm
|
@interface MPParenthesisTerm : MPFunctionTerm
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPParenthesisTerm
|
@implementation MPParenthesisTerm
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (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;
|
FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@class MPParsedExpression, MPTerm;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@class MPParsedExpression
|
@class MPParsedExpression
|
||||||
@abstract A parsed expression represents an expression whose syntax is
|
@abstract A parsed expression represents an expression whose syntax is
|
||||||
@@ -93,7 +99,7 @@ FOUNDATION_EXPORT NSString *const MPErrorRangeKey;
|
|||||||
|
|
||||||
@discussion This method is a convenience method for evaluating the receiver's
|
@discussion This method is a convenience method for evaluating the receiver's
|
||||||
<code>@link
|
<code>@link
|
||||||
//apple_ref/occ/intfp/MPParsedExpression/term@/link</code>.
|
//apple_ref/occ/instp/MPParsedExpression/term@/link</code>.
|
||||||
|
|
||||||
@param error
|
@param error
|
||||||
If an error occured during evaluation it will be returned
|
If an error occured during evaluation it will be returned
|
||||||
|
|||||||
@@ -9,13 +9,15 @@
|
|||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
#import "MPToken.h"
|
#import "MPToken.h"
|
||||||
|
|
||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
|
NSString *const MPMathKitErrorDomain = @"MPMathKitErrorDomain";
|
||||||
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
|
NSString *const MPPathToExpressionKey = @"MPPathToExpressionKey";
|
||||||
NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
|
NSString *const MPErrorRangeKey = @"MPErrorRangeKey";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPParsedExpression
|
@implementation MPParsedExpression
|
||||||
|
|
||||||
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPPowerFunction</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPPowerFunction, MPExpression;
|
@class MPPowerFunction, MPExpression;
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,19 @@
|
|||||||
|
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPPowerFunction
|
@implementation MPPowerFunction
|
||||||
|
|
||||||
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
|
MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
|
||||||
|
|
||||||
|
|
||||||
- (NSArray *)childrenAccessors
|
- (NSArray *)childrenAccessors
|
||||||
{
|
{
|
||||||
return @[@"exponentExpression"];
|
return @[@"exponentExpression"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSString *)description
|
- (NSString *)description
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat:@"^%@", self.exponentExpression.description];
|
return [NSString stringWithFormat:@"^%@", self.exponentExpression.description];
|
||||||
|
|||||||
@@ -9,14 +9,46 @@
|
|||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPPowerFunctionLayout</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPPowerFunctionLayout, MPPowerFunction;
|
@class MPPowerFunctionLayout, MPPowerFunction;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPPowerFunctionLayout
|
||||||
|
@abstract A power function layout displays a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A power function layout draws its child at the top right of the
|
||||||
|
base. Because of this the power function layout has a special
|
||||||
|
property that gets set during the drawing of an expression:
|
||||||
|
<code>@link
|
||||||
|
//apple_ref/occ/instp/MPPowerFunctionLayout/baseBounds@/link</code>.
|
||||||
|
*/
|
||||||
@interface MPPowerFunctionLayout : MPFunctionLayout
|
@interface MPPowerFunctionLayout : MPFunctionLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property baseBounds
|
||||||
|
@abstract The bounds of the expression that is the base of the receiving
|
||||||
|
power function layout.
|
||||||
|
|
||||||
|
@discussion This value should be considered very volatile. It may change even
|
||||||
|
if the power function itself didn't. This value is guaranteed to
|
||||||
|
stay the same only during a single cycle of drawing.
|
||||||
|
*/
|
||||||
@property (nonatomic) NSRect baseBounds;
|
@property (nonatomic) NSRect baseBounds;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method powerFunction
|
||||||
|
@abstract Returns the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPPowerFunction@/link</code> represented by
|
||||||
|
the receiver.
|
||||||
|
*/
|
||||||
- (MPPowerFunction *)powerFunction;
|
- (MPPowerFunction *)powerFunction;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
|
|
||||||
#import "MPPowerFunction.h"
|
#import "MPPowerFunction.h"
|
||||||
|
|
||||||
|
|
||||||
#define kPowerFunctionExponentXOffset 1
|
#define kPowerFunctionExponentXOffset 1
|
||||||
#define kPowerFunctionTrailingOffset 2
|
#define kPowerFunctionTrailingOffset 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPPowerFunctionLayout
|
@implementation MPPowerFunctionLayout
|
||||||
|
|
||||||
- (NSRect)baseBounds
|
- (NSRect)baseBounds
|
||||||
@@ -23,16 +26,19 @@
|
|||||||
return _baseBounds;
|
return _baseBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (MPPowerFunction *)powerFunction
|
- (MPPowerFunction *)powerFunction
|
||||||
{
|
{
|
||||||
return (MPPowerFunction *)self.function;
|
return (MPPowerFunction *)self.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren
|
- (NSIndexSet *)indexesOfRemainingChildren
|
||||||
{
|
{
|
||||||
return [NSIndexSet indexSetWithIndex:0];
|
return [NSIndexSet indexSetWithIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#warning Broken Power Layout
|
#warning Broken Power Layout
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
@@ -41,16 +47,19 @@
|
|||||||
return NSMakePoint(kPowerFunctionExponentXOffset, y);
|
return NSMakePoint(kPowerFunctionExponentXOffset, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
|
return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)generateBounds
|
- (NSRect)generateBounds
|
||||||
{
|
{
|
||||||
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
|
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
|
||||||
@@ -59,9 +68,8 @@
|
|||||||
return NSMakeRect(0, 0, width, height);
|
return NSMakeRect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)draw
|
- (void)draw
|
||||||
{
|
{}
|
||||||
[[NSBezierPath bezierPathWithRect:self.bounds] stroke];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,12 +9,33 @@
|
|||||||
#import "MPFunctionTerm.h"
|
#import "MPFunctionTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPPowerTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPPowerTerm, MPTerm;
|
@class MPPowerTerm, MPTerm;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPPowerTerm
|
||||||
|
@abstract Represents a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPPowerFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A power function is evaluated using the C <code>pow</code>
|
||||||
|
function.
|
||||||
|
*/
|
||||||
@interface MPPowerTerm : MPFunctionTerm
|
@interface MPPowerTerm : MPFunctionTerm
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property baseTerm
|
||||||
|
@abstract The base of the power.
|
||||||
|
|
||||||
|
@discussion This value is set during the evaluation of the power term and
|
||||||
|
should be considered very volatile.
|
||||||
|
*/
|
||||||
@property (nonatomic, strong) MPTerm *baseTerm;
|
@property (nonatomic, strong) MPTerm *baseTerm;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "MPPowerTerm.h"
|
#import "MPPowerTerm.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPPowerTerm
|
@implementation MPPowerTerm
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPProductTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPProductTerm;
|
@class MPProductTerm;
|
||||||
|
|
||||||
@@ -22,7 +28,6 @@
|
|||||||
*/
|
*/
|
||||||
@interface MPProductTerm : MPTerm
|
@interface MPProductTerm : MPTerm
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method initWithFactors:
|
@method initWithFactors:
|
||||||
@abstract Initializes a new product term with the specified
|
@abstract Initializes a new product term with the specified
|
||||||
|
|||||||
@@ -8,17 +8,7 @@
|
|||||||
|
|
||||||
#import "MPProductTerm.h"
|
#import "MPProductTerm.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
#import "MPPowerFunction.h"
|
|
||||||
|
|
||||||
#import "MPFunctionTerm.h"
|
|
||||||
#import "MPElementaryFunctionTerm.h"
|
|
||||||
#import "MPNumber.h"
|
|
||||||
#import "MPFactorialTerm.h"
|
|
||||||
#import "MPPowerTerm.h"
|
|
||||||
#import "MPVariable.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
@implementation MPProductTerm
|
@implementation MPProductTerm
|
||||||
|
|
||||||
@@ -33,6 +23,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSDecimalNumber *value = [NSDecimalNumber one];
|
NSDecimalNumber *value = [NSDecimalNumber one];
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPRangePath</code> class.
|
||||||
|
|
||||||
The <code>MPRangePath</code> combines an <code>NSIndexPath</code> with a
|
The <code>MPRangePath</code> combines an <code>NSIndexPath</code> with a
|
||||||
<code>NSRange</code>. A range path is used in a tree structure to identify a
|
<code>NSRange</code>. A range path is used in a tree structure to identify a
|
||||||
number of subsequent nodes. This is achieved by combining a range path that
|
number of subsequent nodes. This is achieved by combining a range path that
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]
|
#define MPMakeRangePath(loc, len) [MPRangePath rangePathWithLocation:(loc) length:(len)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPRangePath, MPExpression;
|
@class MPRangePath, MPExpression;
|
||||||
|
|
||||||
|
|
||||||
@@ -268,7 +271,6 @@
|
|||||||
|
|
||||||
@interface MPExpression (MPRangeExtension)
|
@interface MPExpression (MPRangeExtension)
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method subexpressionWithRangePath:
|
@method subexpressionWithRangePath:
|
||||||
@abstract Creates a new expression with the symbols in the specified range
|
@abstract Creates a new expression with the symbols in the specified range
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
@implementation MPRangePath
|
@implementation MPRangePath
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Creation Methods
|
#pragma mark Creation Methods
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPSumFunction</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPSumFunction, MPExpression;
|
@class MPSumFunction, MPExpression;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
@implementation MPSumFunction
|
@implementation MPSumFunction
|
||||||
|
|
||||||
|
|
||||||
MPFunctionAccessorImplementation(StartExpression, _startExpression)
|
MPFunctionAccessorImplementation(StartExpression, _startExpression)
|
||||||
MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
|
MPFunctionAccessorImplementation(TargetExpression, _targetExpression)
|
||||||
MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
||||||
@@ -26,11 +25,13 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
|
|||||||
return @[@"startExpression", @"targetExpression", @"sumExpression"];
|
return @[@"startExpression", @"targetExpression", @"sumExpression"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index
|
- (BOOL)expectsVariableDefinitionInChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return index == 0;
|
return index == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (Class)functionTermClass
|
- (Class)functionTermClass
|
||||||
{
|
{
|
||||||
return [MPSumFunctionTerm class];
|
return [MPSumFunctionTerm class];
|
||||||
|
|||||||
@@ -9,12 +9,33 @@
|
|||||||
#import "MPFunctionLayout.h"
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPSumFunctionLayout</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPSumFunctionLayout, MPSumFunction;
|
@class MPSumFunctionLayout, MPSumFunction;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPSumFunctionLayout
|
||||||
|
@abstract A sum function layout displays a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPSumFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A sum is drawn using a capital greeg sigma (∑) The three children
|
||||||
|
are placed around it: The start expression below, target
|
||||||
|
expression above and sum expression to the right of it.
|
||||||
|
*/
|
||||||
@interface MPSumFunctionLayout : MPFunctionLayout
|
@interface MPSumFunctionLayout : MPFunctionLayout
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method sumFunction
|
||||||
|
@abstract Returns the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPSumFunction@/link</code> represented by the
|
||||||
|
receiver.
|
||||||
|
*/
|
||||||
- (MPSumFunction *)sumFunction;
|
- (MPSumFunction *)sumFunction;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -9,14 +9,16 @@
|
|||||||
#import "MPSumFunctionLayout.h"
|
#import "MPSumFunctionLayout.h"
|
||||||
|
|
||||||
#import "MPSumFunction.h"
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
|
||||||
#define kSumFunctionStartExpressionOffset 0
|
#define kSumFunctionStartExpressionOffset 0
|
||||||
#define kSumFunctionTargetExpressionOffset 0
|
#define kSumFunctionTargetExpressionOffset 0
|
||||||
#define kSumFunctionSumExpressionOffset 3
|
#define kSumFunctionSumExpressionOffset 3
|
||||||
#define kSumFunctionTrailingOffset 5
|
#define kSumFunctionTrailingOffset 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPSumFunctionLayout
|
@implementation MPSumFunctionLayout
|
||||||
|
|
||||||
- (MPSumFunction *)sumFunction
|
- (MPSumFunction *)sumFunction
|
||||||
@@ -24,16 +26,19 @@
|
|||||||
return (MPSumFunction *)self.function;
|
return (MPSumFunction *)self.function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfLeadingChild
|
- (NSUInteger)indexOfLeadingChild
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfTrailingChild
|
- (NSUInteger)indexOfTrailingChild
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
if (index != 2) {
|
if (index != 2) {
|
||||||
@@ -42,21 +47,25 @@
|
|||||||
return [super indexOfChildAfterChildAtIndex:index];
|
return [super indexOfChildAfterChildAtIndex:index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexSet *)indexesOfRemainingChildren
|
- (NSIndexSet *)indexesOfRemainingChildren
|
||||||
{
|
{
|
||||||
return [NSIndexSet indexSetWithIndex:2];
|
return [NSIndexSet indexSetWithIndex:2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (CTLineRef)line
|
- (CTLineRef)line
|
||||||
{
|
{
|
||||||
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
|
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
|
||||||
@@ -67,6 +76,7 @@
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)localLineBounds
|
- (NSRect)localLineBounds
|
||||||
{
|
{
|
||||||
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
||||||
@@ -77,6 +87,7 @@
|
|||||||
return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height);
|
return NSMakeRect(xPosition, lineBounds.origin.y, lineBounds.size.width, lineBounds.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
|
||||||
{
|
{
|
||||||
NSRect childBounds = [self childLayoutAtIndex:index].bounds;
|
NSRect childBounds = [self childLayoutAtIndex:index].bounds;
|
||||||
@@ -101,11 +112,13 @@
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
|
||||||
{
|
{
|
||||||
return (index == 0 || index == 1) ? YES : self.usesSmallSize;
|
return (index == 0 || index == 1) ? YES : self.usesSmallSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
|
||||||
{
|
{
|
||||||
if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) {
|
if (point.x < CTLineGetBoundsWithOptions(self.line, 0).size.width / 2) {
|
||||||
@@ -115,6 +128,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSRect)generateBounds
|
- (NSRect)generateBounds
|
||||||
{
|
{
|
||||||
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
NSRect lineBounds = CTLineGetBoundsWithOptions(self.line, 0);
|
||||||
@@ -139,6 +153,7 @@
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)draw
|
- (void)draw
|
||||||
{
|
{
|
||||||
// Get the current context
|
// Get the current context
|
||||||
|
|||||||
@@ -8,11 +8,30 @@
|
|||||||
|
|
||||||
#import "MPFunctionTerm.h"
|
#import "MPFunctionTerm.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPSumFunctionTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPSumFunctionTerm;
|
@class MPSumFunctionTerm;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPSumFunctionTerm
|
||||||
|
@abstract Represents and evaluates a <code>@link
|
||||||
|
//apple_ref/occ/cl/MPSumFunction@/link</code>.
|
||||||
|
|
||||||
|
@discussion A sum function evaluates its sum term <code>n</code> times. How
|
||||||
|
often it actually is evaluated depends on the boundary
|
||||||
|
expressions (start and target). Both are inclusive.
|
||||||
|
|
||||||
|
A sum function also features a variable that contains the current
|
||||||
|
value of the iteration. The variable is defined in the start
|
||||||
|
expression and incremented by <code>1</code> after every
|
||||||
|
iteration.
|
||||||
|
*/
|
||||||
@interface MPSumFunctionTerm : MPFunctionTerm
|
@interface MPSumFunctionTerm : MPFunctionTerm
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPSumFunctionTerm
|
@implementation MPSumFunctionTerm
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPSumTerm</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPSumTerm;
|
@class MPSumTerm;
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,7 @@
|
|||||||
|
|
||||||
#import "MPSumTerm.h"
|
#import "MPSumTerm.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
#import "MPProductTerm.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
@implementation MPSumTerm
|
@implementation MPSumTerm
|
||||||
|
|
||||||
@@ -26,6 +23,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSDecimalNumber *value = [NSDecimalNumber zero];
|
NSDecimalNumber *value = [NSDecimalNumber zero];
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPTerm</code> class.
|
||||||
|
|
||||||
The <code>MPTerm</code> class is used to evaluate a mathematical expression. A
|
The <code>MPTerm</code> class is used to evaluate a mathematical expression. A
|
||||||
term is a purely mathematical representation of a part of an expression. A term
|
term is a purely mathematical representation of a part of an expression. A term
|
||||||
can be defined with three rules:
|
can be defined with three rules:
|
||||||
@@ -66,6 +68,7 @@
|
|||||||
#define ReturnNilIfNil(test) if (test == nil) return nil
|
#define ReturnNilIfNil(test) if (test == nil) return nil
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPTerm;
|
@class MPTerm;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
#import "MPParsedExpression.h"
|
||||||
#import "MPSumTerm.h"
|
|
||||||
#import "MPEvaluationContext.h"
|
#import "MPEvaluationContext.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPTerm
|
@implementation MPTerm
|
||||||
|
|
||||||
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)evaluate:(NSError *__autoreleasing *)error
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)defineVariable:(NSString *)variableName
|
- (BOOL)defineVariable:(NSString *)variableName
|
||||||
value:(NSDecimalNumber *)value
|
value:(NSDecimalNumber *)value
|
||||||
error:(NSError *__autoreleasing *)error
|
error:(NSError *__autoreleasing *)error
|
||||||
@@ -39,6 +41,7 @@
|
|||||||
return couldDefineVariable;
|
return couldDefineVariable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)valueForVariable:(NSString *)variableName
|
- (NSDecimalNumber *)valueForVariable:(NSString *)variableName
|
||||||
error:(NSError *__autoreleasing *)error
|
error:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
@@ -54,6 +57,7 @@
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)undefineVariable:(NSString *)variableName
|
- (void)undefineVariable:(NSString *)variableName
|
||||||
{
|
{
|
||||||
[[MPEvaluationContext sharedContext] undefineVariable:variableName];
|
[[MPEvaluationContext sharedContext] undefineVariable:variableName];
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@header
|
@header
|
||||||
|
This file contains the <code>MPToken</code> class and protocol.
|
||||||
|
|
||||||
One way to represent a mathematical expression using the <code>@link
|
One way to represent a mathematical expression using the <code>@link
|
||||||
MPExpression@/link</code> class is a sequence of tokens. A token is a logical
|
MPExpression@/link</code> class is a sequence of tokens. A token is a logical
|
||||||
unit of input. The different types of units are identified by a @link
|
unit of input. The different types of units are identified by a @link
|
||||||
@@ -30,10 +32,6 @@ while ((token = [self nextToken]).tokenType != MPEOFToken) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@class MPToken;
|
|
||||||
@protocol MPToken;
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@typedef MPTokenType
|
@typedef MPTokenType
|
||||||
@abstract The type of a token identifies its behaviour in a mathematical
|
@abstract The type of a token identifies its behaviour in a mathematical
|
||||||
@@ -103,6 +101,10 @@ typedef enum {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@class MPToken;
|
||||||
|
@protocol MPToken;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@protocol MPToken
|
@protocol MPToken
|
||||||
@abstract Tokens represent logical units in an expresion.
|
@abstract Tokens represent logical units in an expresion.
|
||||||
|
|||||||
@@ -9,14 +9,44 @@
|
|||||||
#import "MPTerm.h"
|
#import "MPTerm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPVariable</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPVariable;
|
@class MPVariable;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPVariable
|
||||||
|
@abstract This class represents a variable.
|
||||||
|
|
||||||
|
@discussion Variables are evaluated in the <code>@link
|
||||||
|
//apple_ref/occ/cl/MPEvaluationContext@/link</code> and generate
|
||||||
|
errors if they are not defined.
|
||||||
|
*/
|
||||||
@interface MPVariable : MPTerm
|
@interface MPVariable : MPTerm
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method initWithVariableName:
|
||||||
|
@abstract Initializes a <code>MPVariable</code> with the specified
|
||||||
|
<code>variableName</code>
|
||||||
|
|
||||||
|
@param variableName
|
||||||
|
The name of the variable. Must not be <code>nil</code> and must
|
||||||
|
be at least one character long.
|
||||||
|
|
||||||
|
@return A new <code>MPVariable</code> instance.
|
||||||
|
*/
|
||||||
- (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */
|
- (instancetype)initWithVariableName:(NSString *)variableName; /* designated initializer */
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property variableName
|
||||||
|
@abstract The receiver's variable name.
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic, strong) NSString *variableName;
|
@property (readonly, nonatomic, strong) NSString *variableName;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,12 +8,7 @@
|
|||||||
|
|
||||||
#import "MPVariable.h"
|
#import "MPVariable.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPEvaluationContext.h"
|
|
||||||
|
|
||||||
@implementation MPVariable
|
@implementation MPVariable
|
||||||
|
|
||||||
@@ -22,11 +17,13 @@
|
|||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
NSAssert(variableName != nil, @"variableName must not be nil.");
|
NSAssert(variableName != nil, @"variableName must not be nil.");
|
||||||
|
NSAssert(variableName.length > 0, @"variableName must be at least one character long.");
|
||||||
_variableName = variableName;
|
_variableName = variableName;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
- (NSDecimalNumber *)doEvaluation:(NSError *__autoreleasing *)error
|
||||||
{
|
{
|
||||||
return [self valueForVariable:self.variableName
|
return [self valueForVariable:self.variableName
|
||||||
|
|||||||
@@ -7,10 +7,20 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>MPWhiteView</code> class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@class MPWhiteView;
|
@class MPWhiteView;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPWhiteView
|
||||||
|
@abstract A <code>NSView</code> that draws a white square in its frame.
|
||||||
|
*/
|
||||||
@interface MPWhiteView : NSView
|
@interface MPWhiteView : NSView
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -6,20 +6,19 @@
|
|||||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "MPExpression.h"
|
|
||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
#import "NSString+MPExpressionElement.h"
|
#import "NSString+MPExpressionElement.h"
|
||||||
#import "MPFunction.h"
|
#import "MPFunction.h"
|
||||||
|
|
||||||
#import "MPSumFunction.h"
|
|
||||||
#import "MPParenthesisFunction.h"
|
|
||||||
#import "MPPowerFunction.h"
|
|
||||||
#import "MPFractionFunction.h"
|
|
||||||
|
|
||||||
#import "MPToken.h"
|
#import "MPToken.h"
|
||||||
#import "MPFunction+MPToken.h"
|
#import "MPFunction+MPToken.h"
|
||||||
|
|
||||||
|
#import "MPFractionFunction.h"
|
||||||
|
#import "MPParenthesisFunction.h"
|
||||||
|
#import "MPPowerFunction.h"
|
||||||
|
#import "MPSumFunction.h"
|
||||||
|
|
||||||
|
#import "MPParsedExpression.h"
|
||||||
|
#import "MPTerm.h"
|
||||||
#import "MPEvaluationContext.h"
|
#import "MPEvaluationContext.h"
|
||||||
#import "MPMathRules.h"
|
#import "MPMathRules.h"
|
||||||
|
|
||||||
@@ -27,5 +26,13 @@
|
|||||||
#import "NSIndexPath+MPAdditions.h"
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
#import "NSRegularExpression+MPParsingAdditions.h"
|
#import "NSRegularExpression+MPParsingAdditions.h"
|
||||||
|
|
||||||
#import "MPExpressionView.h"
|
|
||||||
#import "MPExpressionStorage.h"
|
#import "MPExpressionStorage.h"
|
||||||
|
#import "MPExpressionView.h"
|
||||||
|
#import "MPLayout.h"
|
||||||
|
#import "MPExpressionLayout.h"
|
||||||
|
#import "MPFunctionLayout.h"
|
||||||
|
|
||||||
|
#import "MPFractionFunctionLayout.h"
|
||||||
|
#import "MPParenthesisFunctionLayout.h"
|
||||||
|
#import "MPPowerFunctionLayout.h"
|
||||||
|
#import "MPSumFunctionLayout.h"
|
||||||
@@ -7,6 +7,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>NSIndexPath(MPAdditions)</code> category.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@category NSIndexPath (MPAdditions)
|
@category NSIndexPath (MPAdditions)
|
||||||
@@ -15,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
@interface NSIndexPath (MPAdditions)
|
@interface NSIndexPath (MPAdditions)
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@property firstIndex
|
@property firstIndex
|
||||||
@abstract The first index from the receiver.
|
@abstract The first index from the receiver.
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
@implementation NSIndexPath (MPAdditions)
|
@implementation NSIndexPath (MPAdditions)
|
||||||
|
|
||||||
|
|
||||||
- (NSUInteger)firstIndex
|
- (NSUInteger)firstIndex
|
||||||
{
|
{
|
||||||
return [self indexAtPosition:0];
|
return [self indexAtPosition:0];
|
||||||
|
|||||||
@@ -7,6 +7,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>NSRegularExpression(MPParsingAdditions)</code>
|
||||||
|
category.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@category NSRegularExpression (MPParsingAdditions)
|
@category NSRegularExpression (MPParsingAdditions)
|
||||||
@@ -15,7 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
@interface NSRegularExpression (MPParsingAdditions)
|
@interface NSRegularExpression (MPParsingAdditions)
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@method firstMathInString:
|
@method firstMathInString:
|
||||||
@abstract Returns the first match of the regular expression within the
|
@abstract Returns the first match of the regular expression within the
|
||||||
|
|||||||
@@ -9,11 +9,18 @@
|
|||||||
#import "MPExpression.h"
|
#import "MPExpression.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
This file contains the <code>NSString(MPExpressionElement)</code> category.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@category NSString (MPExpressionElement)
|
@category NSString (MPExpressionElement)
|
||||||
@abstract This category adds <code>@link MPExpressionElement@/link</code>
|
@abstract This category adds <code>@link
|
||||||
protocol conformance to the <code>NSString</code> class.
|
//apple_ref/occ/intf/MPExpressionElement@/link</code> protocol
|
||||||
|
conformance to the <code>NSString</code> class.
|
||||||
*/
|
*/
|
||||||
@interface NSString (MPExpressionElement) <MPExpressionElement>
|
@interface NSString (MPExpressionElement) <MPExpressionElement>
|
||||||
|
|
||||||
|
|||||||
@@ -253,8 +253,6 @@
|
|||||||
3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
3B85832719BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = "<group>"; };
|
3B85833219BB5F2D00D76A8D /* MathKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MathKit.h; sourceTree = "<group>"; };
|
||||||
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = "<group>"; };
|
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = "<group>"; };
|
||||||
3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = "<group>"; };
|
|
||||||
3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = "<group>"; };
|
|
||||||
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3BF9976E18DE623E009CF6C4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
3BF9976E18DE623E009CF6C4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||||
3BF9977118DE623E009CF6C4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
3BF9977118DE623E009CF6C4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||||
@@ -460,15 +458,6 @@
|
|||||||
name = "Function Layouts";
|
name = "Function Layouts";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
3BC4661819B3692A0033F13A /* Temp */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
3BC4661519B365070033F13A /* MPArrayCache.h */,
|
|
||||||
3BC4661619B365070033F13A /* MPArrayCache.m */,
|
|
||||||
);
|
|
||||||
name = Temp;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
3BC46B4B19B38CB60033F13A /* Base Expression Classes */ = {
|
3BC46B4B19B38CB60033F13A /* Base Expression Classes */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -510,22 +499,22 @@
|
|||||||
3B6338671A3A4C2B00698BFB /* MPParsedExpression.m */,
|
3B6338671A3A4C2B00698BFB /* MPParsedExpression.m */,
|
||||||
3B63387A1A3A4C7E00698BFB /* MPTerm.h */,
|
3B63387A1A3A4C7E00698BFB /* MPTerm.h */,
|
||||||
3B63387B1A3A4C7E00698BFB /* MPTerm.m */,
|
3B63387B1A3A4C7E00698BFB /* MPTerm.m */,
|
||||||
3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
|
|
||||||
3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
|
|
||||||
3B6338781A3A4C7E00698BFB /* MPSumTerm.h */,
|
3B6338781A3A4C7E00698BFB /* MPSumTerm.h */,
|
||||||
3B6338791A3A4C7E00698BFB /* MPSumTerm.m */,
|
3B6338791A3A4C7E00698BFB /* MPSumTerm.m */,
|
||||||
3B63386A1A3A4C2B00698BFB /* MPProductTerm.h */,
|
3B63386A1A3A4C2B00698BFB /* MPProductTerm.h */,
|
||||||
3B63386B1A3A4C2B00698BFB /* MPProductTerm.m */,
|
3B63386B1A3A4C2B00698BFB /* MPProductTerm.m */,
|
||||||
3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */,
|
3B6338601A3A4C2B00698BFB /* MPNegatedTerm.h */,
|
||||||
3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */,
|
3B6338611A3A4C2B00698BFB /* MPNegatedTerm.m */,
|
||||||
|
3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */,
|
||||||
|
3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */,
|
||||||
3B6338881A3A4CAF00698BFB /* MPElementaryFunctionTerm.h */,
|
3B6338881A3A4CAF00698BFB /* MPElementaryFunctionTerm.h */,
|
||||||
3B6338891A3A4CAF00698BFB /* MPElementaryFunctionTerm.m */,
|
3B6338891A3A4CAF00698BFB /* MPElementaryFunctionTerm.m */,
|
||||||
3B6338621A3A4C2B00698BFB /* MPNumber.h */,
|
3B6338621A3A4C2B00698BFB /* MPNumber.h */,
|
||||||
3B6338631A3A4C2B00698BFB /* MPNumber.m */,
|
3B6338631A3A4C2B00698BFB /* MPNumber.m */,
|
||||||
3B63387C1A3A4C7E00698BFB /* MPVariable.h */,
|
3B63387C1A3A4C7E00698BFB /* MPVariable.h */,
|
||||||
3B63387D1A3A4C7E00698BFB /* MPVariable.m */,
|
3B63387D1A3A4C7E00698BFB /* MPVariable.m */,
|
||||||
3B63388C1A3A4CBE00698BFB /* MPFunctionTerm.h */,
|
3B6338841A3A4CA500698BFB /* MPFactorialTerm.h */,
|
||||||
3B63388D1A3A4CBE00698BFB /* MPFunctionTerm.m */,
|
3B6338851A3A4CA500698BFB /* MPFactorialTerm.m */,
|
||||||
3B6338901A3A4CCA00698BFB /* MPEvaluationContext.h */,
|
3B6338901A3A4CCA00698BFB /* MPEvaluationContext.h */,
|
||||||
3B6338911A3A4CCA00698BFB /* MPEvaluationContext.m */,
|
3B6338911A3A4CCA00698BFB /* MPEvaluationContext.m */,
|
||||||
3B6338941A3A4CD100698BFB /* MPMathRules.h */,
|
3B6338941A3A4CD100698BFB /* MPMathRules.h */,
|
||||||
@@ -538,14 +527,14 @@
|
|||||||
3BEFF75F1A17FF5C00301C0C /* Functions */ = {
|
3BEFF75F1A17FF5C00301C0C /* Functions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */,
|
3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */,
|
||||||
3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */,
|
3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */,
|
||||||
3B6338641A3A4C2B00698BFB /* MPParenthesisTerm.h */,
|
3B6338641A3A4C2B00698BFB /* MPParenthesisTerm.h */,
|
||||||
3B6338651A3A4C2B00698BFB /* MPParenthesisTerm.m */,
|
3B6338651A3A4C2B00698BFB /* MPParenthesisTerm.m */,
|
||||||
3B6338681A3A4C2B00698BFB /* MPPowerTerm.h */,
|
3B6338681A3A4C2B00698BFB /* MPPowerTerm.h */,
|
||||||
3B6338691A3A4C2B00698BFB /* MPPowerTerm.m */,
|
3B6338691A3A4C2B00698BFB /* MPPowerTerm.m */,
|
||||||
3B63389C1A3A4CEF00698BFB /* MPFractionTerm.h */,
|
3B6338981A3A4CE100698BFB /* MPSumFunctionTerm.h */,
|
||||||
3B63389D1A3A4CEF00698BFB /* MPFractionTerm.m */,
|
3B6338991A3A4CE100698BFB /* MPSumFunctionTerm.m */,
|
||||||
);
|
);
|
||||||
name = Functions;
|
name = Functions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -598,7 +587,6 @@
|
|||||||
3BF9977418DE623E009CF6C4 /* MathPad */ = {
|
3BF9977418DE623E009CF6C4 /* MathPad */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3BC4661819B3692A0033F13A /* Temp */,
|
|
||||||
3BF9978018DE623E009CF6C4 /* MPDocument.h */,
|
3BF9978018DE623E009CF6C4 /* MPDocument.h */,
|
||||||
3BF9978118DE623E009CF6C4 /* MPDocument.m */,
|
3BF9978118DE623E009CF6C4 /* MPDocument.m */,
|
||||||
3B87E353190082E200259938 /* Resources */,
|
3B87E353190082E200259938 /* Resources */,
|
||||||
|
|||||||
@@ -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>
|
#import <MathKit/MathKit.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@class MPDocument
|
||||||
|
@abstract This class is the document class that displays the MathPad user
|
||||||
|
interface.
|
||||||
|
*/
|
||||||
@interface MPDocument : NSDocument
|
@interface MPDocument : NSDocument
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property expressionView
|
||||||
|
@abstract The main expression view.
|
||||||
|
*/
|
||||||
@property (weak) IBOutlet MPExpressionView *expressionView;
|
@property (weak) IBOutlet MPExpressionView *expressionView;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@property resultLabel
|
||||||
|
@abstract The label which displays the result of the calculation.
|
||||||
|
|
||||||
|
@discussion The label is placed inside the expression view.
|
||||||
|
*/
|
||||||
@property (weak) IBOutlet NSTextField *resultLabel;
|
@property (weak) IBOutlet NSTextField *resultLabel;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@method evaluateExpression:
|
||||||
|
@abstract Called by the expression view when it should be evaluated.
|
||||||
|
|
||||||
|
@param sender
|
||||||
|
Typically the object that invoked the method.
|
||||||
|
*/
|
||||||
- (IBAction)evaluateExpression:(id)sender;
|
- (IBAction)evaluateExpression:(id)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
|
|
||||||
#import "MPDocument.h"
|
#import "MPDocument.h"
|
||||||
|
|
||||||
#import "MPParsedExpression.h"
|
|
||||||
|
|
||||||
@implementation MPDocument
|
|
||||||
|
@implementation MPDocument {
|
||||||
|
MPExpression *loadedExpression;
|
||||||
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
@@ -21,46 +23,47 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSString *)windowNibName
|
- (NSString *)windowNibName
|
||||||
{
|
{
|
||||||
// Override returning the nib file name of the document
|
|
||||||
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
|
|
||||||
return @"MPDocument";
|
return @"MPDocument";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
|
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
|
||||||
{
|
{
|
||||||
[super windowControllerDidLoadNib:aController];
|
[super windowControllerDidLoadNib:aController];
|
||||||
self.expressionView.target = self;
|
self.expressionView.target = self;
|
||||||
self.expressionView.action = @selector(evaluateExpression:);
|
self.expressionView.action = @selector(evaluateExpression:);
|
||||||
// Add any code here that needs to be executed once the windowController has loaded the document's window.
|
if (loadedExpression) {
|
||||||
|
[self.expressionView.expressionStorage appendElements:[loadedExpression allItemsInReferenceFrame:MPElementReferenceFrame]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
+ (BOOL)autosavesInPlace
|
+ (BOOL)autosavesInPlace
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
|
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
|
||||||
{
|
{
|
||||||
// Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
|
return [NSKeyedArchiver archivedDataWithRootObject:self.expressionView.expressionStorage];
|
||||||
// You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
|
|
||||||
NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
|
|
||||||
@throw exception;
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
|
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
|
||||||
{
|
{
|
||||||
// Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
|
MPExpression *expression = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
||||||
// You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
|
loadedExpression = expression;
|
||||||
// If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
|
|
||||||
NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
|
|
||||||
@throw exception;
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark Actions
|
#pragma mark Actions
|
||||||
|
|
||||||
|
|
||||||
- (IBAction)evaluateExpression:(id)sender {
|
- (IBAction)evaluateExpression:(id)sender {
|
||||||
NSArray *errors;
|
NSArray *errors;
|
||||||
MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors];
|
MPParsedExpression *parsedExpression = [self.expressionView.expressionStorage parse:&errors];
|
||||||
@@ -74,4 +77,5 @@
|
|||||||
self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @"";
|
self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user