diff --git a/MathPad/Base.lproj/MPDocument.xib b/MathPad/Base.lproj/MPDocument.xib
index 0a691eb..dbf8825 100644
--- a/MathPad/Base.lproj/MPDocument.xib
+++ b/MathPad/Base.lproj/MPDocument.xib
@@ -22,15 +22,28 @@
-
+
+
-
-
-
-
+
+
+
+
+
+
diff --git a/MathPad/MPDocument.h b/MathPad/MPDocument.h
index 927eff2..6b69ad2 100644
--- a/MathPad/MPDocument.h
+++ b/MathPad/MPDocument.h
@@ -10,9 +10,8 @@
@interface MPDocument : NSDocument
-@property (weak) IBOutlet MPExpressionView *termExpressionView;
@property (weak) IBOutlet MPExpressionView *resultExpressionView;
-- (IBAction)changeExpression:(id)sender;
+- (IBAction)evaluateExpressiom:(id)sender;
@end
diff --git a/MathPad/MPDocument.m b/MathPad/MPDocument.m
index aa4ee62..f06ef47 100644
--- a/MathPad/MPDocument.m
+++ b/MathPad/MPDocument.m
@@ -7,7 +7,6 @@
//
#import "MPDocument.h"
-#import "MPModel.h"
#import "MPView.h"
@implementation MPDocument
@@ -59,8 +58,12 @@
}
#pragma mark Actions
-- (IBAction)changeExpression:(id)sender {
- [self.resultExpressionView.expressionStorage insertElement:@"abc" atLocation:6];
+- (IBAction)evaluateExpressiom:(id)sender {
+ NSError *error;
+ NSLog(@"Result: %f", [self.resultExpressionView.expressionStorage evaluateExpression:&error]);
+ if (error) {
+ NSLog(@"Error: %@", error);
+ }
}
@end
diff --git a/MathPad/MPExpression.h b/MathPad/MPExpression.h
index 8a9f9e9..6862649 100644
--- a/MathPad/MPExpression.h
+++ b/MathPad/MPExpression.h
@@ -9,7 +9,7 @@
@import Foundation;
#import "NSString+MPExpressionElement.h"
-@class MPExpression, MPFunction, MPRangePath;
+@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator;
@protocol MPExpressionElement;
/*!
@@ -269,8 +269,26 @@
- (void)replaceSymbolsInRange:(NSRange)range
withElements:(NSArray *)elements;
-#warning Evaluating must possibly return error
-- (double)doubleValue; // Evaluates Expression
+
+/*!
+ @method elements
+ @brief Returns an array of all elements in the receiver.
+
+ @discussion The elements in the returned array are not copied before they are
+ returned.
+
+ @return An array of all elements from the receiver.
+ */
+- (NSArray *)elements;
+
+
+#pragma mark Evaluating Expressions
+
+- (double)evaluateExpression:(NSError *__autoreleasing *)error;
+
+
+// TODO: Private?
+@property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator;
#pragma mark Notifications
// All notification methods should create a new rangePath with the receiver's index added to the beginning of the path and then ascend the message to it's parent
@@ -312,7 +330,7 @@
@return @c YES if @c anExpression is equal to the receiver, @c NO
otherwise.
*/
-- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
+// - (BOOL)isEqualToExpression:(MPExpression *)anExpression;
- (NSString *)description;
- (NSUInteger)hash;
@@ -534,21 +552,6 @@
- (void)deleteElementsInRange:(NSRange)range;
-#pragma mark Querying Expressions
-
-
-/*!
- @method elements
- @brief Returns an array of all elements in the receiver.
-
- @discussion The elements in the returned array are not copied before they are
- returned.
-
- @return An array of all elements from the receiver.
- */
-- (NSArray *)elements;
-
-
/*!
@method mutableElements
@brief Returns a proxy mutable array object that responds to all methods
@@ -564,3 +567,4 @@
// - (NSMutableArray *)mutableElements;
@end
+
diff --git a/MathPad/MPExpression.m b/MathPad/MPExpression.m
index cc22c52..bdae3f7 100644
--- a/MathPad/MPExpression.m
+++ b/MathPad/MPExpression.m
@@ -6,6 +6,8 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
+// TODO: Reorganise header/implementation order. Drop the primitive/not primitive Approach
+
#import "MPExpression.h"
#import "MPFunction.h"
#import "MPRangePath.h"
@@ -13,8 +15,11 @@
#import "NSIndexPath+MPAdditions.h"
#import "MPException.h"
-@interface MPExpression ()
-@property (readonly, nonatomic, strong) NSMutableArray *elements;
+#import "MPExpressionEvaluator.h"
+
+@interface MPExpression () {
+ NSMutableArray *__strong _elements;
+}
@end
@interface MPExpression (MPExpressionPrivate)
@@ -64,8 +69,8 @@
NSString *splitElement = (NSString *)self.elements[splitElementIndex];
NSString *leftPart = [splitElement substringToIndex:splitOffset];
NSString *rightPart = [splitElement substringFromIndex:splitOffset];
- [self.elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
- withObjectsFromArray:@[leftPart, rightPart]];
+ [_elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
+ withObjectsFromArray:@[leftPart, rightPart]];
++splitElementIndex;
}
*insertionIndex = splitElementIndex;
@@ -81,8 +86,6 @@
NSUInteger _replacementLength;
}
-@synthesize elements = _elements;
-
#pragma mark Creation Methods
- (instancetype)init
{
@@ -114,14 +117,14 @@
id current = self.elements[index];
if ([current isString]) {
if (current.length == 0) {
- [self.elements removeObjectAtIndex:index];
+ [_elements removeObjectAtIndex:index];
if (index >= _editedRange.location && index < NSMaxRange(_editedRange)) {
--_replacementLength;
}
--index;
} else if ([next isString]) {
NSString *new = [NSString stringWithFormat:@"%@%@", current, next];
- [self.elements replaceObjectsInRange:NSMakeRange(index, 2)
+ [_elements replaceObjectsInRange:NSMakeRange(index, 2)
withObjectsFromArray:@[new]];
NSUInteger maxReplacementIndex = _editedRange.location + _replacementLength;
if (index == _editedRange.location - 1) {
@@ -244,7 +247,7 @@
// Perform the replacement
NSMutableArray *newElements = [[NSMutableArray alloc] initWithArray:elements
copyItems:YES];
- [self.elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
+ [_elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
withObjectsFromArray:newElements];
_cachedLength = 0;
@@ -260,11 +263,23 @@
replacementLength:_replacementLength];
}
-
-- (double)doubleValue
+- (NSArray *)elements
{
-#warning Unimplemented Method
- return 0;
+ return _elements;
+}
+
+- (double)evaluateExpression:(NSError *__autoreleasing *)error
+{
+ return [self.evaluator evaluateWithError:error];
+}
+
+@synthesize evaluator = _evaluator;
+- (MPExpressionEvaluator *)evaluator
+{
+ if (!_evaluator) {
+ _evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self];
+ }
+ return _evaluator;
}
#pragma mark Notifications
@@ -272,8 +287,7 @@
replacementLength:(NSUInteger)replacementLength
{
NSUInteger selfIndex = [self.parent indexOfChild:self];
- MPRangePath *newPath = rangePath.copy;
- newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
+ MPRangePath *newPath = MPMakeRangePath([rangePath.location indexPathByPreceedingIndex:selfIndex], rangePath.length);
[self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength];
}
diff --git a/MathPad/MPExpressionLayout.m b/MathPad/MPExpressionLayout.m
index baa4e9d..0ae3c52 100644
--- a/MathPad/MPExpressionLayout.m
+++ b/MathPad/MPExpressionLayout.m
@@ -16,7 +16,6 @@
@interface MPExpressionLayout (MPLineGeneration)
- (CTLineRef)lineForElementAtIndex:(NSUInteger)index;
-- (CTLineRef)createLineForString:(NSString *)aString;
@end
@@ -36,16 +35,6 @@
return (__bridge CTLineRef)lineObject;
}
-- (CTLineRef)createLineForString:(NSString *)aString
-{
- NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
- attributes:@{NSFontAttributeName: self.font}];
- CFAttributedStringRef attributedString = CFBridgingRetain(text);
- CTLineRef line = CTLineCreateWithAttributedString(attributedString);
- CFRelease(attributedString); // TODO: Is this release appropriate?
- return line;
-}
-
@end
@implementation MPExpressionLayout
diff --git a/MathPad/MPExpressionView.m b/MathPad/MPExpressionView.m
index 6a40209..0624075 100644
--- a/MathPad/MPExpressionView.m
+++ b/MathPad/MPExpressionView.m
@@ -51,7 +51,7 @@
{
NSRect expressionBounds = [self.expressionStorage.rootLayout bounds];
CGFloat y = (self.bounds.size.height - expressionBounds.size.height) / 2 + fabs(expressionBounds.origin.y);
- return NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
+ return NSMakePoint(0, y);
}
@end
@@ -124,15 +124,19 @@
- (void)initializeExpressionView
{
+ // Setup the Expression Storage
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
expressionStorage.expressionView = self;
_expressionStorage = expressionStorage;
- NSRect frame = NSMakeRect(10, 10, 500, 500);
- NSButton *button = [[NSButton alloc] initWithFrame:frame];
+
+ // Setup the Functions Button
+ NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
button.buttonType = NSSwitchButton;
[button setTitle:@"Functions"];
self.functionsButton = button;
[self addSubview:self.functionsButton];
+
+ // Setup Selection
self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)];
self.caretBlinkRate = 1.0;
[self restartCaretTimer];
@@ -202,7 +206,9 @@
- (void)keyDown:(NSEvent *)theEvent
{
NSString *characters = theEvent.characters;
- if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]].length == 0) {
+ NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
+ [allowedCharacters addCharactersInString:@"+-*/= "];
+ if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
[targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[characters]];
self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0);
@@ -277,7 +283,8 @@
- (void)moveToEndOfLine:(id)sender
{
-
+ MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
+ self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.length], 0);
}
- (void)moveLeftAndModifySelection:(id)sender
@@ -317,12 +324,12 @@
- (void)moveWordRightAndModifySelection:(id)sender
{
-
+#warning Unimplemented Method
}
- (void)moveWordLeftAndModifySelection:(id)sender
{
-
+#warning Unimplemented Method
}
- (void)selectAll:(id)sender
@@ -361,7 +368,7 @@
// Draw the background
[super drawRect:dirtyRect];
[[NSColor whiteColor] set];
- NSRectFill(self.bounds);
+ NSRectFill(dirtyRect);
// Calculate the position of the expression (probably also forces layout of the expression the first time)
NSPoint expressionOrigin = self.expressionOrigin;
diff --git a/MathPad/MPFunction.h b/MathPad/MPFunction.h
index bba4431..ca7ca9c 100644
--- a/MathPad/MPFunction.h
+++ b/MathPad/MPFunction.h
@@ -37,7 +37,7 @@
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Evaluating Functions
-- (double)doubleValue; // Override
+- (double)evaluateFunction:(NSError *__autoreleasing *)error;
#pragma mark Messages
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
@@ -46,7 +46,7 @@
- (void)didChangeChildAtIndex:(NSUInteger)index;
#pragma mark Working With Functions
-- (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
+// - (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
- (NSString *)description; // Should be overridden
- (NSUInteger)hash;// Override
diff --git a/MathPad/MPFunction.m b/MathPad/MPFunction.m
index bd3b02d..c70f8d4 100644
--- a/MathPad/MPFunction.m
+++ b/MathPad/MPFunction.m
@@ -82,7 +82,7 @@
}
#pragma mark Evaluating Functions
-- (double)doubleValue
+- (double)evaluateFunction:(NSError *__autoreleasing *)error
{
return 0;
}
@@ -92,8 +92,7 @@
replacementLength:(NSUInteger)replacementLength
{
NSUInteger selfIndex = [self.parent indexOfElement:self];
- MPRangePath *newPath = rangePath.copy;
- newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
+ MPRangePath *newPath = MPMakeRangePath([rangePath.location indexPathByPreceedingIndex:selfIndex], rangePath.length);
[self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength];
}
diff --git a/MathPad/MPLayout.h b/MathPad/MPLayout.h
index a341e74..26cb9b6 100644
--- a/MathPad/MPLayout.h
+++ b/MathPad/MPLayout.h
@@ -41,6 +41,8 @@
- (void)invalidate;
#pragma mark Calculation and Drawing Methods
+- (CTLineRef)createLineForString:(NSString *)aString;
+
@property (nonatomic, getter = isFlipped) BOOL flipped;
@property (nonatomic) BOOL usesSmallSize;
- (NSRect)bounds;
diff --git a/MathPad/MPLayout.m b/MathPad/MPLayout.m
index 22768e8..49b5b4f 100644
--- a/MathPad/MPLayout.m
+++ b/MathPad/MPLayout.m
@@ -128,6 +128,16 @@
}
#pragma mark Calculation and Drawing Methods
+- (CTLineRef)createLineForString:(NSString *)aString
+{
+ NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
+ attributes:@{NSFontAttributeName: self.font}];
+ CFAttributedStringRef attributedString = CFBridgingRetain(text);
+ CTLineRef line = CTLineCreateWithAttributedString(attributedString);
+ CFRelease(attributedString); // TODO: Is this release appropriate?
+ return line;
+}
+
- (NSRect)bounds
{
if (NSEqualRects(_cachedBounds, NSZeroRect)) {
@@ -161,33 +171,4 @@
[transform concat];
}
-@end
-
-@implementation MPLayout (MPSubclassImplement)
-
-- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
-{
- return nil;
-}
-
-- (NSRect)generateBounds
-{
- return NSZeroRect;
-}
-
-- (NSRect)boundingRectForRange:(NSRange)range
-{
- return NSZeroRect;
-}
-
-- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
-{
- return NSZeroPoint;
-}
-
-- (void)draw
-{
-
-}
-
-@end
+@end
\ No newline at end of file
diff --git a/MathPad/MPSumFunction.m b/MathPad/MPSumFunction.m
index e267d2c..6b649f5 100644
--- a/MathPad/MPSumFunction.m
+++ b/MathPad/MPSumFunction.m
@@ -97,10 +97,10 @@
}
#pragma mark Evaluating Functions
-- (double)doubleValue
+- (double)evaluateFunction:(NSError *__autoreleasing *)error
{
#warning Unimplemented Method
- return 0;
+ return 0.0;
}
#pragma mark Working With Functions
diff --git a/MathPad/MPSumFunctionLayout.m b/MathPad/MPSumFunctionLayout.m
index 887f2b7..b30de11 100644
--- a/MathPad/MPSumFunctionLayout.m
+++ b/MathPad/MPSumFunctionLayout.m
@@ -26,13 +26,7 @@
- (CTLineRef)line
{
CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{
- NSAttributedString *text =
- [[NSAttributedString alloc] initWithString:@"∑"
- attributes:@{NSFontAttributeName: self.font}];
- CFAttributedStringRef attributedString = CFBridgingRetain(text);
- CTLineRef line = CTLineCreateWithAttributedString(attributedString);
- CFRelease(attributedString); // TODO: Is this release appropriate
- return line;
+ return [self createLineForString:@"∑"];
}];
return line;
}