From 91320385f06e540f4ee312cc1a3ac1d060d451a0 Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Mon, 8 Sep 2014 22:43:20 +0200 Subject: [PATCH] Corrected errors when selecting expressions --- MathPad/MPExpressionView.m | 192 +++++++++++++++++++++++++----- MathPad/NSIndexPath+MPAdditions.h | 4 +- MathPad/NSIndexPath+MPAdditions.m | 16 ++- 3 files changed, 181 insertions(+), 31 deletions(-) diff --git a/MathPad/MPExpressionView.m b/MathPad/MPExpressionView.m index bd669cb..f5fe640 100644 --- a/MathPad/MPExpressionView.m +++ b/MathPad/MPExpressionView.m @@ -9,6 +9,7 @@ #import "MPExpressionView.h" #import "MPExpressionStorage.h" #import "MPExpressionLayout.h" +#import "MPFunctionLayout.h" #import "MPRangePath.h" @@ -48,13 +49,15 @@ - (NSRect)selectionRect; -- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selection - selectWords:(BOOL)flag; -- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selection - selectWords:(BOOL)flag; +- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath + byExtendingSelection:(BOOL)extendingSelection + selectWords:(BOOL)selectWords; +- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath + byExtendingSelection:(BOOL)extendingSelection + selectWords:(BOOL)selectWords; -- (MPRangePath *)rangePathEnclosingAnchor:(NSIndexPath *)anchor - newSelection:(NSIndexPath *)newSelection; +- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchor + newSelectionPath:(NSIndexPath *)newSelection; @end @@ -101,43 +104,120 @@ return selectionRect; } -- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selection - selectWords:(BOOL)flag +- (NSIndexPath *)selectionToTheRightOf:(NSIndexPath *)selectionPath + byExtendingSelection:(BOOL)extendingSelection + selectWords:(BOOL)selectWords { - MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[selection indexPathByRemovingLastIndex]]; - NSUInteger locationInTarget = selection.lastIndex; + NSIndexPath *targetExpressionPath = [selectionPath indexPathByRemovingLastIndex]; + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; + NSUInteger locationInTarget = selectionPath.lastIndex; NSUInteger locationInElement; NSUInteger targetElementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget offset:&locationInElement]; + id targetElement; + // There is only a target element if the selection is not the last location in an expression if (targetElementIndex < targetExpression.numberOfElements) { targetElement = [targetExpression elementAtIndex:targetElementIndex]; } - if (locationInElement == 0 && !flag) { - NSLog(@"Move to next"); - // Move to next element + + if (!selectWords && !extendingSelection && (locationInElement == 0 || locationInTarget == targetExpression.length)) { + // First or last index in an element or expression + + // Last element in the expression + if (locationInTarget == targetExpression.length) { + // The selection is inside a function and should proceed + if (selectionPath.length > 1) { + NSIndexPath *functionPath = [[selectionPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; + NSUInteger currentChildIndex = [selectionPath indexPathByRemovingLastIndex].lastIndex; + NSUInteger newChildIndex = [functionLayout indexOfChildAfterChildAtIndex:currentChildIndex]; + + // The function is to be exited + if (newChildIndex == NSNotFound) { + targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]]; + NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex]; + return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression+1]; + } else { + return [[targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex] indexPathByAddingIndex:0]; + } + } // else the selection does not change + + // First Element + } else { + if ([targetElement isString]) { + locationInTarget++; + } else { + NSIndexPath *targetFunctionPath = [selectionPath indexPathByReplacingLastIndexWithIndex:targetElementIndex]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:targetFunctionPath]; + NSUInteger leadingChildIndex = [functionLayout indexOfLeadingChild]; + return [[targetFunctionPath indexPathByAddingIndex:leadingChildIndex] indexPathByAddingIndex:0]; + } + } } else if (locationInTarget < targetExpression.length) { - if (flag) { + if (selectWords) { locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex+1]; } else { locationInTarget++; } } - return [selection indexPathByReplacingLastIndexWithIndex:locationInTarget]; + return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget]; } -- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selection - selectWords:(BOOL)flag +- (NSIndexPath *)selectionToTheLeftOf:(NSIndexPath *)selectionPath + byExtendingSelection:(BOOL)extendingSelection + selectWords:(BOOL)selectWords { - MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[selection indexPathByRemovingLastIndex]]; - NSUInteger locationInTarget = selection.lastIndex; + NSIndexPath *targetExpressionPath = [selectionPath indexPathByRemovingLastIndex]; + MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; + NSUInteger locationInTarget = selectionPath.lastIndex; NSUInteger locationInElement; NSUInteger targetElementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget offset:&locationInElement]; - if (locationInElement == 0 && !flag) { - // Move to previous element + + NSUInteger previousElementIndex = targetElementIndex - (locationInElement == 0 ? 1 : 0); + id previousElement; + if (locationInTarget > 0) { + previousElement = [targetExpression elementAtIndex:previousElementIndex]; + } + + if (!selectWords && !extendingSelection && locationInElement == 0) { + // First element in expression + if (locationInTarget == 0) { + if (selectionPath.length > 1) { + NSIndexPath *functionPath = [[selectionPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; + NSUInteger currentChildIndex = [selectionPath indexPathByRemovingLastIndex].lastIndex; + NSUInteger newChildIndex = [functionLayout indexOfChildBeforeChildAtIndex:currentChildIndex]; + + // The function is to be exited + if (newChildIndex == NSNotFound) { + targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]]; + NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex]; + return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression]; + } else { + targetExpressionPath = [targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex]; + targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; + return [targetExpressionPath indexPathByAddingIndex:targetExpression.length]; + } + } // else the selection does not change + + // Just + } else { + if ([previousElement isString]) { + locationInTarget--; + } else { + NSIndexPath *targetFunctionPath = [selectionPath indexPathByReplacingLastIndexWithIndex:previousElementIndex]; + MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:targetFunctionPath]; + NSUInteger trailingChildIndex = [functionLayout indexOfTrailingChild]; + + targetExpressionPath = [targetFunctionPath indexPathByAddingIndex:trailingChildIndex]; + targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; + return [targetExpressionPath indexPathByAddingIndex:targetExpression.length]; + } + } } else if (locationInTarget > 0) { - if (flag) { + if (selectWords) { if (locationInElement == 0) { targetElementIndex--; } @@ -146,13 +226,53 @@ locationInTarget--; } } - return [selection indexPathByReplacingLastIndexWithIndex:locationInTarget]; + return [selectionPath indexPathByReplacingLastIndexWithIndex:locationInTarget]; } -- (MPRangePath *)rangePathEnclosingAnchor:(NSIndexPath *)anchor - newSelection:(NSIndexPath *)newSelection +- (MPRangePath *)rangePathEnclosingAnchorPath:(NSIndexPath *)anchorPath + newSelectionPath:(NSIndexPath *)newSelectionPath { + if ([anchorPath isEqual:newSelectionPath]) { + return MPMakeRangePath(anchorPath, 0); + } + NSIndexPath *commonPath = [anchorPath commonIndexPathWith:newSelectionPath]; + if (commonPath.length == anchorPath.length-1 && commonPath.length == newSelectionPath.length-1) { + // The two paths point to different locations in the same expression + NSUInteger anchorIndex = [anchorPath indexAtPosition:commonPath.length]; + NSUInteger newIndex = [newSelectionPath indexAtPosition:commonPath.length]; + NSUInteger minIndex = MIN(anchorIndex, newIndex); + NSUInteger length = MAX(anchorIndex, newIndex) - minIndex; + return MPMakeRangePath([commonPath indexPathByAddingIndex:minIndex], length); + } else { + if ((commonPath.length & 1) == 1) { + commonPath = [commonPath indexPathByRemovingLastIndex]; + } + MPExpression *closestCommonAncestor = [self.expressionStorage elementAtIndexPath:commonPath]; + NSUInteger anchorIndex = [anchorPath indexAtPosition:commonPath.length]; + NSUInteger newIndex = [newSelectionPath indexAtPosition:commonPath.length]; + + if (commonPath.length < anchorPath.length-1) { + anchorIndex = [closestCommonAncestor locationOfElementAtIndex:anchorIndex]; + } + if (commonPath.length < newSelectionPath.length-1) { + newIndex = [closestCommonAncestor locationOfElementAtIndex:newIndex]; + } + + NSUInteger minIndex = MIN(anchorIndex, newIndex); + if (commonPath.length < anchorPath.length-1 && anchorIndex != minIndex) { + anchorIndex++; + } else if (commonPath.length < newSelectionPath.length-1 && newIndex != minIndex) { + newIndex++; + } + + NSUInteger length = MAX(anchorIndex, newIndex) - minIndex; + if (anchorIndex == newIndex) { + length++; + } + MPRangePath *newSelection = MPMakeRangePath([commonPath indexPathByAddingIndex:minIndex], length); + return newSelection; + } } @end @@ -290,6 +410,7 @@ self.selection = MPMakeRangePath(self.selection.maxRangePath, 0); } else { NSIndexPath *newSelectionLocation = [self selectionToTheRightOf:self.selection.location + byExtendingSelection:NO selectWords:NO]; self.selection = MPMakeRangePath(newSelectionLocation, 0); } @@ -301,6 +422,7 @@ self.selection = MPMakeRangePath(self.selection.location, 0); } else { NSIndexPath *newSelectionLocation = [self selectionToTheLeftOf:self.selection.location + byExtendingSelection:NO selectWords:NO]; self.selection = MPMakeRangePath(newSelectionLocation, 0); } @@ -310,6 +432,7 @@ { NSIndexPath *location = self.selection.maxRangePath; NSIndexPath *newSelectionLocation = [self selectionToTheRightOf:location + byExtendingSelection:NO selectWords:YES]; self.selection = MPMakeRangePath(newSelectionLocation, 0); } @@ -318,6 +441,7 @@ { NSIndexPath *location = self.selection.location; NSIndexPath *newSelectionLocation = [self selectionToTheLeftOf:location + byExtendingSelection:NO selectWords:YES]; self.selection = MPMakeRangePath(newSelectionLocation, 0); } @@ -342,12 +466,16 @@ NSIndexPath *maxLocation = self.selection.maxRangePath; if (self.selectionModifyingStart) { location = [self selectionToTheLeftOf:location + byExtendingSelection:YES selectWords:NO]; } else { maxLocation = [self selectionToTheLeftOf:maxLocation + byExtendingSelection:YES selectWords:NO]; } - self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); + self.selection = [self rangePathEnclosingAnchorPath:maxLocation newSelectionPath:location]; + NSLog(@"%@", self.selection); +// self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); } - (void)moveRightAndModifySelection:(id)sender @@ -360,9 +488,11 @@ if (self.selectionModifyingStart) { location = [self selectionToTheRightOf:location - selectWords:NO]; + byExtendingSelection:YES + selectWords:NO]; } else { maxLocation = [self selectionToTheRightOf:maxLocation + byExtendingSelection:YES selectWords:NO]; } self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); @@ -377,12 +507,14 @@ NSIndexPath *maxLocation = self.selection.maxRangePath; if (self.selectionModifyingStart) { location = [self selectionToTheRightOf:location + byExtendingSelection:YES selectWords:YES]; if (location.lastIndex > maxLocation.lastIndex) { location = [location indexPathByReplacingLastIndexWithIndex:maxLocation.lastIndex]; } } else { maxLocation = [self selectionToTheRightOf:maxLocation + byExtendingSelection:YES selectWords:YES]; } self.selection = MPMakeRangePath(location, maxLocation.lastIndex-location.lastIndex); @@ -397,9 +529,11 @@ NSIndexPath *maxLocation = self.selection.maxRangePath; if (self.selectionModifyingStart) { location = [self selectionToTheLeftOf:location + byExtendingSelection:YES selectWords:YES]; } else { maxLocation = [self selectionToTheLeftOf:maxLocation + byExtendingSelection:YES selectWords:YES]; if (maxLocation.lastIndex < location.lastIndex) { maxLocation = [maxLocation indexPathByReplacingLastIndexWithIndex:location.lastIndex]; @@ -448,8 +582,8 @@ pointInView.y -= expressionOrigin.y; NSIndexPath *mouseSelectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView]; - self.selection = [self rangePathEnclosingAnchor:self.mouseAnchor - newSelection:mouseSelectionPath]; + self.selection = [self rangePathEnclosingAnchorPath:self.mouseAnchor + newSelectionPath:mouseSelectionPath]; } #pragma mark Drawing Methods diff --git a/MathPad/NSIndexPath+MPAdditions.h b/MathPad/NSIndexPath+MPAdditions.h index 185ac20..f40ab5d 100644 --- a/MathPad/NSIndexPath+MPAdditions.h +++ b/MathPad/NSIndexPath+MPAdditions.h @@ -10,6 +10,7 @@ @interface NSIndexPath (MPAdditions) +- (NSUInteger)firstIndex; - (NSUInteger)lastIndex; - (NSIndexPath *)indexPathByReplacingLastIndexWithIndex:(NSUInteger)index; @@ -34,7 +35,8 @@ - (NSIndexPath *)indexPathByIncrementingLastIndex; - (NSIndexPath *)indexPathByDecrementingLastIndex; -- (NSIndexPath *)indexPathWithLength:(NSUInteger)length; // use length indexes from the receiver, exception if too much +- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)length; // use length indexes from the receiver, exception if too much +- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)length; // number of indexes from the beginning to exclude - (NSIndexPath *)commonIndexPathWith:(NSIndexPath *)indexPath; diff --git a/MathPad/NSIndexPath+MPAdditions.m b/MathPad/NSIndexPath+MPAdditions.m index 43a975f..14a57a0 100644 --- a/MathPad/NSIndexPath+MPAdditions.m +++ b/MathPad/NSIndexPath+MPAdditions.m @@ -10,6 +10,11 @@ @implementation NSIndexPath (MPAdditions) +- (NSUInteger)firstIndex +{ + return [self indexAtPosition:0]; +} + - (NSUInteger)lastIndex { return [self indexAtPosition:self.length-1]; @@ -58,7 +63,7 @@ return [[self indexPathByRemovingLastIndex] indexPathByAddingIndex:lastIndex]; } -- (NSIndexPath *)indexPathWithLength:(NSUInteger)length +- (NSIndexPath *)indexPathByRemovingIndexesFrom:(NSUInteger)length { NSIndexPath *indexPath = [[NSIndexPath alloc] init]; for (NSUInteger position = 0; position < length; position++) { @@ -67,6 +72,15 @@ return indexPath; } +- (NSIndexPath *)indexPathByRemovingIndexesTo:(NSUInteger)length +{ + NSIndexPath *indexPath = [[NSIndexPath alloc] init]; + for (NSUInteger position = length; position < self.length; position++) { + indexPath = [indexPath indexPathByAddingIndex:[self indexAtPosition:position]]; + } + return indexPath; +} + - (NSIndexPath *)commonIndexPathWith:(NSIndexPath *)indexPath { NSIndexPath *commonPath = [[NSIndexPath alloc] init];