Internal Redesign:
- Combined MPExpression and MPMutableExpression - Abstracted children of MPExpression into MPExpressionElement protocol - Abstracted most of MPExpressionLayout and MPFunctionLayout into common superclass MPLayout
This commit is contained in:
@@ -9,20 +9,20 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
3B0F69A919028C6000817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; };
|
||||
3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */; };
|
||||
3B0F69AD1902AD2200817707 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3581900857C00259938 /* MPExpression.m */; };
|
||||
3B0F69AE1902AD2800817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; };
|
||||
3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; };
|
||||
3B528D10199417E10054DB5F /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */; };
|
||||
3B528D13199417E90054DB5F /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D12199417E90054DB5F /* MPFunctionLayout.m */; };
|
||||
3B528D1619941F5B0054DB5F /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */; };
|
||||
3B528D1819941F5B0054DB5F /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D12199417E90054DB5F /* MPFunctionLayout.m */; };
|
||||
3B53AD5F1997E0FB00C925C4 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; };
|
||||
3B688D9919982DF50006B4AB /* MPLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B688D9819982DF50006B4AB /* MPLayout.m */; };
|
||||
3B87E3561900856F00259938 /* MPExpressionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3551900856F00259938 /* MPExpressionView.m */; };
|
||||
3B87E3591900857C00259938 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3581900857C00259938 /* MPExpression.m */; };
|
||||
3B87E36019009D5F00259938 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; };
|
||||
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */; };
|
||||
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; };
|
||||
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */; };
|
||||
3BB09ED3190713FC0080A5ED /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */; };
|
||||
3BB09EDE190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */; };
|
||||
3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */; };
|
||||
3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */; };
|
||||
3BBBA358190327B700824E74 /* MPMutableExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */; };
|
||||
3BBBA35D1903F8A700824E74 /* NSObject+MPStringTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */; };
|
||||
3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; };
|
||||
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; };
|
||||
3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA3941905704200824E74 /* MPRangeTests.m */; };
|
||||
@@ -37,7 +37,8 @@
|
||||
3BF9979118DE623E009CF6C4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9979018DE623E009CF6C4 /* XCTest.framework */; };
|
||||
3BF9979218DE623E009CF6C4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; };
|
||||
3BF9979A18DE623E009CF6C4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3BF9979818DE623E009CF6C4 /* InfoPlist.strings */; };
|
||||
3BFCFF491905AAF30001FE33 /* NSTextStorage+MPSetContents.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */; };
|
||||
3BFAC38F1997B61300B3EF67 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; };
|
||||
3BFAC39C1997BC7600B3EF67 /* NSString+MPExpressionElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -54,10 +55,14 @@
|
||||
3B0F69A719028BC600817707 /* MPException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPException.h; sourceTree = "<group>"; };
|
||||
3B0F69A819028C6000817707 /* MPException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPException.m; sourceTree = "<group>"; };
|
||||
3B0F69AB1902A82C00817707 /* MPExpressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionTests.m; sourceTree = "<group>"; };
|
||||
3B528D0D199417740054DB5F /* MPLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLayout.h; sourceTree = "<group>"; };
|
||||
3B528D0E199417E10054DB5F /* MPExpressionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionLayout.h; sourceTree = "<group>"; };
|
||||
3B528D0F199417E10054DB5F /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.m; sourceTree = "<group>"; };
|
||||
3B528D11199417E90054DB5F /* MPFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionLayout.h; sourceTree = "<group>"; };
|
||||
3B528D12199417E90054DB5F /* MPFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionLayout.m; sourceTree = "<group>"; };
|
||||
3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = "<group>"; };
|
||||
3B87E3541900856F00259938 /* MPExpressionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionView.h; sourceTree = "<group>"; };
|
||||
3B87E3551900856F00259938 /* MPExpressionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionView.m; sourceTree = "<group>"; };
|
||||
3B87E3571900857C00259938 /* MPExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpression.h; sourceTree = "<group>"; };
|
||||
3B87E3581900857C00259938 /* MPExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpression.m; sourceTree = "<group>"; };
|
||||
3B87E35B1900933200259938 /* MPRangePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRangePath.h; sourceTree = "<group>"; };
|
||||
3B87E35C1900933200259938 /* MPRangePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangePath.m; sourceTree = "<group>"; };
|
||||
3B87E35E19009D5F00259938 /* MPFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunction.h; sourceTree = "<group>"; };
|
||||
@@ -66,18 +71,11 @@
|
||||
3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionStorage.m; sourceTree = "<group>"; };
|
||||
3BB09EC71906FD830080A5ED /* MPSumFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunction.h; sourceTree = "<group>"; };
|
||||
3BB09EC81906FD830080A5ED /* MPSumFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunction.m; sourceTree = "<group>"; };
|
||||
3BB09ECE190713F00080A5ED /* MPExpressionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionLayout.h; sourceTree = "<group>"; };
|
||||
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.m; sourceTree = "<group>"; };
|
||||
3BB09ED1190713FC0080A5ED /* MPFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionLayout.h; sourceTree = "<group>"; };
|
||||
3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionLayout.m; sourceTree = "<group>"; };
|
||||
3BB09EDC190728220080A5ED /* NSIndexPath+MPReverseIndexPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPReverseIndexPath.h"; sourceTree = "<group>"; };
|
||||
3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPReverseIndexPath.m"; sourceTree = "<group>"; };
|
||||
3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPAdditions.h"; sourceTree = "<group>"; };
|
||||
3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPAdditions.m"; sourceTree = "<group>"; };
|
||||
3BB09EDF190736160080A5ED /* MPSumFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunctionLayout.h; sourceTree = "<group>"; };
|
||||
3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunctionLayout.m; sourceTree = "<group>"; };
|
||||
3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMutableExpressionTests.m; sourceTree = "<group>"; };
|
||||
3BBBA3591903EA9B00824E74 /* MPModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPModel.h; sourceTree = "<group>"; };
|
||||
3BBBA35B1903F8A700824E74 /* NSObject+MPStringTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MPStringTest.h"; sourceTree = "<group>"; };
|
||||
3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MPStringTest.m"; sourceTree = "<group>"; };
|
||||
3BBBA38419047FC900824E74 /* MPView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPView.h; sourceTree = "<group>"; };
|
||||
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangeTests.m; sourceTree = "<group>"; };
|
||||
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -99,8 +97,11 @@
|
||||
3BF9979018DE623E009CF6C4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||
3BF9979718DE623E009CF6C4 /* MathPadTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MathPadTests-Info.plist"; sourceTree = "<group>"; };
|
||||
3BF9979918DE623E009CF6C4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3BFCFF471905AAF30001FE33 /* NSTextStorage+MPSetContents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextStorage+MPSetContents.h"; sourceTree = "<group>"; };
|
||||
3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextStorage+MPSetContents.m"; sourceTree = "<group>"; };
|
||||
3BFAC38D1997B61300B3EF67 /* MPExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpression.h; sourceTree = "<group>"; };
|
||||
3BFAC38E1997B61300B3EF67 /* MPExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpression.m; sourceTree = "<group>"; };
|
||||
3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPExpressionElement.h; sourceTree = "<group>"; };
|
||||
3BFAC39A1997BC7600B3EF67 /* NSString+MPExpressionElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MPExpressionElement.h"; sourceTree = "<group>"; };
|
||||
3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MPExpressionElement.m"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -127,14 +128,18 @@
|
||||
3B87E350190082B300259938 /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3BBBA35A1903F89100824E74 /* Categories */,
|
||||
3BBBA3591903EA9B00824E74 /* MPModel.h */,
|
||||
3B87E3571900857C00259938 /* MPExpression.h */,
|
||||
3B87E3581900857C00259938 /* MPExpression.m */,
|
||||
3BFAC38D1997B61300B3EF67 /* MPExpression.h */,
|
||||
3BFAC38E1997B61300B3EF67 /* MPExpression.m */,
|
||||
3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */,
|
||||
3B87E35E19009D5F00259938 /* MPFunction.h */,
|
||||
3B87E35F19009D5F00259938 /* MPFunction.m */,
|
||||
3BFAC39A1997BC7600B3EF67 /* NSString+MPExpressionElement.h */,
|
||||
3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */,
|
||||
3B87E35B1900933200259938 /* MPRangePath.h */,
|
||||
3B87E35C1900933200259938 /* MPRangePath.m */,
|
||||
3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */,
|
||||
3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */,
|
||||
3B0F69A719028BC600817707 /* MPException.h */,
|
||||
3B0F69A819028C6000817707 /* MPException.m */,
|
||||
3BB09EBC1905EF210080A5ED /* Functions */,
|
||||
@@ -194,10 +199,12 @@
|
||||
3BB09ED819071C270080A5ED /* Private */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3BB09ECE190713F00080A5ED /* MPExpressionLayout.h */,
|
||||
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */,
|
||||
3BB09ED1190713FC0080A5ED /* MPFunctionLayout.h */,
|
||||
3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */,
|
||||
3B528D0D199417740054DB5F /* MPLayout.h */,
|
||||
3B688D9819982DF50006B4AB /* MPLayout.m */,
|
||||
3B528D0E199417E10054DB5F /* MPExpressionLayout.h */,
|
||||
3B528D0F199417E10054DB5F /* MPExpressionLayout.m */,
|
||||
3B528D11199417E90054DB5F /* MPFunctionLayout.h */,
|
||||
3B528D12199417E90054DB5F /* MPFunctionLayout.m */,
|
||||
);
|
||||
name = Private;
|
||||
sourceTree = "<group>";
|
||||
@@ -211,19 +218,6 @@
|
||||
name = "Function Layouts";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3BBBA35A1903F89100824E74 /* Categories */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3BBBA35B1903F8A700824E74 /* NSObject+MPStringTest.h */,
|
||||
3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */,
|
||||
3BFCFF471905AAF30001FE33 /* NSTextStorage+MPSetContents.h */,
|
||||
3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */,
|
||||
3BB09EDC190728220080A5ED /* NSIndexPath+MPReverseIndexPath.h */,
|
||||
3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */,
|
||||
);
|
||||
name = Categories;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3BF9976218DE623E009CF6C4 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -292,7 +286,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B0F69AB1902A82C00817707 /* MPExpressionTests.m */,
|
||||
3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */,
|
||||
3BBBA3941905704200824E74 /* MPRangeTests.m */,
|
||||
3BF9979618DE623E009CF6C4 /* Supporting Files */,
|
||||
);
|
||||
@@ -408,17 +401,17 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3B688D9919982DF50006B4AB /* MPLayout.m in Sources */,
|
||||
3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */,
|
||||
3B87E3561900856F00259938 /* MPExpressionView.m in Sources */,
|
||||
3BFCFF491905AAF30001FE33 /* NSTextStorage+MPSetContents.m in Sources */,
|
||||
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */,
|
||||
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */,
|
||||
3BF9977B18DE623E009CF6C4 /* main.m in Sources */,
|
||||
3B87E3591900857C00259938 /* MPExpression.m in Sources */,
|
||||
3BB09ED3190713FC0080A5ED /* MPFunctionLayout.m in Sources */,
|
||||
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */,
|
||||
3BBBA35D1903F8A700824E74 /* NSObject+MPStringTest.m in Sources */,
|
||||
3BB09EDE190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m in Sources */,
|
||||
3BFAC38F1997B61300B3EF67 /* MPExpression.m in Sources */,
|
||||
3BFAC39C1997BC7600B3EF67 /* NSString+MPExpressionElement.m in Sources */,
|
||||
3B528D10199417E10054DB5F /* MPExpressionLayout.m in Sources */,
|
||||
3B528D13199417E90054DB5F /* MPFunctionLayout.m in Sources */,
|
||||
3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */,
|
||||
3B87E36019009D5F00259938 /* MPFunction.m in Sources */,
|
||||
3B0F69A919028C6000817707 /* MPException.m in Sources */,
|
||||
3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */,
|
||||
@@ -430,13 +423,14 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3BBBA358190327B700824E74 /* MPMutableExpressionTests.m in Sources */,
|
||||
3B528D1619941F5B0054DB5F /* MPExpressionLayout.m in Sources */,
|
||||
3B528D1819941F5B0054DB5F /* MPFunctionLayout.m in Sources */,
|
||||
3B0F69AE1902AD2800817707 /* MPException.m in Sources */,
|
||||
3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */,
|
||||
3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */,
|
||||
3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */,
|
||||
3B53AD5F1997E0FB00C925C4 /* MPExpression.m in Sources */,
|
||||
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */,
|
||||
3B0F69AD1902AD2200817707 /* MPExpression.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13C1021" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
|
||||
</dependencies>
|
||||
@@ -7,7 +7,6 @@
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="MPDocument">
|
||||
<connections>
|
||||
<outlet property="resultExpressionView" destination="lcd-Ip-jjR" id="1NN-l5-30k"/>
|
||||
<outlet property="termExpressionView" destination="fqc-IQ-ceJ" id="br9-0u-qYk"/>
|
||||
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
@@ -17,22 +16,18 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="133" y="235" width="507" height="240"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1080"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
|
||||
<value key="minSize" type="size" width="94" height="86"/>
|
||||
<view key="contentView" id="gIp-Ho-8D9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="507" height="240"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="fqc-IQ-ceJ" customClass="MPExpressionView">
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView">
|
||||
<rect key="frame" x="20" y="124" width="467" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView">
|
||||
<rect key="frame" x="20" y="20" width="467" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IMg-L0-qdu">
|
||||
<rect key="frame" x="405" y="-7" width="88" height="32"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IMg-L0-qdu">
|
||||
<rect key="frame" x="209" y="13" width="88" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Change" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Xxz-j2-fsI">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
@@ -44,14 +39,12 @@
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="fqc-IQ-ceJ" secondAttribute="bottom" constant="8" symbolic="YES" id="GTC-QI-4wc"/>
|
||||
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" id="Pth-4c-GNR"/>
|
||||
<constraint firstAttribute="bottom" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="20" symbolic="YES" id="VNk-bs-YOH"/>
|
||||
<constraint firstAttribute="trailing" secondItem="fqc-IQ-ceJ" secondAttribute="trailing" constant="20" symbolic="YES" id="fER-QH-XqN"/>
|
||||
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="kpD-MX-yOS"/>
|
||||
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="to2-nb-CUV"/>
|
||||
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="leading" secondItem="lcd-Ip-jjR" secondAttribute="leading" id="xJa-Qd-Ytg"/>
|
||||
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="124" id="xrY-1x-QCm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" constant="20" symbolic="YES" id="3tX-J3-Wte"/>
|
||||
<constraint firstItem="IMg-L0-qdu" firstAttribute="top" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="83" id="62S-rU-IWO"/>
|
||||
<constraint firstItem="lcd-Ip-jjR" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="XN3-k3-tOU"/>
|
||||
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="gqS-BG-xpS"/>
|
||||
<constraint firstItem="IMg-L0-qdu" firstAttribute="centerX" secondItem="lcd-Ip-jjR" secondAttribute="centerX" id="sCM-Pj-4wd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="IMg-L0-qdu" secondAttribute="bottom" constant="20" symbolic="YES" id="tNe-R6-QlG"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
|
||||
@@ -59,9 +59,9 @@
|
||||
}
|
||||
|
||||
#pragma mark Actions
|
||||
|
||||
- (IBAction)changeExpression:(id)sender {
|
||||
[self.resultExpressionView.expressionStorage insertString:@"abc" atIndex:6];
|
||||
[self.resultExpressionView.expressionStorage insertElement:@"abc" atLocation:6];
|
||||
self.resultExpressionView.needsDisplay = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef MathPad_MPException_h
|
||||
#define MathPad_MPException_h
|
||||
|
||||
extern NSString *MPIllegalSymbolException;
|
||||
extern NSString *MPIllegalSymbolExceptionSymbolKey;
|
||||
extern NSString *MPIllegalElementException;
|
||||
extern NSString *MPIllegalElementExceptionElementKey;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
|
||||
#import "MPException.h"
|
||||
|
||||
NSString *MPIllegalSymbolException = @"MPIllegalSymbolException";
|
||||
NSString *MPIllegalSymbolExceptionSymbolKey = @"MPIllegalSymbolExceptionSymbolKey";
|
||||
NSString *MPIllegalElementException = @"MPIllegalSymbolException";
|
||||
NSString *MPIllegalElementExceptionElementKey = @"MPIllegalSymbolExceptionSymbolKey";
|
||||
@@ -2,124 +2,82 @@
|
||||
// MPExpression.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 17.04.14.
|
||||
// Created by Kim Wittenburg on 10.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
#import "NSString+MPExpressionElement.h"
|
||||
|
||||
@class MPExpression, MPFunction, MPRangePath;
|
||||
@protocol MPExpressionElement;
|
||||
|
||||
extern NSString *MPAdditionOperator;
|
||||
extern NSString *MPSubtractionOperator;
|
||||
extern NSString *MPMultiplicationOperator;
|
||||
extern NSString *MPDivisionOperator;
|
||||
|
||||
@interface MPExpression : NSObject <NSCopying, NSMutableCopying, NSCoding>
|
||||
@interface MPExpression : NSObject <NSCopying, NSCoding>
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)initWithSymbols:(NSArray *)symbols;
|
||||
- (instancetype)init; // Convenience
|
||||
- (instancetype)initWithElement:(id<MPExpressionElement>)element; // Convenience
|
||||
- (instancetype)initWithElements:(NSArray *)elements; // Designated Initializer
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
@property (nonatomic, weak) MPFunction *parent; // Set automatically, nil for root expression
|
||||
|
||||
@property (nonatomic, weak) MPFunction *parent; // Documentation: Do not set, may be nil
|
||||
|
||||
- (void)fixSymbols;
|
||||
- (void)fixElements; // Called automatically, removes empty elements, joins subsequent strings
|
||||
|
||||
#pragma mark Primitive Methods
|
||||
|
||||
- (NSUInteger)numberOfSymbols;
|
||||
- (id)symbolAtIndex:(NSUInteger)index; // Either an NSString or a MPFunction (which can be mutated)
|
||||
- (NSUInteger)length;
|
||||
- (NSUInteger)numberOfElements;
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index;
|
||||
- (NSArray *)elementsInRange:(NSRange)range;
|
||||
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element;
|
||||
- (void)replaceElementsInRange:(NSRange)range withElements:(NSArray *)elements;
|
||||
// TODO: - (NSUInteger)indexOfElementAtLocation:(NSUInteger)location;
|
||||
|
||||
- (double)doubleValue; // Evaluates Expression
|
||||
|
||||
#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
|
||||
// TODO: More notifications
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
|
||||
#pragma mark Basic NSObject Methods
|
||||
- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
|
||||
|
||||
- (NSString *)description;
|
||||
- (NSUInteger)hash;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPExpression (MPExpressionExtensionMethods)
|
||||
|
||||
+ (NSArray *)operators;
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (id)init;
|
||||
- (instancetype)initWithString:(NSString *)aString;
|
||||
- (instancetype)initWithFunction:(MPFunction *)aFunction;
|
||||
|
||||
+ (instancetype)expression;
|
||||
+ (instancetype)expressionWithString:(NSString *)aString;
|
||||
+ (instancetype)expressionWithFunction:(MPFunction *)aFunction;
|
||||
+ (instancetype)expressionWithSymbols:(NSArray *)symbols;
|
||||
@interface MPExpression (MPExpressionExtension)
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
- (NSUInteger)indexOfSymbol:(id)symbol;
|
||||
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath; // May also return MPExpression
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath; // Returns an MPExpression or id<MPExpressionElement>
|
||||
- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath;
|
||||
|
||||
#pragma mark Working With Expressions
|
||||
|
||||
- (NSUInteger)length;
|
||||
|
||||
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from;
|
||||
- (MPExpression *)subexpressionToIndex:(NSUInteger)to;
|
||||
- (MPExpression *)subexpressionFromLocation:(NSUInteger)from;
|
||||
- (MPExpression *)subexpressionToLocation:(NSUInteger)to;
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)range;
|
||||
|
||||
- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
|
||||
|
||||
- (MPExpression *)expressionByAppendingString:(NSString *)aString;
|
||||
- (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction;
|
||||
- (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression;
|
||||
- (MPExpression *)expressionByAppendingSymbols:(NSArray *)symbols;
|
||||
#pragma mark Mutating Expressions
|
||||
- (void)appendElement:(id<MPExpressionElement>)anElement;
|
||||
- (void)appendElements:(NSArray *)elements;
|
||||
|
||||
- (void)insertElement:(id<MPExpressionElement>)anElement atLocation:(NSUInteger)index;
|
||||
- (void)insertElements:(NSArray *)elements atLocation:(NSUInteger)index;
|
||||
|
||||
- (void)deleteElementsInRange:(NSRange)range;
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
|
||||
- (float)floatValue;
|
||||
- (int)intValue;
|
||||
- (NSInteger)integerValue;
|
||||
- (long long)longLongValue;
|
||||
|
||||
#pragma mark Querying an Expression's Contents
|
||||
|
||||
- (NSArray *)symbols;
|
||||
|
||||
- (NSString *)description;
|
||||
|
||||
- (NSUInteger)hash;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPExpression (MPChangeNotificationExtension)
|
||||
|
||||
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPMutableExpression : MPExpression
|
||||
|
||||
- (void)replaceSymbolsInRange:(NSRange)range
|
||||
withSymbols:(NSArray *)symbols;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPMutableExpression (MPMutableExpressionExtensionMethods)
|
||||
|
||||
- (void)insertString:(NSString *)aString
|
||||
atIndex:(NSUInteger)loc;
|
||||
- (void)insertFunction:(MPFunction *)aFunction
|
||||
atIndex:(NSUInteger)loc;
|
||||
- (void)insertExpression:(MPExpression *)anExpression
|
||||
atIndex:(NSUInteger)loc;
|
||||
- (void)insertSymbols:(NSArray *)symbols
|
||||
atIndex:(NSUInteger)loc;
|
||||
|
||||
- (void)deleteSymbolsInRange:(NSRange)range;
|
||||
|
||||
- (void)appendString:(NSString *)aString;
|
||||
- (void)appendFunction:(MPFunction *)aFunction;
|
||||
- (void)appendExpression:(MPExpression *)anExpression;
|
||||
- (void)appendSymbols:(NSArray *)symbols;
|
||||
|
||||
- (void)setString:(NSString *)aString;
|
||||
- (void)setFunction:(MPFunction *)aFunction;
|
||||
- (void)setExpression:(MPExpression *)anExpression;
|
||||
- (void)setSymbols:(NSArray *)symbols;
|
||||
#pragma mark Querying Expressions
|
||||
- (NSArray *)elements;
|
||||
|
||||
@end
|
||||
|
||||
@@ -2,88 +2,228 @@
|
||||
// MPExpression.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 17.04.14.
|
||||
// Created by Kim Wittenburg on 10.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPExpression.h"
|
||||
#import "MPFunction.h"
|
||||
#import "MPException.h"
|
||||
#import "MPRangePath.h"
|
||||
|
||||
#import "NSObject+MPStringTest.h"
|
||||
#import "NSIndexPath+MPReverseIndexPath.h"
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
#import "MPException.h"
|
||||
|
||||
NSString *MPAdditionOperator = @"+";
|
||||
NSString *MPSubtractionOperator = @"-";
|
||||
NSString *MPMultiplicationOperator = @"*";
|
||||
NSString *MPDivisionOperator = @"/";
|
||||
@interface MPExpression ()
|
||||
@property (readonly, nonatomic, strong) NSMutableArray *elements;
|
||||
@end
|
||||
|
||||
@interface MPExpression (MPExpressionPrivate)
|
||||
|
||||
- (NSInteger)lengthOfSymbol:(id)symbol;
|
||||
- (void)validateSymbols:(NSArray *)symbols;
|
||||
- (void)getSplitOffset:(out NSUInteger *)offset
|
||||
inSymbolAtIndex:(out NSUInteger *)symbolIndex
|
||||
forSplitLocation:(NSUInteger)loc;
|
||||
- (void)validateElements:(NSArray *)elements;
|
||||
- (BOOL)splitElementsAtLocation:(NSUInteger)location
|
||||
insertionIndex:(out NSUInteger *)insertionIndex;
|
||||
- (NSUInteger)calculateSplitOffsetForSplitLocation:(NSUInteger)location
|
||||
inElementAtIndex:(out NSUInteger *)elementIndex;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression (MPExpressionPrivate)
|
||||
|
||||
- (void)validateElements:(NSArray *)elements
|
||||
{
|
||||
for (id element in elements) {
|
||||
if (![element conformsToProtocol:@protocol(MPExpressionElement)]) {
|
||||
@throw [NSException exceptionWithName:MPIllegalElementException
|
||||
reason:@"Elements must conform to the MPExpressionElement protocol."
|
||||
userInfo:@{MPIllegalElementExceptionElementKey: element}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)splitElementsAtLocation:(NSUInteger)location
|
||||
insertionIndex:(out NSUInteger *)insertionIndex
|
||||
{
|
||||
if (location == 0) {
|
||||
*insertionIndex = 0;
|
||||
return NO;
|
||||
}
|
||||
NSUInteger splitElementIndex;
|
||||
NSUInteger splitOffset = [self calculateSplitOffsetForSplitLocation:location
|
||||
inElementAtIndex:&splitElementIndex];
|
||||
id<MPExpressionElement> splitElement = self.elements[splitElementIndex];
|
||||
if (splitOffset == splitElement.length) {
|
||||
splitOffset = 0;
|
||||
splitElementIndex++;
|
||||
}
|
||||
if (splitOffset != 0) {
|
||||
NSString *stringElement = (NSString *)splitElement;
|
||||
NSString *leftPart = [stringElement substringToIndex:splitOffset];
|
||||
NSString *rightPart = [stringElement substringFromIndex:splitOffset];
|
||||
[self.elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
|
||||
withObjectsFromArray:@[leftPart, rightPart]];
|
||||
++splitElementIndex;
|
||||
}
|
||||
*insertionIndex = splitElementIndex;
|
||||
return splitOffset != 0;
|
||||
}
|
||||
|
||||
- (NSUInteger)calculateSplitOffsetForSplitLocation:(NSUInteger)location
|
||||
inElementAtIndex:(out NSUInteger *)elementIndex
|
||||
{
|
||||
NSUInteger length = 0;
|
||||
NSUInteger index = 0;
|
||||
NSUInteger elementLength = 0;
|
||||
for (id<MPExpressionElement> element in self.elements) {
|
||||
elementLength = element.length;
|
||||
length += elementLength;
|
||||
if (length >= location) {
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
*elementIndex = index;
|
||||
return elementLength - (length - location);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression {
|
||||
@package
|
||||
__strong NSArray *_symbols;
|
||||
NSInteger _length;
|
||||
NSUInteger _cachedLength;
|
||||
NSRange editedRange;
|
||||
NSRange replacementRange;
|
||||
}
|
||||
|
||||
#pragma mark Creation Methods
|
||||
@synthesize elements = _elements;
|
||||
|
||||
- (instancetype)initWithSymbols:(NSArray *)symbols
|
||||
#pragma mark Creation Methods
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithElements:@[]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithElement:(id<MPExpressionElement>)element
|
||||
{
|
||||
return [self initWithElements:@[element]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithElements:(NSArray *)elements
|
||||
{
|
||||
[self validateSymbols:symbols];
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_symbols = [[NSArray alloc] initWithArray:symbols
|
||||
_cachedLength = 0;
|
||||
_elements = [[NSMutableArray alloc] initWithCapacity:elements.count];
|
||||
_elements = [[NSMutableArray alloc] initWithArray:elements
|
||||
copyItems:YES];
|
||||
[self fixSymbols];
|
||||
[self fixElements];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
- (void)fixSymbols
|
||||
- (void)fixElements
|
||||
{
|
||||
NSMutableArray *mutableSymbols = [_symbols mutableCopy];
|
||||
for (NSInteger index = 0; index < mutableSymbols.count; index++) {
|
||||
id next = index+1 < mutableSymbols.count ? mutableSymbols[index+1] : nil;
|
||||
id current = mutableSymbols[index];
|
||||
for (NSUInteger index = 0; index < self.elements.count; index++) {
|
||||
id<MPExpressionElement> next = index+1 < self.elements.count ? self.elements[index+1] :nil;
|
||||
id<MPExpressionElement> current = self.elements[index];
|
||||
if ([current isString]) {
|
||||
if ([current length] == 0) {
|
||||
[mutableSymbols removeObjectAtIndex:index];
|
||||
index--;
|
||||
if (current.length == 0) {
|
||||
[self.elements removeObjectAtIndex:index];
|
||||
if (index < replacementRange.location) {
|
||||
replacementRange.location--;
|
||||
} else if (index < NSMaxRange(replacementRange)-1) {
|
||||
replacementRange.length--;
|
||||
} else if (index == NSMaxRange(replacementRange)) {
|
||||
editedRange.length++;
|
||||
}
|
||||
--index;
|
||||
} else if ([next isString]) {
|
||||
NSString *new = [NSString stringWithFormat:@"%@%@", current, next];
|
||||
[mutableSymbols replaceObjectAtIndex:index withObject:new];
|
||||
[mutableSymbols removeObjectAtIndex:index+1];
|
||||
index--;
|
||||
[self.elements replaceObjectsInRange:NSMakeRange(index, 2)
|
||||
withObjectsFromArray:@[new]];
|
||||
if (index < replacementRange.location) {
|
||||
replacementRange.location--;
|
||||
} else if (index < NSMaxRange(replacementRange)-1) {
|
||||
replacementRange.length--;
|
||||
} else if (index == NSMaxRange(replacementRange)-1) {
|
||||
editedRange.length++;
|
||||
}
|
||||
--index;
|
||||
}
|
||||
} else {
|
||||
[(MPFunction *)current setParent:self];
|
||||
}
|
||||
}
|
||||
_symbols = [mutableSymbols copy];
|
||||
}
|
||||
|
||||
#pragma mark Primitive Methods
|
||||
|
||||
- (NSUInteger)numberOfSymbols
|
||||
- (NSUInteger)length
|
||||
{
|
||||
return [_symbols count];
|
||||
if (_cachedLength == 0) {
|
||||
for (id<MPExpressionElement> element in self.elements) {
|
||||
_cachedLength += element.length;
|
||||
}
|
||||
}
|
||||
return _cachedLength;
|
||||
}
|
||||
|
||||
- (id)symbolAtIndex:(NSUInteger)index
|
||||
- (NSUInteger)numberOfElements
|
||||
{
|
||||
return _symbols[index];
|
||||
return self.elements.count;
|
||||
}
|
||||
|
||||
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index
|
||||
{
|
||||
return self.elements[index];
|
||||
}
|
||||
|
||||
- (NSArray *)elementsInRange:(NSRange)range
|
||||
{
|
||||
return [self.elements subarrayWithRange:range];
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
|
||||
{
|
||||
return [self.elements indexOfObject:element];
|
||||
}
|
||||
|
||||
- (void)replaceElementsInRange:(NSRange)range
|
||||
withElements:(NSArray *)elements
|
||||
{
|
||||
if (NSMaxRange(range) > self.length) {
|
||||
@throw [NSException exceptionWithName:NSRangeException
|
||||
reason:@"Range out of bounds of expression"
|
||||
userInfo:nil];
|
||||
}
|
||||
[self validateElements:elements];
|
||||
|
||||
// Locate the position, split the elements
|
||||
NSUInteger startIndex;
|
||||
BOOL didSplitStart = NO;
|
||||
if ([self numberOfElements] == 0) {
|
||||
startIndex = 0;
|
||||
} else {
|
||||
didSplitStart = [self splitElementsAtLocation:range.location
|
||||
insertionIndex:&startIndex];
|
||||
}
|
||||
NSUInteger endIndex;
|
||||
BOOL didSplitEnd = [self splitElementsAtLocation:NSMaxRange(range)
|
||||
insertionIndex:&endIndex];
|
||||
|
||||
// Perform the replacement
|
||||
NSArray *newElements = [[NSArray alloc] initWithArray:elements
|
||||
copyItems:YES];
|
||||
[self.elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
|
||||
withObjectsFromArray:newElements];
|
||||
|
||||
|
||||
_cachedLength = 0;
|
||||
NSUInteger editingStart = startIndex - (didSplitStart?1:0);
|
||||
NSUInteger editingLength = endIndex - startIndex + (didSplitStart?1:0) + (didSplitEnd?1:0);
|
||||
editedRange = NSMakeRange(editingStart, editingLength);
|
||||
replacementRange = NSMakeRange(startIndex, elements.count);
|
||||
[self fixElements];
|
||||
MPRangePath *changePath = [[MPRangePath alloc] initWithRange:editedRange];
|
||||
[self didChangeElementsInRangePath:changePath replacementLength:replacementRange.length];
|
||||
}
|
||||
|
||||
- (double)doubleValue
|
||||
@@ -92,218 +232,18 @@ NSString *MPDivisionOperator = @"/";
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
#pragma mark Notifications
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
MPExpression *copy = [[MPExpression allocWithZone:zone] initWithSymbols:_symbols];
|
||||
return copy;
|
||||
NSUInteger selfIndex = [self.parent indexOfChild:self];
|
||||
MPRangePath *newPath = rangePath.copy;
|
||||
newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
|
||||
[self.parent didChangeElementsInRangePath:newPath
|
||||
replacementLength:replacementLength];
|
||||
}
|
||||
|
||||
#pragma mark - NSMutableCopying
|
||||
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone
|
||||
{
|
||||
MPMutableExpression *mutableCopy = [[MPMutableExpression allocWithZone:zone] initWithSymbols:_symbols];
|
||||
return mutableCopy;
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
// TODO: Test Coding
|
||||
return [self initWithSymbols:[aDecoder decodeObject]];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_symbols];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression (MPExpressionPrivate)
|
||||
|
||||
- (NSInteger)lengthOfSymbol:(id)symbol
|
||||
{
|
||||
if ([symbol isString]) {
|
||||
return [symbol length];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (void)validateSymbols:(NSArray *)symbols
|
||||
{
|
||||
for (id symbol in symbols) {
|
||||
if (!([symbol isString]
|
||||
|| [symbol isKindOfClass:[MPFunction class]])) {
|
||||
@throw [NSException exceptionWithName:MPIllegalSymbolException
|
||||
reason:@"Only NSString and MPFunction objects are valid symbols."
|
||||
userInfo:@{MPIllegalSymbolExceptionSymbolKey: symbol}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getSplitOffset:(out NSUInteger *)offset
|
||||
inSymbolAtIndex:(out NSUInteger *)symbolIndex
|
||||
forSplitLocation:(NSUInteger)loc
|
||||
{
|
||||
NSUInteger length = 0;
|
||||
NSUInteger index = 0;
|
||||
NSUInteger symbolLength = 0;
|
||||
for (id symbol in _symbols) {
|
||||
symbolLength = [self lengthOfSymbol:symbol];
|
||||
length += symbolLength;
|
||||
if (length >= loc) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
*offset = symbolLength - (length - loc);
|
||||
*symbolIndex = index;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression (MPExpressionExtensionMethods)
|
||||
|
||||
+ (NSArray *)operators
|
||||
{
|
||||
return @[MPAdditionOperator, MPSubtractionOperator, MPMultiplicationOperator, MPDivisionOperator];
|
||||
}
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithSymbols:@[]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithString:(NSString *)aString
|
||||
{
|
||||
return [self initWithSymbols:@[aString]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFunction:(MPFunction *)aFunction
|
||||
{
|
||||
return [self initWithSymbols:@[aFunction]];
|
||||
}
|
||||
|
||||
+ (instancetype)expression
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (instancetype)expressionWithString:(NSString *)aString
|
||||
{
|
||||
return [[self alloc] initWithString:aString];
|
||||
}
|
||||
|
||||
+ (instancetype)expressionWithFunction:(MPFunction *)aFunction
|
||||
{
|
||||
return [[self alloc] initWithFunction:aFunction];
|
||||
}
|
||||
|
||||
+ (instancetype)expressionWithSymbols:(NSArray *)symbols
|
||||
{
|
||||
return [[self alloc] initWithSymbols:symbols];
|
||||
}
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
- (NSUInteger)indexOfSymbol:(id)symbol
|
||||
{
|
||||
return [_symbols indexOfObject:symbol];
|
||||
}
|
||||
|
||||
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.length == 0) {
|
||||
return self;
|
||||
}
|
||||
id symbol = [self symbolAtIndex:[indexPath indexAtPosition:0]];
|
||||
if (indexPath.length == 1) {
|
||||
return symbol;
|
||||
}
|
||||
if ([symbol isKindOfClass:[MPFunction class]]) {
|
||||
return [symbol symbolAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark Working With Expressions
|
||||
|
||||
- (NSUInteger)length
|
||||
{
|
||||
if (_length == 0) {
|
||||
for (id symbol in _symbols) {
|
||||
_length += [self lengthOfSymbol:symbol];
|
||||
}
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
|
||||
{
|
||||
return [self subexpressionWithRange:NSMakeRange(from, [self length] - from)];
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
|
||||
{
|
||||
return [self subexpressionWithRange:NSMakeRange(0, to)];
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)range
|
||||
{
|
||||
if (NSMaxRange(range) > self.length) {
|
||||
@throw [NSException exceptionWithName:NSRangeException
|
||||
reason:@"Range outside bounds of expression."
|
||||
userInfo:nil];
|
||||
}
|
||||
if (range.location == self.length || NSMaxRange(range) == 0 || range.length == 0) {
|
||||
// Speed this up
|
||||
return [[MPExpression alloc] init];
|
||||
}
|
||||
NSUInteger startOffset;
|
||||
NSUInteger startSymbolIndex;
|
||||
[self getSplitOffset:&startOffset
|
||||
inSymbolAtIndex:&startSymbolIndex
|
||||
forSplitLocation:range.location];
|
||||
id startSymbol = _symbols[startSymbolIndex];
|
||||
if (startOffset == [self lengthOfSymbol:startSymbol]) {
|
||||
startOffset = 0;
|
||||
startSymbolIndex++;
|
||||
startSymbol = _symbols[startSymbolIndex];
|
||||
} else if ([startSymbol isString]) {
|
||||
startSymbol = [startSymbol substringFromIndex:startOffset];
|
||||
}
|
||||
NSUInteger endOffset;
|
||||
NSUInteger endSymbolIndex;
|
||||
[self getSplitOffset:&endOffset
|
||||
inSymbolAtIndex:&endSymbolIndex
|
||||
forSplitLocation:NSMaxRange(range)];
|
||||
id endSymbol = _symbols[endSymbolIndex];
|
||||
if ([endSymbol isString]) {
|
||||
endSymbol = [endSymbol substringToIndex:endOffset];
|
||||
|
||||
}
|
||||
|
||||
NSMutableArray *symbols = [[NSMutableArray alloc] initWithCapacity:endSymbolIndex-startSymbolIndex+1];
|
||||
[symbols addObject:startSymbol];
|
||||
if (endSymbolIndex > startSymbolIndex + 1) {
|
||||
NSInteger restLength = endSymbolIndex - startSymbolIndex - 1;
|
||||
[symbols addObjectsFromArray:[_symbols subarrayWithRange:NSMakeRange(startSymbolIndex+1, restLength)]];
|
||||
}
|
||||
if (endSymbolIndex > startSymbolIndex) {
|
||||
[symbols addObject:endSymbol];
|
||||
} else if (endSymbolIndex == startSymbolIndex && [startSymbol isString]) {
|
||||
NSString *result = [_symbols[startSymbolIndex] substringWithRange:NSMakeRange(startOffset, endOffset-startOffset)];
|
||||
[symbols replaceObjectAtIndex:0
|
||||
withObject:result];
|
||||
}
|
||||
return [[MPExpression alloc] initWithSymbols:symbols];
|
||||
}
|
||||
#pragma mark Basic NSObject Methods
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
@@ -321,32 +261,141 @@ NSString *MPDivisionOperator = @"/";
|
||||
|
||||
- (BOOL)isEqualToExpression:(MPExpression *)anExpression
|
||||
{
|
||||
// TODO: Use ->_symbols or .symbols
|
||||
return [_symbols isEqualToArray:anExpression->_symbols];
|
||||
return [self.elements isEqualToArray:anExpression.elements];
|
||||
}
|
||||
|
||||
- (MPExpression *)expressionByAppendingString:(NSString *)aString
|
||||
- (NSString *)description
|
||||
{
|
||||
return [self expressionByAppendingSymbols:@[aString]];
|
||||
NSMutableString *description = [[NSMutableString alloc] init];
|
||||
NSUInteger index = 0;
|
||||
for (id element in self.elements) {
|
||||
if ([element isString]) {
|
||||
NSMutableString *correctedSymbol = [[element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
|
||||
// Prefix operator
|
||||
if (element != self.elements[0]) {
|
||||
unichar prefix = [correctedSymbol characterAtIndex:0];
|
||||
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) {
|
||||
[correctedSymbol insertString:@"*"
|
||||
atIndex:0];
|
||||
}
|
||||
}
|
||||
// Suffix operator
|
||||
if (element != [self.elements lastObject]) {
|
||||
unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1];
|
||||
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) {
|
||||
[correctedSymbol appendString:@"*"];
|
||||
}
|
||||
}
|
||||
[description appendString:correctedSymbol];
|
||||
} else if (index > 0 && [self.elements[index-1] isKindOfClass:[MPFunction class]]) {
|
||||
[description appendFormat:@"*%@", [element description]];
|
||||
} else {
|
||||
[description appendString:[element description]];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
- (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return [self expressionByAppendingSymbols:@[aFunction]];
|
||||
return [self.elements hash];
|
||||
}
|
||||
|
||||
- (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression
|
||||
#pragma mark - NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return [self expressionByAppendingSymbols:anExpression.symbols];
|
||||
MPExpression *copy = [[MPExpression allocWithZone:zone] initWithElements:self.elements];
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (MPExpression *)expressionByAppendingSymbols:(NSArray *)symbols
|
||||
#pragma mark - NSCoding
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
return [[MPExpression alloc] initWithSymbols:[_symbols arrayByAddingObjectsFromArray:symbols]];
|
||||
// TODO: Test Coding
|
||||
return [self initWithElements:[aDecoder decodeObject]];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:self.elements];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression (MPExpressionExtension)
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.length == 0) {
|
||||
return self;
|
||||
}
|
||||
id<MPExpressionElement> element = [self elementAtIndex:[indexPath indexAtPosition:0]];
|
||||
if (indexPath.length == 1) {
|
||||
return element;
|
||||
}
|
||||
if ([element isFunction]) {
|
||||
return [(MPFunction *)element elementAtIndexPath:[indexPath indexPathByRemovingLastIndex]];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath
|
||||
{
|
||||
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
|
||||
return [targetExpression elementsInRange:rangePath.rangeAtLastIndex];
|
||||
}
|
||||
|
||||
#pragma mark Working With Expressions
|
||||
- (MPExpression *)subexpressionFromLocation:(NSUInteger)from
|
||||
{
|
||||
return [self subexpressionWithRange:NSMakeRange(from, self.length - from)];
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionToLocation:(NSUInteger)to
|
||||
{
|
||||
return [self subexpressionWithRange:NSMakeRange(0, to)];
|
||||
}
|
||||
|
||||
- (MPExpression *)subexpressionWithRange:(NSRange)range
|
||||
{
|
||||
MPExpression *subexpression = [self copy];
|
||||
NSRange preceedingRange = NSMakeRange(0, range.location);
|
||||
NSUInteger firstOut = NSMaxRange(range);
|
||||
NSRange exceedingRange = NSMakeRange(firstOut, self.length-firstOut);
|
||||
[subexpression deleteElementsInRange:exceedingRange];
|
||||
[subexpression deleteElementsInRange:preceedingRange];
|
||||
return subexpression;
|
||||
}
|
||||
|
||||
#pragma mark Mutating Expressions
|
||||
- (void)appendElement:(id<MPExpressionElement>)anElement
|
||||
{
|
||||
[self appendElements:@[anElement]];
|
||||
}
|
||||
|
||||
- (void)appendElements:(NSArray *)elements
|
||||
{
|
||||
[self replaceElementsInRange:NSMakeRange(self.length, 0) withElements:elements];
|
||||
}
|
||||
|
||||
- (void)insertElement:(id<MPExpressionElement>)anElement atLocation:(NSUInteger)index
|
||||
{
|
||||
[self insertElements:@[anElement] atLocation:index];
|
||||
}
|
||||
|
||||
- (void)insertElements:(NSArray *)elements atLocation:(NSUInteger)index
|
||||
{
|
||||
[self replaceElementsInRange:NSMakeRange(index, 0) withElements:elements];
|
||||
}
|
||||
|
||||
- (void)deleteElementsInRange:(NSRange)range
|
||||
{
|
||||
[self replaceElementsInRange:range withElements:@[]];
|
||||
}
|
||||
|
||||
#pragma mark Evaluating Expressions
|
||||
|
||||
- (float)floatValue
|
||||
{
|
||||
return (float)[self doubleValue];
|
||||
@@ -367,290 +416,4 @@ NSString *MPDivisionOperator = @"/";
|
||||
return (long long)[self doubleValue];
|
||||
}
|
||||
|
||||
#pragma mark Querying an Expression's Contents
|
||||
|
||||
- (NSArray *)symbols
|
||||
{
|
||||
// _symbols is immutable so it is ok to just return it instead of making a copy
|
||||
return _symbols;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSMutableString *description = [[NSMutableString alloc] init];
|
||||
NSUInteger index = 0;
|
||||
for (id symbol in _symbols) {
|
||||
if ([symbol isString]) {
|
||||
NSMutableString *correctedSymbol = [[symbol stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
|
||||
// Prefix operator
|
||||
if (symbol != _symbols[0]) {
|
||||
unichar prefix = [correctedSymbol characterAtIndex:0];
|
||||
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) {
|
||||
[correctedSymbol insertString:@"*"
|
||||
atIndex:0];
|
||||
}
|
||||
}
|
||||
// Suffix operator
|
||||
if (symbol != [_symbols lastObject]) {
|
||||
unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1];
|
||||
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) {
|
||||
[correctedSymbol appendString:@"*"];
|
||||
}
|
||||
}
|
||||
[description appendString:correctedSymbol];
|
||||
} else if (index > 0 && [_symbols[index-1] isKindOfClass:[MPFunction class]]) {
|
||||
[description appendFormat:@"*%@", [symbol description]];
|
||||
} else {
|
||||
[description appendString:[symbol description]];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return [_symbols hash];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpression (MPChangeNotificationExtension)
|
||||
|
||||
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length
|
||||
{
|
||||
if (!self.parent) {
|
||||
return;
|
||||
}
|
||||
NSUInteger selfIndex = [self.parent indexOfChild:self];
|
||||
NSIndexPath *newLocation = [rangePath.location indexPathByPrecedingIndex:selfIndex];
|
||||
MPRangePath *newPath = [[MPRangePath alloc] initWithLocation:newLocation length:rangePath.length];
|
||||
[self.parent symbolsChangedInRangePath:newPath replacementLength:length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPMutableExpression {
|
||||
NSRange editedRange;
|
||||
NSRange replacementRange;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSymbols:(NSArray *)symbols
|
||||
{
|
||||
[self validateSymbols:symbols];
|
||||
self = [super initWithSymbols:nil];
|
||||
if (self) {
|
||||
editedRange = NSMakeRange(0, 0);
|
||||
replacementRange = NSMakeRange(0, 0);
|
||||
_symbols = [[NSMutableArray alloc] initWithArray:symbols
|
||||
copyItems:YES];
|
||||
[self fixSymbols];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)fixSymbols
|
||||
{
|
||||
NSMutableArray *mutableSymbols = (NSMutableArray *)_symbols;
|
||||
for (NSInteger index = 0; index < mutableSymbols.count; index++) {
|
||||
id next = index+1 < mutableSymbols.count ? mutableSymbols[index+1] : nil;
|
||||
id current = mutableSymbols[index];
|
||||
if ([current isString]) {
|
||||
if ([current length] == 0) {
|
||||
[mutableSymbols removeObjectAtIndex:index];
|
||||
if (index < replacementRange.location) {
|
||||
replacementRange.location--;
|
||||
} else if (index < NSMaxRange(replacementRange)-1) {
|
||||
replacementRange.length--;
|
||||
} else if (index == NSMaxRange(replacementRange)) {
|
||||
editedRange.length++;
|
||||
}
|
||||
index--;
|
||||
} else if ([next isString]) {
|
||||
NSString *new = [NSString stringWithFormat:@"%@%@", current, next];
|
||||
[mutableSymbols replaceObjectAtIndex:index withObject:new];
|
||||
[mutableSymbols removeObjectAtIndex:index+1];
|
||||
if (index < replacementRange.location) {
|
||||
replacementRange.location--;
|
||||
} else if (index < NSMaxRange(replacementRange)-1) {
|
||||
replacementRange.length--;
|
||||
} else if (index == NSMaxRange(replacementRange)-1) {
|
||||
editedRange.length++;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
} else {
|
||||
[(MPFunction *)current setParent:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)symbols
|
||||
{
|
||||
// Return an immutable array:
|
||||
return [_symbols copy];
|
||||
}
|
||||
|
||||
- (void)replaceSymbolsInRange:(NSRange)range
|
||||
withSymbols:(NSArray *)symbols
|
||||
{
|
||||
if (NSMaxRange(range) > self.length) {
|
||||
@throw [NSException exceptionWithName:NSRangeException
|
||||
reason:@"Range out of bounds of expression."
|
||||
userInfo:nil];
|
||||
}
|
||||
[self validateSymbols:symbols];
|
||||
|
||||
// Locate the position, split the symbols
|
||||
NSUInteger startIndex;
|
||||
BOOL didSplitStart = NO;
|
||||
if ([self numberOfSymbols] == 0) {
|
||||
startIndex = 0;
|
||||
} else {
|
||||
[self splitSymbolsAtLocation:range.location
|
||||
insertionIndex:&startIndex
|
||||
didSplit:&didSplitStart];
|
||||
}
|
||||
|
||||
// Perform the deletion
|
||||
NSUInteger endIndex;
|
||||
BOOL didSplitEnd = NO;
|
||||
[self splitSymbolsAtLocation:NSMaxRange(range)
|
||||
insertionIndex:&endIndex
|
||||
didSplit:&didSplitEnd];
|
||||
if (range.length > 0) {
|
||||
NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
|
||||
for (NSUInteger index = startIndex; index < endIndex; index++) {
|
||||
[indexes addIndex:index];
|
||||
}
|
||||
// TODO: Replace with removeObjectsInRange:
|
||||
[(NSMutableArray *)_symbols removeObjectsAtIndexes:indexes];
|
||||
}
|
||||
|
||||
// Perform the insertion
|
||||
if (symbols.count > 0) {
|
||||
NSArray *newSymbols = [[NSArray alloc] initWithArray:symbols copyItems:YES];
|
||||
[(NSMutableArray *)_symbols replaceObjectsInRange:NSMakeRange(startIndex, 0)
|
||||
withObjectsFromArray:newSymbols];
|
||||
}
|
||||
|
||||
// Invalidate length and revalidate structure
|
||||
_length = 0;
|
||||
NSUInteger editingStart = startIndex - (didSplitStart?1:0);
|
||||
NSUInteger editingLength = endIndex - startIndex + (didSplitStart?1:0) + (didSplitEnd?1:0);
|
||||
editedRange = NSMakeRange(editingStart, editingLength);
|
||||
replacementRange = NSMakeRange(startIndex, symbols.count);
|
||||
[self fixSymbols];
|
||||
MPRangePath *changePath = [[MPRangePath alloc] initWithRange:editedRange];
|
||||
[self symbolsChangedInRangePath:changePath replacementLength:replacementRange.length];
|
||||
}
|
||||
|
||||
- (void)splitSymbolsAtLocation:(NSUInteger)loc
|
||||
insertionIndex:(out NSUInteger *)insertionIndex
|
||||
didSplit:(out BOOL *)flag;
|
||||
{
|
||||
NSUInteger splitSymbolIndex;
|
||||
NSUInteger splitOffset;
|
||||
[self getSplitOffset:&splitOffset
|
||||
inSymbolAtIndex:&splitSymbolIndex
|
||||
forSplitLocation:loc];
|
||||
id splitSymbol = _symbols[splitSymbolIndex];
|
||||
NSInteger splitSymbolLength = [self lengthOfSymbol:splitSymbol];
|
||||
if (splitOffset == splitSymbolLength) {
|
||||
splitOffset = 0;
|
||||
splitSymbolIndex++;
|
||||
}
|
||||
if (splitOffset != 0) {
|
||||
NSString *leftPart = [splitSymbol substringToIndex:splitOffset];
|
||||
NSString *rightPart = [splitSymbol substringFromIndex:splitOffset];
|
||||
[(NSMutableArray *)_symbols replaceObjectAtIndex:splitSymbolIndex
|
||||
withObject:leftPart];
|
||||
splitSymbolIndex++;
|
||||
[(NSMutableArray *)_symbols insertObject:rightPart
|
||||
atIndex:splitSymbolIndex];
|
||||
}
|
||||
*flag = splitOffset != 0;
|
||||
*insertionIndex = splitSymbolIndex;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPMutableExpression (MPMutableExpressionExtensionMethods)
|
||||
|
||||
- (void)insertString:(NSString *)aString
|
||||
atIndex:(NSUInteger)loc
|
||||
{
|
||||
[self insertSymbols:@[aString]
|
||||
atIndex:loc];
|
||||
}
|
||||
|
||||
- (void)insertFunction:(MPFunction *)aFunction
|
||||
atIndex:(NSUInteger)loc
|
||||
{
|
||||
[self insertSymbols:@[aFunction]
|
||||
atIndex:loc];
|
||||
}
|
||||
|
||||
- (void)insertExpression:(MPExpression *)anExpression
|
||||
atIndex:(NSUInteger)loc
|
||||
{
|
||||
[self insertSymbols:anExpression.symbols
|
||||
atIndex:loc];
|
||||
}
|
||||
|
||||
- (void)insertSymbols:(NSArray *)symbols
|
||||
atIndex:(NSUInteger)loc
|
||||
{
|
||||
[self replaceSymbolsInRange:NSMakeRange(loc, 0)
|
||||
withSymbols:symbols];
|
||||
}
|
||||
|
||||
- (void)deleteSymbolsInRange:(NSRange)range
|
||||
{
|
||||
[self replaceSymbolsInRange:range
|
||||
withSymbols:@[]];
|
||||
}
|
||||
|
||||
- (void)appendString:(NSString *)aString
|
||||
{
|
||||
[self appendSymbols:@[aString]];
|
||||
}
|
||||
|
||||
- (void)appendFunction:(MPFunction *)aFunction
|
||||
{
|
||||
[self appendSymbols:@[aFunction]];
|
||||
}
|
||||
|
||||
- (void)appendExpression:(MPExpression *)anExpression
|
||||
{
|
||||
[self appendSymbols:anExpression.symbols];
|
||||
}
|
||||
|
||||
- (void)appendSymbols:(NSArray *)symbols
|
||||
{
|
||||
[self replaceSymbolsInRange:NSMakeRange(self.length, 0)
|
||||
withSymbols:symbols];
|
||||
}
|
||||
|
||||
- (void)setString:(NSString *)aString
|
||||
{
|
||||
[self setSymbols:@[aString]];
|
||||
}
|
||||
|
||||
- (void)setFunction:(MPFunction *)aFunction
|
||||
{
|
||||
[self setSymbols:@[aFunction]];
|
||||
}
|
||||
|
||||
- (void)setExpression:(MPExpression *)anExpression
|
||||
{
|
||||
[self setSymbols:anExpression.symbols];
|
||||
}
|
||||
|
||||
- (void)setSymbols:(NSArray *)symbols
|
||||
{
|
||||
[self replaceSymbolsInRange:NSMakeRange(0, self.length)
|
||||
withSymbols:symbols];
|
||||
}
|
||||
|
||||
@end
|
||||
24
MathPad/MPExpressionElement.h
Normal file
24
MathPad/MPExpressionElement.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// MPExpressionElement.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@protocol MPExpressionElement <NSObject, NSCoding>
|
||||
|
||||
- (BOOL)isString;
|
||||
- (BOOL)isFunction;
|
||||
|
||||
- (NSUInteger)length;
|
||||
|
||||
- (double)doubleValue;
|
||||
- (float)floatValue;
|
||||
- (int)intValue;
|
||||
- (NSInteger)integerValue;
|
||||
- (long long)longLongValue;
|
||||
|
||||
@end
|
||||
@@ -2,64 +2,18 @@
|
||||
// MPExpressionLayout.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 22.04.14.
|
||||
// Created by Kim Wittenburg on 07.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@import Cocoa;
|
||||
#import "MPLayout.h"
|
||||
#import "MPExpression.h"
|
||||
|
||||
@class MPExpressionLayout, MPFunctionLayout, MPExpressionStorage, MPExpressionView, MPExpression;
|
||||
@interface MPExpressionLayout : MPLayout
|
||||
|
||||
@interface MPExpressionLayout : NSObject {
|
||||
BOOL _valid;
|
||||
NSSize _cachedSize;
|
||||
NSMutableArray *_symbolCache;
|
||||
}
|
||||
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
// -init not supported
|
||||
- (id)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
|
||||
- (id)initWithExpressionPath:(NSIndexPath *)expressionPath
|
||||
parent:(MPFunctionLayout *)parent;
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
@property (readonly, nonatomic, weak) MPFunctionLayout *parent;
|
||||
|
||||
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
|
||||
@property (readonly, nonatomic, strong) NSIndexPath *expressionPath;
|
||||
|
||||
@property (nonatomic, weak) MPExpressionView *expressionView;
|
||||
|
||||
- (MPExpression *)expression; // Convenience
|
||||
- (NSLayoutManager *)layoutManager;
|
||||
- (NSTextContainer *)textContainer;
|
||||
- (NSTextStorage *)textStorage;
|
||||
|
||||
#pragma mark Cache Methods
|
||||
|
||||
- (void)invalidate;
|
||||
- (void)editedExpressionInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)length;
|
||||
|
||||
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index;
|
||||
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
|
||||
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index;
|
||||
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index;
|
||||
|
||||
#pragma mark Sizes Calculation Methods
|
||||
|
||||
- (NSSize)sizeForAllSymbols;
|
||||
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index;
|
||||
- (NSSize)sizeForSymbolsInRange:(NSRange)range;
|
||||
|
||||
#pragma mark Drawing Methods
|
||||
|
||||
- (void)drawSymbolAtIndex:(NSUInteger)index
|
||||
atPoint:(NSPoint)point;
|
||||
- (void)drawSymbolsInRange:(NSRange)range
|
||||
atPoint:(NSPoint)point;
|
||||
- (void)drawAllSymbolsAtPoint:(NSPoint)point;
|
||||
@property (readonly, nonatomic, weak) MPExpression *expression; // Convenience
|
||||
|
||||
@end
|
||||
|
||||
@@ -2,45 +2,69 @@
|
||||
// MPExpressionLayout.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 22.04.14.
|
||||
// Created by Kim Wittenburg on 07.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPExpressionLayout.h"
|
||||
#import "MPExpressionStorage.h"
|
||||
#import "MPFunctionLayout.h"
|
||||
#import "MPModel.h"
|
||||
#import "MPExpressionView.h"
|
||||
|
||||
@interface MPExpressionLayout (MPPathGeneration)
|
||||
|
||||
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index;
|
||||
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpressionLayout (MPPathGeneration)
|
||||
|
||||
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
id symbol = [self.expression elementAtIndex:index];
|
||||
if ([symbol isString]) {
|
||||
return [self cachableObjectForIndex:index
|
||||
generator:^id{
|
||||
return [self generateBezierPathForString:symbol];
|
||||
}];
|
||||
} else {
|
||||
MPLayout *layout = [self childLayoutAtIndex:index];
|
||||
return layout.bezierPath;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString
|
||||
{
|
||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
|
||||
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
||||
self.textStorage.attributedString = text;
|
||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
||||
NSGlyph glyphs[glyphRange.length+1];
|
||||
NSUInteger actualGlyphCount = [self.layoutManager getGlyphs:glyphs
|
||||
range:glyphRange];
|
||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
||||
[path moveToPoint:NSZeroPoint];
|
||||
[path appendBezierPathWithGlyphs:glyphs
|
||||
count:actualGlyphCount
|
||||
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPExpressionLayout
|
||||
|
||||
# pragma mark Creation Methods
|
||||
|
||||
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_symbolCache = [[NSMutableArray alloc] init];
|
||||
_expressionStorage = expressionStorage;
|
||||
_expressionPath = [[NSIndexPath alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithExpressionPath:(NSIndexPath *)expressionPath
|
||||
parent:(MPFunctionLayout *)parent
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_symbolCache = [[NSMutableArray alloc] init];
|
||||
_expressionPath = expressionPath;
|
||||
_parent = parent;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
@synthesize expressionStorage = _expressionStorage;
|
||||
- (MPExpressionStorage *)expressionStorage
|
||||
{
|
||||
@@ -52,172 +76,50 @@
|
||||
|
||||
- (MPExpression *)expression
|
||||
{
|
||||
return [self.expressionStorage symbolAtIndexPath:self.expressionPath];
|
||||
}
|
||||
|
||||
- (NSLayoutManager *)layoutManager
|
||||
{
|
||||
return self.expressionStorage.layoutManager;
|
||||
}
|
||||
|
||||
- (NSTextContainer *)textContainer
|
||||
{
|
||||
return self.expressionStorage.textContainer;
|
||||
}
|
||||
|
||||
- (NSTextStorage *)textStorage
|
||||
{
|
||||
return self.expressionStorage.textStorage;
|
||||
return [self.expressionStorage elementAtIndexPath:self.path];
|
||||
}
|
||||
|
||||
#pragma mark Cache Methods
|
||||
|
||||
// TODO: Return nil from caching with illegal index
|
||||
|
||||
- (void)invalidate
|
||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||
{
|
||||
_valid = NO;
|
||||
[self.parent invalidate];
|
||||
[self.expressionView setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)editedExpressionInRange:(NSRange)range replacementLength:(NSUInteger)length
|
||||
{
|
||||
// TODO: New symbols may also be inserted in the middle or at the beginning
|
||||
NSInteger changeInLength = length - range.length;
|
||||
while (_symbolCache.count < (self.expression.numberOfSymbols + changeInLength)) {
|
||||
[_symbolCache addObject:[NSNull null]];
|
||||
}
|
||||
NSMutableArray *newPlaceholders = [[NSMutableArray alloc] initWithCapacity:length];
|
||||
while (newPlaceholders.count < length) {
|
||||
[newPlaceholders addObject:[NSNull null]];
|
||||
}
|
||||
[_symbolCache replaceObjectsInRange:range withObjectsFromArray:newPlaceholders];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index >= _symbolCache.count) {
|
||||
return NO;
|
||||
}
|
||||
return _symbolCache[index] != [NSNull null];
|
||||
}
|
||||
|
||||
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
|
||||
{
|
||||
if ([self hasCacheForSymbolAtIndex:index]) {
|
||||
id cacheObject = _symbolCache[index];
|
||||
if ([cacheObject isKindOfClass:[NSValue class]]) {
|
||||
id cachedObject = [self cachableObjectForIndex:index generator:^id{
|
||||
NSIndexPath *indexPath = [self.path indexPathByAddingIndex:index];
|
||||
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
|
||||
parent:self];
|
||||
return layout;
|
||||
}];
|
||||
if ([cachedObject isKindOfClass:[NSBezierPath class]]) {
|
||||
return nil;
|
||||
}
|
||||
return cacheObject;
|
||||
}
|
||||
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:[self.expressionPath indexPathByAddingIndex:index] parent:self];
|
||||
while (index >= _symbolCache.count) {
|
||||
[_symbolCache addObject:[NSNull null]];
|
||||
}
|
||||
_symbolCache[index] = layout;
|
||||
return layout;
|
||||
return cachedObject;
|
||||
}
|
||||
|
||||
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index
|
||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
id cachedSymbol = _symbolCache[index];
|
||||
if ([cachedSymbol isKindOfClass:[NSValue class]]) {
|
||||
return [cachedSymbol sizeValue];
|
||||
}
|
||||
return [(MPFunctionLayout *)cachedSymbol sizeOfFunction];
|
||||
}
|
||||
|
||||
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index
|
||||
{
|
||||
while (index >= _symbolCache.count) {
|
||||
[_symbolCache addObject:[NSNull null]];
|
||||
}
|
||||
_symbolCache[index] = [NSValue valueWithSize:size];
|
||||
}
|
||||
|
||||
#pragma mark Size Calculation Methods
|
||||
|
||||
- (NSSize)sizeForAllSymbols
|
||||
{
|
||||
if (!_valid) {
|
||||
_cachedSize = [self sizeForSymbolsInRange:NSMakeRange(0, self.expression.numberOfSymbols)];
|
||||
_valid = YES;
|
||||
}
|
||||
return _cachedSize;
|
||||
}
|
||||
|
||||
- (NSSize)sizeForSymbolsInRange:(NSRange)range
|
||||
{
|
||||
NSSize size = NSMakeSize(0, 0);
|
||||
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
|
||||
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
|
||||
size.width += symbolSize.width;
|
||||
size.height = MAX(size.height, symbolSize.height);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index
|
||||
{
|
||||
if ([self hasCacheForSymbolAtIndex:index]) {
|
||||
return [self cachedSizeForSymbolAtIndex:index];
|
||||
}
|
||||
id symbol = [self.expression symbolAtIndex:index];
|
||||
id symbol = [self.expression elementAtIndex:index];
|
||||
if ([symbol isString]) {
|
||||
[self.textStorage setString:symbol];
|
||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
||||
NSSize symbolSize = [self.layoutManager boundingRectForGlyphRange:glyphRange
|
||||
inTextContainer:self.textContainer].size;
|
||||
[self cacheSize:symbolSize
|
||||
forSymbolAtIndex:index];
|
||||
|
||||
return symbolSize;
|
||||
return [self bezierPathForChildAtIndex:index].bounds.size;
|
||||
} else {
|
||||
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
|
||||
return [layout sizeOfFunction];
|
||||
return [self childLayoutAtIndex:index].size;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Drawing Methods
|
||||
|
||||
- (void)drawSymbolAtIndex:(NSUInteger)index
|
||||
atPoint:(NSPoint)point
|
||||
- (NSBezierPath *)generateBezierPath
|
||||
{
|
||||
id symbol = [self.expression symbolAtIndex:index];
|
||||
// point.x = point.y = 0;
|
||||
NSLog(@"draw Symbol: %@ at Point: (x: %f, y: %f)", symbol, point.x, point.y);
|
||||
if ([symbol isString]) {
|
||||
[self.textStorage setString:symbol];
|
||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
||||
[self.layoutManager drawGlyphsForGlyphRange:glyphRange
|
||||
atPoint:point];
|
||||
} else {
|
||||
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
|
||||
NSLog(@"layout: %@, index: %ld", layout, index);
|
||||
[layout drawFunctionAtPoint:point];
|
||||
NSBezierPath *fullPath = [NSBezierPath bezierPath];
|
||||
[fullPath moveToPoint:NSZeroPoint];
|
||||
NSUInteger x = 0;
|
||||
for (NSInteger index = 0; index < self.expression.numberOfElements; ++index) {
|
||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
||||
// TODO: Translate by the right amount
|
||||
[transform translateXBy:x yBy:0];
|
||||
NSBezierPath *path = [self bezierPathForChildAtIndex:index].copy;
|
||||
[path transformUsingAffineTransform:transform];
|
||||
[fullPath appendBezierPath:path];
|
||||
x += path.bounds.size.width;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawSymbolsInRange:(NSRange)range
|
||||
atPoint:(NSPoint)point
|
||||
{
|
||||
NSSize overallSize = [self sizeForSymbolsInRange:range];
|
||||
CGFloat x = point.x;
|
||||
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
|
||||
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
|
||||
CGFloat dy = (overallSize.height - symbolSize.height) / 2;
|
||||
[self drawSymbolAtIndex:index
|
||||
atPoint:NSMakePoint(x, point.y + dy)];
|
||||
x += symbolSize.width;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawAllSymbolsAtPoint:(NSPoint)point
|
||||
{
|
||||
[self drawSymbolsInRange:NSMakeRange(0, [self.expression numberOfSymbols]) atPoint:point];
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
@class MPExpressionStorage, MPExpressionLayout;
|
||||
|
||||
@interface MPExpressionStorage : MPMutableExpression
|
||||
@interface MPExpressionStorage : MPExpression
|
||||
|
||||
@property (nonatomic, strong) MPExpressionLayout *expressionLayout;
|
||||
@property (nonatomic, strong) MPExpressionLayout *rootLayout;
|
||||
|
||||
- (NSLayoutManager *)layoutManager;
|
||||
- (NSTextContainer *)textContainer;
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
@implementation MPExpressionStorage
|
||||
|
||||
- (instancetype)initWithSymbols:(NSArray *)symbols
|
||||
- (instancetype)initWithElements:(NSArray *)elements
|
||||
{
|
||||
self = [super initWithSymbols:symbols];
|
||||
self = [super initWithElements:elements];
|
||||
if (self) {
|
||||
_expressionLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self];
|
||||
_rootLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -59,24 +59,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
if (rangePath.location.length == 0) {
|
||||
return;
|
||||
}
|
||||
id current = self.expressionLayout;
|
||||
for (NSUInteger position = 1; position < rangePath.location.length-1; position++) {
|
||||
if ([current isKindOfClass:[MPExpressionLayout class]]) {
|
||||
current = [(MPExpressionLayout *)current functionLayoutForFunctionAtIndex:position];
|
||||
} else {
|
||||
current = [(MPFunctionLayout *)current expressionLayoutForChildAtIndex:position];
|
||||
}
|
||||
}
|
||||
if ([current isKindOfClass:[MPExpressionLayout class]]) {
|
||||
[(MPExpressionLayout *)current editedExpressionInRange:rangePath.rangeAtLastIndex replacementLength:length];
|
||||
} else {
|
||||
[(MPFunctionLayout *)current editedChildAtIndex:[rangePath.location indexAtPosition:rangePath.location.length-1]];
|
||||
MPLayout *current = self.rootLayout;
|
||||
for (NSUInteger index = 1; index < rangePath.location.length-1; index++) {
|
||||
current = [current childLayoutAtIndex:index];
|
||||
}
|
||||
[current clearCacheInRange:rangePath.rangeAtLastIndex replacementLength:replacementLength];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#pragma mark Properties
|
||||
|
||||
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
|
||||
- (MPExpressionLayout *)expressionLayout; // Convenience Method
|
||||
|
||||
@property (nonatomic, getter = isEditable) BOOL editable;
|
||||
@property (nonatomic, strong) MPRangePath *selection;
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
//
|
||||
|
||||
#import "MPExpressionView.h"
|
||||
#import "MPExpressionLayout.h"
|
||||
#import "MPExpressionStorage.h"
|
||||
|
||||
#import "NSObject+MPStringTest.h"
|
||||
#import "MPExpressionLayout.h"
|
||||
|
||||
#import "MPSumFunction.h"
|
||||
|
||||
@interface MPExpressionView (MPCursor)
|
||||
@property (nonatomic, strong) NSTimer *cursorTimer;
|
||||
@end
|
||||
|
||||
@implementation MPExpressionView
|
||||
|
||||
#pragma mark Creation Methods
|
||||
@@ -47,23 +49,20 @@
|
||||
|
||||
- (void)initializeObjects
|
||||
{
|
||||
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithSymbols:@[@"12 345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
|
||||
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
|
||||
_expressionStorage = expressionStorage;
|
||||
[self.expressionLayout setExpressionView:self];
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
[_expressionStorage.expressionLayout setExpressionView:nil];
|
||||
_expressionStorage = expressionStorage;
|
||||
[_expressionStorage.expressionLayout setExpressionView:self];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (MPExpressionLayout *)expressionLayout
|
||||
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
|
||||
{
|
||||
return self.expressionStorage.expressionLayout;
|
||||
_expressionStorage = expressionStorage;
|
||||
}
|
||||
|
||||
#pragma mark Drawing Methods
|
||||
@@ -74,11 +73,10 @@
|
||||
[[NSColor whiteColor] set];
|
||||
NSRectFill(self.bounds);
|
||||
[[NSColor blackColor] set];
|
||||
NSSize expressionSize = [self.expressionLayout sizeForAllSymbols];
|
||||
NSSize expressionSize = [self.expressionStorage.rootLayout size];
|
||||
CGFloat y = (self.bounds.size.height - expressionSize.height) / 2;
|
||||
NSLog(@"%f", self.bounds.origin.y);
|
||||
NSPoint point = NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
|
||||
[self.expressionLayout drawAllSymbolsAtPoint:point];
|
||||
[self.expressionStorage.rootLayout drawAtPoint:point];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,58 +6,47 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
#import "MPExpressionElement.h"
|
||||
|
||||
@class MPFunction, MPExpression, MPRangePath;
|
||||
|
||||
@interface MPFunction : NSObject <NSCopying, NSCoding>
|
||||
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
#pragma mark Properties
|
||||
// Subclasses should define accessor properties for all sub expressions, which, in the setter, should send didChangeElementAtRangePath:replacementLength: to self with a replacement length of 1.
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
@property (nonatomic, weak) MPExpression *parent; // Documentation: Do not set
|
||||
|
||||
- (NSUInteger)numberOfChildren;
|
||||
- (MPExpression *)childAtIndex:(NSUInteger)index;
|
||||
- (NSUInteger)numberOfChildren; // Override
|
||||
- (MPExpression *)childAtIndex:(NSUInteger)index; // Override
|
||||
- (void)setChild:(MPExpression *)child
|
||||
atIndex:(NSUInteger)index;
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
|
||||
- (double)doubleValue;
|
||||
|
||||
#pragma mark Working With Functions
|
||||
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPFunction (MPFunctionExtensionMethods)
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
atIndex:(NSUInteger)index; // Override
|
||||
|
||||
// May be overridden for performance improvements
|
||||
- (NSArray *)children;
|
||||
- (NSArray *)children; // Indexes must equal the ones from the native methods
|
||||
- (NSUInteger)indexOfChild:(MPExpression *)child;
|
||||
|
||||
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
- (double)doubleValue; // Override
|
||||
|
||||
- (float)floatValue;
|
||||
- (int)intValue;
|
||||
- (NSInteger)integerValue;
|
||||
- (long long)longLongValue;
|
||||
#pragma mark Messages
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
- (void)didChangeChild:(MPExpression *)child;
|
||||
- (void)didChangeChildAtIndex:(NSUInteger)index;
|
||||
|
||||
- (NSString *)description;
|
||||
#pragma mark Working With Functions
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
|
||||
|
||||
- (NSUInteger)hash;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPFunction (MPDisplayExtension)
|
||||
|
||||
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length;
|
||||
- (NSString *)description; // Should be overridden
|
||||
- (NSUInteger)hash;// Override
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
#import "MPExpression.h"
|
||||
#import "MPRangePath.h"
|
||||
|
||||
#import "NSIndexPath+MPReverseIndexPath.h"
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
@implementation MPFunction
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
@@ -25,7 +24,6 @@
|
||||
}
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
- (NSUInteger)numberOfChildren
|
||||
{
|
||||
return 0;
|
||||
@@ -38,63 +36,10 @@
|
||||
|
||||
- (void)setChild:(MPExpression *)child
|
||||
atIndex:(NSUInteger)index
|
||||
{}
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
|
||||
- (double)doubleValue
|
||||
{
|
||||
return 0;
|
||||
[self didChangeChildAtIndex:index];
|
||||
}
|
||||
|
||||
#pragma mark Working With Functions
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (object == nil) {
|
||||
return NO;
|
||||
}
|
||||
if (![object isKindOfClass:[MPFunction class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToFunction:(MPFunction *)object];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
|
||||
{
|
||||
return [aFunction isMemberOfClass:[MPFunction class]] && [self isMemberOfClass:[MPFunction class]];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return [[MPFunction allocWithZone:zone] init];
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPFunction (MPFunctionExtensionMethods)
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
NSUInteger childCount = [self numberOfChildren];
|
||||
@@ -116,16 +61,115 @@
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.length == 0) {
|
||||
return self;
|
||||
}
|
||||
MPExpression *child = [self childAtIndex:[indexPath indexAtPosition:0]];
|
||||
return [child symbolAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
||||
return [child elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
|
||||
}
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
- (double)doubleValue
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark Notifications
|
||||
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
NSUInteger selfIndex = [self.parent indexOfElement:self];
|
||||
MPRangePath *newPath = rangePath.copy;
|
||||
newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
|
||||
[self.parent didChangeElementsInRangePath:newPath
|
||||
replacementLength:replacementLength];
|
||||
}
|
||||
|
||||
- (void)didChangeChild:(MPExpression *)child
|
||||
{
|
||||
[self didChangeChildAtIndex:[self indexOfChild:child]];
|
||||
}
|
||||
|
||||
- (void)didChangeChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
MPRangePath *path = [[MPRangePath alloc] initWithRange:NSMakeRange(index, 1)];
|
||||
[self didChangeElementsInRangePath:path
|
||||
replacementLength:1];
|
||||
}
|
||||
|
||||
#pragma mark Working With Functions
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (object == nil) {
|
||||
return NO;
|
||||
}
|
||||
if (![object isKindOfClass:[MPFunction class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToFunction:(MPFunction *)object];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
|
||||
{
|
||||
return [aFunction isMemberOfClass:[MPFunction class]] && [self isMemberOfClass:[MPFunction class]];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return @"[]";
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return [[MPFunction allocWithZone:zone] init];
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSArray *children = [aDecoder decodeObject];
|
||||
NSInteger index = 0;
|
||||
for (MPExpression *child in children) {
|
||||
[self setChild:child atIndex:index];
|
||||
++index;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:self.children];
|
||||
}
|
||||
|
||||
#pragma mark - MPExpressionElement
|
||||
- (BOOL)isString
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isFunction
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSUInteger)length
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (float)floatValue
|
||||
{
|
||||
@@ -147,26 +191,4 @@
|
||||
return (long long)[self doubleValue];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return @"[]";
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPFunction (MPDisplayExtension)
|
||||
|
||||
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length
|
||||
{
|
||||
NSUInteger index = [self.parent indexOfSymbol:self];
|
||||
NSIndexPath *newLocation = [rangePath.location indexPathByPrecedingIndex:index];
|
||||
MPRangePath *newRangePath = [[MPRangePath alloc] initWithLocation:newLocation length:rangePath.length];
|
||||
[self.parent symbolsChangedInRangePath:newRangePath replacementLength:length];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -2,56 +2,30 @@
|
||||
// MPFunctionLayout.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 22.04.14.
|
||||
// Created by Kim Wittenburg on 07.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@import Cocoa;
|
||||
#import "MPLayout.h"
|
||||
|
||||
@class MPFunctionLayout, MPExpressionLayout, MPExpressionStorage, MPFunction;
|
||||
@interface MPFunctionLayout : MPLayout
|
||||
|
||||
@interface MPFunctionLayout : NSObject {
|
||||
@protected
|
||||
BOOL _valid;
|
||||
NSSize _cachedSize;
|
||||
NSMutableArray *_childCache;
|
||||
}
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
|
||||
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
|
||||
parent:(MPExpressionLayout *)parent;
|
||||
|
||||
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
|
||||
parent:(MPExpressionLayout *)parent;
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
@property (readonly, nonatomic, weak) MPExpressionLayout *parent;
|
||||
|
||||
@property (readonly, nonatomic, strong) NSIndexPath *functionPath;
|
||||
|
||||
- (MPExpressionStorage *)expressionStorage;
|
||||
- (MPFunction *)function; // Convenience
|
||||
- (NSLayoutManager *)layoutManager;
|
||||
- (NSTextContainer *)textContainer;
|
||||
- (NSTextStorage *)textStorage;
|
||||
|
||||
#pragma mark Cache Methods
|
||||
|
||||
- (void)invalidate;
|
||||
- (void)editedChildAtIndex:(NSUInteger)index;
|
||||
|
||||
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index;
|
||||
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index;
|
||||
|
||||
#pragma mark Size Calculation Methods
|
||||
|
||||
- (NSSize)sizeOfFunction;
|
||||
- (NSSize)calculateSize;
|
||||
|
||||
#pragma mark Drawing Methods
|
||||
|
||||
- (void)drawFunctionAtPoint:(NSPoint)point;
|
||||
@property (readonly, nonatomic, weak) MPFunction *function; // Convenience
|
||||
|
||||
@end
|
||||
|
||||
@interface MPFunctionLayout (MPSubclassOverride)
|
||||
|
||||
// Should also implement accessor method for special function type:
|
||||
// - (MPCustomFunction *)customFunction
|
||||
// {
|
||||
// return (MPCustomFunction *)self.function;
|
||||
// }
|
||||
|
||||
- (NSBezierPath *)generateBezierPath;
|
||||
|
||||
@end
|
||||
@@ -2,14 +2,13 @@
|
||||
// MPFunctionLayout.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 22.04.14.
|
||||
// Created by Kim Wittenburg on 07.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPFunctionLayout.h"
|
||||
#import "MPExpressionLayout.h"
|
||||
#import "MPExpressionStorage.h"
|
||||
#import "MPFunction.h"
|
||||
#import "MPExpressionLayout.h"
|
||||
|
||||
#import "MPSumFunction.h"
|
||||
#import "MPSumFunctionLayout.h"
|
||||
@@ -17,114 +16,43 @@
|
||||
@implementation MPFunctionLayout
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
|
||||
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
|
||||
parent:(MPExpressionLayout *)parent
|
||||
{
|
||||
MPFunction *function = [parent.expressionStorage symbolAtIndexPath:functionPath];
|
||||
MPFunction *function = [parent.expressionStorage elementAtIndexPath:path];
|
||||
Class class = [function class];
|
||||
if (class == [MPSumFunction class]) {
|
||||
return [[MPSumFunctionLayout alloc] initWithFunctionPath:functionPath parent:parent];
|
||||
return [[MPSumFunctionLayout alloc] initWithPath:path parent:parent];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
|
||||
parent:(MPExpressionLayout *)parent
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_functionPath = functionPath;
|
||||
_parent = parent;
|
||||
_childCache = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
return [[self alloc] initWithPath:path parent:parent];
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
- (MPExpressionStorage *)expressionStorage
|
||||
{
|
||||
return self.parent.expressionStorage;
|
||||
}
|
||||
|
||||
- (MPFunction *)function
|
||||
{
|
||||
return [self.expressionStorage symbolAtIndexPath:self.functionPath];
|
||||
}
|
||||
|
||||
- (NSLayoutManager *)layoutManager
|
||||
{
|
||||
return self.expressionStorage.layoutManager;
|
||||
}
|
||||
|
||||
- (NSTextContainer *)textContainer
|
||||
{
|
||||
return self.expressionStorage.textContainer;
|
||||
}
|
||||
|
||||
- (NSTextStorage *)textStorage
|
||||
{
|
||||
return self.expressionStorage.textStorage;
|
||||
return [self.expressionStorage elementAtIndexPath:self.path];
|
||||
}
|
||||
|
||||
#pragma mark Cache Methods
|
||||
|
||||
- (void)invalidate
|
||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
|
||||
{
|
||||
_valid = NO;
|
||||
[self.parent invalidate];
|
||||
return [self cachableObjectForIndex:index generator:^id{
|
||||
NSIndexPath *childPath = [self.path indexPathByAddingIndex:index];
|
||||
MPExpressionLayout *layout = [[MPExpressionLayout alloc] initWithPath:childPath
|
||||
parent:self];
|
||||
return layout;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)editedChildAtIndex:(NSUInteger)index
|
||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
if ([self hasCacheForChildAtIndex:index]) {
|
||||
_childCache[index] = [NSNull null];
|
||||
}
|
||||
[self invalidate];
|
||||
MPLayout *childLayout = [self childLayoutAtIndex:index];
|
||||
return [childLayout size];
|
||||
}
|
||||
|
||||
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index >= _childCache.count) {
|
||||
return NO;
|
||||
}
|
||||
return _childCache[index] != [NSNull null];
|
||||
}
|
||||
|
||||
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index
|
||||
{
|
||||
if ([self hasCacheForChildAtIndex:index]) {
|
||||
return _childCache[index];
|
||||
}
|
||||
while (index >= _childCache.count) {
|
||||
[_childCache addObject:[NSNull null]];
|
||||
}
|
||||
MPExpressionLayout *expressionLayout = [[MPExpressionLayout alloc] initWithExpressionPath:[self.functionPath indexPathByAddingIndex:index] parent:self];
|
||||
_childCache[index] = expressionLayout;
|
||||
return expressionLayout;
|
||||
}
|
||||
|
||||
#pragma mark Size Calculation Methods
|
||||
|
||||
- (NSSize)sizeOfFunction
|
||||
{
|
||||
if (!_valid) {
|
||||
_cachedSize = [self calculateSize];
|
||||
_valid = YES;
|
||||
}
|
||||
return _cachedSize;
|
||||
}
|
||||
|
||||
- (NSSize)calculateSize
|
||||
{
|
||||
return NSMakeSize(0, 0);
|
||||
}
|
||||
|
||||
#pragma mark Drawing Methods
|
||||
|
||||
- (void)drawFunctionAtPoint:(NSPoint)point
|
||||
- (NSBezierPath *)generateBezierPath
|
||||
{
|
||||
return [NSBezierPath bezierPath];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
56
MathPad/MPLayout.h
Normal file
56
MathPad/MPLayout.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// MPDrawable.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 07.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Cocoa;
|
||||
#import "MPExpressionStorage.h"
|
||||
|
||||
#define MPNull [NSNull null]
|
||||
|
||||
@interface MPLayout : NSObject
|
||||
|
||||
#pragma mark Creation Methods
|
||||
- (instancetype)init;
|
||||
- (instancetype)initWithPath:(NSIndexPath *)path
|
||||
parent:(MPLayout *)parent;
|
||||
|
||||
#pragma mark Text System Objects
|
||||
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
|
||||
@property (readonly, nonatomic, weak) NSLayoutManager *layoutManager;
|
||||
@property (readonly, nonatomic, weak) NSTextContainer *textContainer;
|
||||
@property (readonly, nonatomic, weak) NSTextStorage *textStorage;
|
||||
|
||||
#pragma mark Cache Tree
|
||||
@property (readonly, nonatomic, weak) MPLayout *parent;
|
||||
@property (readonly, nonatomic, strong) NSIndexPath *path;
|
||||
|
||||
#pragma mark Cache Methods
|
||||
// Querying Caches
|
||||
- (id)cachableObjectForIndex:(NSUInteger)index
|
||||
generator:(id(^)())generator;
|
||||
|
||||
// Clearing Caches
|
||||
- (void)clearCacheInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)replacementLength;
|
||||
- (void)invalidate;
|
||||
|
||||
#pragma mark Calculation and Drawing Methods
|
||||
// @property (nonatomic) BOOL usesSmallSize;
|
||||
- (NSSize)size;
|
||||
|
||||
- (NSBezierPath *)bezierPath;
|
||||
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point;
|
||||
|
||||
- (void)drawAtPoint:(NSPoint)point;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPLayout (MPSubclassImplement)
|
||||
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
|
||||
- (NSSize)sizeForChildAtIndex:(NSUInteger)index; // To be implemented
|
||||
- (NSBezierPath *)generateBezierPath; // To be implemented
|
||||
@end
|
||||
159
MathPad/MPLayout.m
Normal file
159
MathPad/MPLayout.m
Normal file
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// MPLayout.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 11.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPLayout.h"
|
||||
|
||||
@interface MPLayout ()
|
||||
|
||||
// Querying Caches
|
||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
|
||||
|
||||
// Storing Caches
|
||||
- (void)cacheObject:(id)anObject
|
||||
forElementAtIndex:(NSUInteger)index;
|
||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPLayout {
|
||||
NSMutableArray *_cache;
|
||||
NSBezierPath *_cachedPath;
|
||||
}
|
||||
|
||||
#pragma mark Creation Methods
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_cache = [[NSMutableArray alloc] init];
|
||||
_cachedPath = nil;
|
||||
_path = [[NSIndexPath alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithPath:(NSIndexPath *)path
|
||||
parent:(MPLayout *)parent
|
||||
{
|
||||
self = [self init];
|
||||
if (self) {
|
||||
_path = path;
|
||||
_parent = parent;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma Text System Objects
|
||||
- (MPExpressionStorage *)expressionStorage
|
||||
{
|
||||
return self.parent.expressionStorage;
|
||||
}
|
||||
|
||||
- (NSLayoutManager *)layoutManager
|
||||
{
|
||||
return self.expressionStorage.layoutManager;
|
||||
}
|
||||
|
||||
- (NSTextContainer *)textContainer
|
||||
{
|
||||
return self.expressionStorage.textContainer;
|
||||
}
|
||||
|
||||
- (NSTextStorage *)textStorage
|
||||
{
|
||||
return self.expressionStorage.textStorage;
|
||||
}
|
||||
|
||||
#pragma mark Cache Tree
|
||||
// Querying Caches
|
||||
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index >= _cache.count) {
|
||||
return NO;
|
||||
}
|
||||
return _cache[index] != MPNull;
|
||||
}
|
||||
|
||||
- (id)cachableObjectForIndex:(NSUInteger)index
|
||||
generator:(id (^)())generator
|
||||
{
|
||||
if ([self hasCacheForElementAtIndex:index]) {
|
||||
return _cache[index];
|
||||
}
|
||||
id object = generator();
|
||||
[self cacheObject:object
|
||||
forElementAtIndex:index];
|
||||
return object;
|
||||
}
|
||||
|
||||
// Storing Caches
|
||||
- (void)cacheObject:(id)anObject
|
||||
forElementAtIndex:(NSUInteger)index
|
||||
{
|
||||
[self ensureCacheSizeForIndex:index];
|
||||
_cache[index] = anObject;
|
||||
}
|
||||
|
||||
- (void)ensureCacheSizeForIndex:(NSUInteger)index
|
||||
{
|
||||
while (index >= _cache.count) {
|
||||
[_cache addObject:MPNull];
|
||||
}
|
||||
}
|
||||
|
||||
// Clearing Caches
|
||||
- (void)clearCacheInRange:(NSRange)range
|
||||
replacementLength:(NSUInteger)replacementLength
|
||||
{
|
||||
NSMutableArray *placeholders = [[NSMutableArray alloc] initWithCapacity:replacementLength];
|
||||
while (placeholders.count < replacementLength) {
|
||||
[placeholders addObject:MPNull];
|
||||
}
|
||||
[_cache replaceObjectsInRange:range
|
||||
withObjectsFromArray:placeholders];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_cachedPath = nil;
|
||||
[self.parent invalidate];
|
||||
}
|
||||
|
||||
#pragma mark Calculation Methods
|
||||
- (NSSize)size
|
||||
{
|
||||
return self.bezierPath.bounds.size;
|
||||
}
|
||||
|
||||
- (NSBezierPath *)bezierPath
|
||||
{
|
||||
if (!_cachedPath) {
|
||||
_cachedPath = [self generateBezierPath];
|
||||
}
|
||||
return _cachedPath;
|
||||
}
|
||||
|
||||
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point
|
||||
{
|
||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
||||
[transform translateXBy:point.x
|
||||
yBy:point.y];
|
||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
||||
[path appendBezierPath:self.bezierPath];
|
||||
[path transformUsingAffineTransform:transform];
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void)drawAtPoint:(NSPoint)point
|
||||
{
|
||||
NSBezierPath *path = [self bezierPathAtOrigin:point];
|
||||
[path fill];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -18,8 +18,6 @@
|
||||
#import "MPFunction.h"
|
||||
#import "MPRangePath.h"
|
||||
|
||||
#import "NSObject+MPStringTest.h"
|
||||
#import "NSTextStorage+MPSetContents.h"
|
||||
#import "NSIndexPath+MPReverseIndexPath.h"
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "MPExpression.h"
|
||||
|
||||
@class MPRangePath, MPExpression;
|
||||
|
||||
@interface MPRangePath : NSObject <NSCopying, NSCoding>
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "MPRangePath.h"
|
||||
#import "MPExpression.h"
|
||||
|
||||
@implementation MPRangePath
|
||||
|
||||
@@ -143,7 +144,7 @@
|
||||
|
||||
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
|
||||
{
|
||||
MPExpression *targetExpression = [self symbolAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]];
|
||||
MPExpression *targetExpression = [self elementAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]];
|
||||
if (![targetExpression isKindOfClass:[MPExpression class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
#import "MPFunction.h"
|
||||
|
||||
@class MPSumFunction, MPExpression;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
@implementation MPSumFunction
|
||||
|
||||
#pragma mark Creation Methods
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
@@ -27,8 +26,32 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
#pragma mark Properties
|
||||
- (void)setStartExpression:(MPExpression *)startExpression
|
||||
{
|
||||
_startExpression.parent = nil;
|
||||
_startExpression = startExpression;
|
||||
_startExpression.parent = self;
|
||||
[self didChangeChildAtIndex:0];
|
||||
}
|
||||
|
||||
- (void)setTargetExpression:(MPExpression *)targetExpression
|
||||
{
|
||||
_targetExpression.parent = nil;
|
||||
_targetExpression = targetExpression;
|
||||
_targetExpression.parent = self;
|
||||
[self didChangeChildAtIndex:1];
|
||||
}
|
||||
|
||||
- (void)setSumExpression:(MPExpression *)sumExpression
|
||||
{
|
||||
_sumExpression.parent = nil;
|
||||
_sumExpression = sumExpression;
|
||||
_sumExpression.parent = self;
|
||||
[self didChangeChildAtIndex:2];
|
||||
}
|
||||
|
||||
#pragma mark Working With the Expression Tree
|
||||
- (NSUInteger)numberOfChildren
|
||||
{
|
||||
return 3;
|
||||
@@ -48,36 +71,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
return @[self.startExpression, self.targetExpression, self.sumExpression];
|
||||
}
|
||||
|
||||
- (void)setChild:(MPExpression *)child
|
||||
atIndex:(NSUInteger)index
|
||||
{
|
||||
switch (index) {
|
||||
// TODO: Copy child?
|
||||
case 0:
|
||||
self.startExpression.parent = nil;
|
||||
self.startExpression = child;
|
||||
break;
|
||||
case 1:
|
||||
self.targetExpression.parent = nil;
|
||||
self.targetExpression = child;
|
||||
break;
|
||||
case 2:
|
||||
self.sumExpression.parent = nil;
|
||||
self.sumExpression = child;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
child.parent = self;
|
||||
[self didChangeChildAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSArray *)children
|
||||
{
|
||||
return @[self.startExpression, self.targetExpression, self.sumExpression];
|
||||
}
|
||||
|
||||
#pragma mark Evaluating Functions
|
||||
|
||||
- (double)doubleValue
|
||||
{
|
||||
#warning Implementation
|
||||
@@ -85,7 +104,6 @@
|
||||
}
|
||||
|
||||
#pragma mark Working With Functions
|
||||
|
||||
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
|
||||
{
|
||||
if (![aFunction isKindOfClass:[MPSumFunction class]]) {
|
||||
@@ -106,8 +124,13 @@
|
||||
return [NSString stringWithFormat:@"Sum(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.sumExpression];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
#warning Unimplemented Method
|
||||
return [super hash];
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
MPSumFunction *copy = [[MPSumFunction allocWithZone:zone] init];
|
||||
|
||||
@@ -16,20 +16,21 @@
|
||||
return (MPSumFunction *)self.function;
|
||||
}
|
||||
|
||||
- (NSSize)calculateSize
|
||||
- (NSBezierPath *)generateBezierPath
|
||||
{
|
||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}];
|
||||
[self.textStorage setAttributedString:text];
|
||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑"
|
||||
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
|
||||
self.textStorage.attributedString = text;
|
||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
||||
return [self.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textContainer].size;
|
||||
}
|
||||
|
||||
- (void)drawFunctionAtPoint:(NSPoint)point
|
||||
{
|
||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}];
|
||||
[self.textStorage setAttributedString:text];
|
||||
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
|
||||
[self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:point];
|
||||
NSGlyph glyphs[glyphRange.length+1];
|
||||
NSUInteger actualGylphCount = [self.layoutManager getGlyphs:glyphs
|
||||
range:glyphRange];
|
||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
||||
[path moveToPoint:NSZeroPoint];
|
||||
[path appendBezierPathWithGlyphs:glyphs
|
||||
count:actualGylphCount
|
||||
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
30
MathPad/NSIndexPath+MPAdditions.h
Normal file
30
MathPad/NSIndexPath+MPAdditions.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// NSIndexPath+MPRemoveFirstIndex.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 23.04.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@interface NSIndexPath (MPAdditions)
|
||||
|
||||
/*!
|
||||
@method indexPathByRemovingFirstIndex
|
||||
@brief Provides an index path with the indexes in the receiving index path, excluding the first one.
|
||||
@discussion Returns an empty NSIndexPath instance if the receiving index path’s length is 1 or less.
|
||||
@return A new index path with the receiving index path’s indexes, excluding the first one.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByRemovingFirstIndex;
|
||||
|
||||
/*!
|
||||
@method indexPathByPreceedingIndex:
|
||||
@brief Provides an index path with the given index followed by the indexes of the receiver.
|
||||
@discussion If the receiver does not contain any indexes the given index is the only index contained in the returned index path.
|
||||
@param index The index new index preceeding all others
|
||||
@return A new index path with all the receiver's indexes preceeded by @c index.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index;
|
||||
|
||||
@end
|
||||
37
MathPad/NSIndexPath+MPAdditions.m
Normal file
37
MathPad/NSIndexPath+MPAdditions.m
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// NSIndexPath+MPRemoveFirstIndex.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 23.04.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSIndexPath+MPAdditions.h"
|
||||
|
||||
@implementation NSIndexPath (MPAdditions)
|
||||
|
||||
- (NSIndexPath *)indexPathByRemovingFirstIndex
|
||||
{
|
||||
if (self.length <= 1) {
|
||||
return [[NSIndexPath alloc] init];
|
||||
}
|
||||
NSUInteger indexes[self.length];
|
||||
[self getIndexes:indexes];
|
||||
NSUInteger newIndexes[self.length-1];
|
||||
for (NSUInteger i = 0; i < self.length-1; i++) {
|
||||
newIndexes[i] = indexes[i+1];
|
||||
}
|
||||
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length-1];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index
|
||||
{
|
||||
NSUInteger newIndexes[self.length+1];
|
||||
newIndexes[0] = index;
|
||||
for (NSUInteger i = 0; i < self.length; i++) {
|
||||
newIndexes[i+1] = [self indexAtPosition:i];
|
||||
}
|
||||
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length+1];
|
||||
}
|
||||
|
||||
@end
|
||||
14
MathPad/NSString+MPExpressionElement.h
Normal file
14
MathPad/NSString+MPExpressionElement.h
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// NSString+MPExpressionElement.h
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
#import "MPExpressionElement.h"
|
||||
|
||||
@interface NSString (MPExpressionElement) <MPExpressionElement>
|
||||
|
||||
@end
|
||||
49
MathPad/NSString+MPExpressionElement.m
Normal file
49
MathPad/NSString+MPExpressionElement.m
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// NSString+MPExpressionElement.m
|
||||
// MathPad
|
||||
//
|
||||
// Created by Kim Wittenburg on 10.08.14.
|
||||
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSString+MPExpressionElement.h"
|
||||
|
||||
@implementation NSString (MPExpressionElement)
|
||||
|
||||
- (BOOL)isString
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isFunction
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (double)doubleValue
|
||||
{
|
||||
#warning Unimplemented Method
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (float)floatValue
|
||||
{
|
||||
return (float)[self doubleValue];
|
||||
}
|
||||
|
||||
- (int)intValue
|
||||
{
|
||||
return (int)[self doubleValue];
|
||||
}
|
||||
|
||||
- (NSInteger)integerValue
|
||||
{
|
||||
return (NSInteger)[self doubleValue];
|
||||
}
|
||||
|
||||
- (long long)longLongValue
|
||||
{
|
||||
return (long long)[self doubleValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,119 +20,119 @@
|
||||
- (void)testInitialization {
|
||||
// Test empty expression
|
||||
MPExpression *testExpression = [[MPExpression alloc] init];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 0);
|
||||
XCTAssertEqual(testExpression.numberOfElements, 0);
|
||||
|
||||
// Test expression with string
|
||||
testExpression = [[MPExpression alloc] initWithString:@"1234+5678"];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"1234+5678");
|
||||
testExpression = [[MPExpression alloc] initWithElement:@"1234+5678"];
|
||||
XCTAssertEqual(testExpression.numberOfElements, 1);
|
||||
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"1234+5678");
|
||||
|
||||
// Test expression with function
|
||||
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([testExpression symbolAtIndex:0], [[MPFunction alloc] init]);
|
||||
testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
|
||||
XCTAssertEqual([testExpression numberOfElements], 1);
|
||||
XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]);
|
||||
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 4);
|
||||
XCTAssertEqualObjects([testExpression symbolAtIndex:2], @"17");
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqual([testExpression numberOfElements], 4);
|
||||
XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17");
|
||||
|
||||
// Test expression with subsequent strings
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", @"5678"]];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"12345678");
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]];
|
||||
XCTAssertEqual([testExpression numberOfElements], 1);
|
||||
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678");
|
||||
|
||||
// Test expression with only empty string
|
||||
testExpression = [[MPExpression alloc] initWithString:@""];
|
||||
XCTAssertEqual([testExpression numberOfSymbols], 0);
|
||||
testExpression = [[MPExpression alloc] initWithElement:@""];
|
||||
XCTAssertEqual([testExpression numberOfElements], 0);
|
||||
}
|
||||
|
||||
- (void)testSubexpressions {
|
||||
MPExpression *testExpression = [MPExpression expressionWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
|
||||
/********** subexpressionFromIndex: **********/
|
||||
// Test with start index at front
|
||||
MPExpression *subexpression = [testExpression subexpressionFromIndex:0];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 4);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"1234");
|
||||
MPExpression *subexpression = [testExpression subexpressionFromLocation:0];
|
||||
XCTAssertEqual([subexpression numberOfElements], 4);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234");
|
||||
|
||||
// Test with start index in first symbol
|
||||
subexpression = [testExpression subexpressionFromIndex:2];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 4);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
|
||||
// Test with start index in first element
|
||||
subexpression = [testExpression subexpressionFromLocation:2];
|
||||
XCTAssertEqual([subexpression numberOfElements], 4);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
|
||||
|
||||
// Test with start index in middle symbol starting with a literal
|
||||
subexpression = [testExpression subexpressionFromIndex:6];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 2);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"7");
|
||||
// Test with start index in middle element starting with a literal
|
||||
subexpression = [testExpression subexpressionFromLocation:6];
|
||||
XCTAssertEqual([subexpression numberOfElements], 2);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7");
|
||||
|
||||
// Test with start index in middle symbol starting with a function
|
||||
subexpression = [testExpression subexpressionFromIndex:4];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 3);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]);
|
||||
// Test with start index in middle element starting with a function
|
||||
subexpression = [testExpression subexpressionFromLocation:4];
|
||||
XCTAssertEqual([subexpression numberOfElements], 3);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
|
||||
|
||||
// Test with start index in last symbol
|
||||
subexpression = [testExpression subexpressionFromIndex:7];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]);
|
||||
// Test with start index in last element
|
||||
subexpression = [testExpression subexpressionFromLocation:7];
|
||||
XCTAssertEqual([subexpression numberOfElements], 1);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
|
||||
|
||||
// Test with start index at end
|
||||
subexpression = [testExpression subexpressionFromIndex:8];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 0);
|
||||
subexpression = [testExpression subexpressionFromLocation:8];
|
||||
XCTAssertEqual([subexpression numberOfElements], 0);
|
||||
|
||||
/********** subexpressionToIndex: **********/
|
||||
// Test with end index at front
|
||||
subexpression = [testExpression subexpressionToIndex:0];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 0);
|
||||
subexpression = [testExpression subexpressionToLocation:0];
|
||||
XCTAssertEqual([subexpression numberOfElements], 0);
|
||||
|
||||
// Test with end index in first symbol
|
||||
subexpression = [testExpression subexpressionToIndex:2];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"12");
|
||||
// Test with end index in first Element
|
||||
subexpression = [testExpression subexpressionToLocation:2];
|
||||
XCTAssertEqual([subexpression numberOfElements], 1);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12");
|
||||
|
||||
// Test with end index in middle symbol ending with a literal
|
||||
subexpression = [testExpression subexpressionToIndex:6];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 3);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1");
|
||||
// Test with end index in middle Element ending with a literal
|
||||
subexpression = [testExpression subexpressionToLocation:6];
|
||||
XCTAssertEqual([subexpression numberOfElements], 3);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
|
||||
|
||||
// Test with end index in middle symbol ending with a function
|
||||
subexpression = [testExpression subexpressionToIndex:5];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 2);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]);
|
||||
// Test with end index in middle Element ending with a function
|
||||
subexpression = [testExpression subexpressionToLocation:5];
|
||||
XCTAssertEqual([subexpression numberOfElements], 2);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
|
||||
|
||||
// Test with end index at end
|
||||
subexpression = [testExpression subexpressionToIndex:8];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 4);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:3], [[MPFunction alloc] init]);
|
||||
subexpression = [testExpression subexpressionToLocation:8];
|
||||
XCTAssertEqual([subexpression numberOfElements], 4);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]);
|
||||
|
||||
/********** subexpressionWithRange: **********/
|
||||
// Test with empty range
|
||||
subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 0);
|
||||
XCTAssertEqual([subexpression numberOfElements], 0);
|
||||
|
||||
// Test with start and end in first symbol
|
||||
// Test with start and end in first element
|
||||
subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 1);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"23");
|
||||
XCTAssertEqual([subexpression numberOfElements], 1);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23");
|
||||
|
||||
// Test with start in first and end in middle after function
|
||||
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 2);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]);
|
||||
XCTAssertEqual([subexpression numberOfElements], 2);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
|
||||
|
||||
// Test with start in first and end in middle after literal
|
||||
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)];
|
||||
XCTAssertEqual([subexpression numberOfSymbols], 3);
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
|
||||
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1");
|
||||
XCTAssertEqual([subexpression numberOfElements], 3);
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
|
||||
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
|
||||
}
|
||||
|
||||
- (void)testSubexpressionsIllegalRange {
|
||||
MPExpression *testExpression = [MPExpression expressionWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
|
||||
|
||||
// Test with start index beyond end
|
||||
@try {
|
||||
[testExpression subexpressionFromIndex:10];
|
||||
[testExpression subexpressionFromLocation:10];
|
||||
XCTFail(@"Should have raised an exception.");
|
||||
}
|
||||
@catch (NSException *exception) {}
|
||||
@@ -146,11 +146,11 @@
|
||||
}
|
||||
|
||||
- (void)testEqualExpressions {
|
||||
MPExpression *expression1 = [[MPExpression alloc] initWithString:@"123"];
|
||||
MPExpression *expression2 = [[MPExpression alloc] initWithString:@"1234"];
|
||||
MPExpression *expression3 = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
|
||||
MPExpression *expression4 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]];
|
||||
MPExpression *expression5 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]];
|
||||
MPExpression *expression1 = [[MPExpression alloc] initWithElement:@"123"];
|
||||
MPExpression *expression2 = [[MPExpression alloc] initWithElement:@"1234"];
|
||||
MPExpression *expression3 = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
|
||||
MPExpression *expression4 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
|
||||
MPExpression *expression5 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
|
||||
|
||||
XCTAssertNotEqualObjects(expression1, expression2);
|
||||
XCTAssertNotEqualObjects(expression1, expression3);
|
||||
@@ -159,68 +159,83 @@
|
||||
XCTAssertEqualObjects(expression4, expression5);
|
||||
}
|
||||
|
||||
- (void)testAppendSymbols {
|
||||
MPExpression *expression1 = [[MPExpression alloc] initWithString:@"123"];
|
||||
MPExpression *expression2 = [expression1 expressionByAppendingFunction:[[MPFunction alloc] init]];
|
||||
MPExpression *expression3 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
|
||||
|
||||
XCTAssertEqualObjects(expression2, expression3);
|
||||
}
|
||||
|
||||
- (void)testDescription {
|
||||
// Test Simple Expressions
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithString:@"1234"];
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithElement:@"1234"];
|
||||
XCTAssertEqualObjects([testExpression description], @"1234");
|
||||
|
||||
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
|
||||
testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
|
||||
XCTAssertEqualObjects([testExpression description], @"[]");
|
||||
|
||||
// Test function after literal without explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[@"123", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqualObjects([testExpression description], @"123*[]");
|
||||
|
||||
// Test function after literal with explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[@"123+", [[MPFunction alloc] init]]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[@"123+", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqualObjects([testExpression description], @"123+[]");
|
||||
|
||||
// Test literal after function without explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
|
||||
XCTAssertEqualObjects([testExpression description], @"[]*123");
|
||||
|
||||
// Test literal after function with explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-123"]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-123"]];
|
||||
XCTAssertEqualObjects([testExpression description], @"[]-123");
|
||||
|
||||
// Test function after function without explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]];
|
||||
XCTAssertEqualObjects([testExpression description], @"[]*[]");
|
||||
|
||||
// Test function after function with explicit operator
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqualObjects([testExpression description], @"[]-[]");
|
||||
|
||||
|
||||
// Test whitespaces in literal
|
||||
testExpression = [[MPExpression alloc] initWithSymbols:@[@" 123 + ", [[MPFunction alloc] init]]];
|
||||
testExpression = [[MPExpression alloc] initWithElements:@[@" 123 + ", [[MPFunction alloc] init]]];
|
||||
XCTAssertEqualObjects([testExpression description], @"123 +[]");
|
||||
}
|
||||
|
||||
- (void)testCopying {
|
||||
MPExpression *baseExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
|
||||
MPExpression *baseExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
|
||||
MPExpression *copy = [baseExpression copy];
|
||||
|
||||
XCTAssertEqual(baseExpression.numberOfElements, copy.numberOfElements);
|
||||
XCTAssertNotEqual(copy, baseExpression);
|
||||
XCTAssertNotEqual([baseExpression symbolAtIndex:0], [copy symbolAtIndex:0]);
|
||||
XCTAssertNotEqual([baseExpression elementAtIndex:0], [copy elementAtIndex:0]);
|
||||
XCTAssertEqualObjects(baseExpression, copy);
|
||||
}
|
||||
|
||||
- (void)testMutableCopying {
|
||||
MPExpression *baseExpression = [[MPExpression alloc] initWithString:@"123"];
|
||||
MPMutableExpression *expression1 = [baseExpression mutableCopy];
|
||||
[expression1 appendFunction:[[MPFunction alloc] init]];
|
||||
MPExpression *expression2 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
|
||||
- (void)testMutating {
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
|
||||
|
||||
XCTAssertEqualObjects(expression1, expression2);
|
||||
XCTAssertNotEqualObjects(baseExpression, expression1);
|
||||
[testExpression appendElement:@"90"];
|
||||
XCTAssertEqual([testExpression numberOfElements], 5);
|
||||
// 1234 [] 5678 [] 90
|
||||
|
||||
[testExpression deleteElementsInRange:NSMakeRange(2, 4)];
|
||||
XCTAssertEqual([testExpression numberOfElements], 3);
|
||||
// 12678 [] 90
|
||||
|
||||
[testExpression insertElement:[[MPFunction alloc] init]
|
||||
atLocation:2];
|
||||
XCTAssertEqual([testExpression numberOfElements], 5);
|
||||
// 12 [] 678 [] 90
|
||||
|
||||
[testExpression replaceElementsInRange:NSMakeRange(2, 5)
|
||||
withElements:@[[[MPFunction alloc] init]]];
|
||||
XCTAssertEqual([testExpression numberOfElements], 3);
|
||||
// 12 [] 90
|
||||
}
|
||||
|
||||
- (void)testInvalidMutatingRange {
|
||||
@try {
|
||||
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
|
||||
[testExpression deleteElementsInRange:NSMakeRange(5, 17)];
|
||||
XCTFail(@"Should have raised an exception");
|
||||
}
|
||||
@catch (NSException *exception) {}
|
||||
}
|
||||
|
||||
// TODO: Test evaluating expressions
|
||||
|
||||
Reference in New Issue
Block a user