mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 03:11:18 +00:00
Rewrite insertion point movement actions properly.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@15904 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
5ca050e9ca
commit
39744f3b2f
5 changed files with 578 additions and 394 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2003-02-08 17:51 Alexander Malmberg <alexander@malmberg.org>
|
||||
|
||||
* Headers/gnustep/gui/NSLayoutManager.h, Source/NSLayoutManager.m:
|
||||
Add -characterIndexMoving:fromCharacterIndex:originalCharacterIndex:
|
||||
distance: method.
|
||||
|
||||
* Source/NSTextView.m: Keep track of the selection affinity.
|
||||
|
||||
* Source/NSTextView_actions.m: Use the new method and affinity
|
||||
handling to reimplement the insertion point movement actions.
|
||||
|
||||
2003-02-07 17:47 Alexander Malmberg <alexander@malmberg.org>
|
||||
|
||||
* Source/GSTextStorage.m (-setAttributes:range:): Fix memory leak.
|
||||
|
|
|
@ -32,6 +32,18 @@
|
|||
|
||||
@class NSParagraphStyle;
|
||||
|
||||
|
||||
/*
|
||||
GNUstep extension.
|
||||
*/
|
||||
typedef enum {
|
||||
GSInsertionPointMoveLeft,
|
||||
GSInsertionPointMoveRight,
|
||||
GSInsertionPointMoveDown,
|
||||
GSInsertionPointMoveUp,
|
||||
} GSInsertionPointMovementDirection;
|
||||
|
||||
|
||||
@interface NSLayoutManager : GSLayoutManager
|
||||
{
|
||||
/* Public for use only in the associated NSTextViews. Don't access
|
||||
|
@ -124,6 +136,36 @@ GNUstep extension.
|
|||
-(NSRect) insertionPointRectForCharacterIndex: (unsigned int)cindex
|
||||
inTextContainer: (NSTextContainer *)textContainer;
|
||||
|
||||
|
||||
/*
|
||||
Insertion point movement primitive. 'from' is the character index moved from,
|
||||
and 'original' is the character index originally moved from in this sequence
|
||||
of moves (ie. if the user hits the down key several times, the first call
|
||||
would have original==from, and subsequent calls would use the same 'original'
|
||||
and the 'from' returned from the last call).
|
||||
|
||||
The returned character index will always be different from 'from' unless
|
||||
'from' is the "furthest" character index in the text container in the
|
||||
specified direction.
|
||||
|
||||
The distance is the target distance for the move (in the text container's
|
||||
coordinate system). The move won't be farther than this distance unless
|
||||
it's impossible to move a shorter distance. Distance 0.0 is treated
|
||||
specially: the move will be the shortest possible move, and movement will
|
||||
"make sense" even if the glyph/character mapping is complex at 'from'
|
||||
(eg. it will move through ligatures in a sensible way).
|
||||
|
||||
Note that this method does not work across text containers. 'original' and
|
||||
'from' should be in the same container, and the returned index will also be
|
||||
in that container.
|
||||
|
||||
GNUstep extension.
|
||||
*/
|
||||
-(unsigned int) characterIndexMoving: (GSInsertionPointMovementDirection)direction
|
||||
fromCharacterIndex: (unsigned int)from
|
||||
originalCharacterIndex: (unsigned int)original
|
||||
distance: (float)distance;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
@ -79,6 +79,8 @@ http://wiki.gnustep.org/index.php/NominallySpacedGlyphs
|
|||
i = r.location;
|
||||
f = [self effectiveFontForGlyphAtIndex: i
|
||||
range: &r];
|
||||
/* TODO: this is rather inefficient and doesn't deal with non-shown
|
||||
glyphs */
|
||||
for (; i < glyphIndex; i++)
|
||||
{
|
||||
if (i == r.location + r.length)
|
||||
|
@ -545,8 +547,50 @@ anything visible
|
|||
}
|
||||
|
||||
|
||||
-(NSRect) insertionPointRectForCharacterIndex: (unsigned int)cindex
|
||||
inTextContainer: (NSTextContainer *)textContainer
|
||||
/*
|
||||
Insertion point positioning and movement.
|
||||
*/
|
||||
|
||||
-(unsigned int) _glyphIndexForCharacterIndex: (unsigned int)cindex
|
||||
fractionThrough: (float *)fraction
|
||||
{
|
||||
if (cindex == [[_textStorage string] length])
|
||||
{
|
||||
if (!cindex)
|
||||
{
|
||||
*fraction = 0.0;
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
*fraction = 1.0;
|
||||
return [self numberOfGlyphs] - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRange glyphRange, charRange;
|
||||
unsigned int glyph_index;
|
||||
float fraction_through;
|
||||
|
||||
glyphRange = [self glyphRangeForCharacterRange: NSMakeRange(cindex, 1)
|
||||
actualCharacterRange: &charRange];
|
||||
|
||||
/* Deal with composite characters and ligatures. */
|
||||
fraction_through = (cindex - charRange.location) / (float)charRange.length;
|
||||
fraction_through *= glyphRange.length;
|
||||
|
||||
glyph_index = glyphRange.location + floor(fraction_through);
|
||||
fraction_through -= floor(fraction_through);
|
||||
|
||||
*fraction = fraction_through;
|
||||
return glyph_index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Note: other methods rely a lot on the fact that the rectangle returned here
|
||||
has the same y origin and height as the line frag rect it is in.
|
||||
*/
|
||||
-(NSRect) _insertionPointRectForCharacterIndex: (unsigned int)cindex
|
||||
textContainer: (int *)textContainer
|
||||
{
|
||||
int i;
|
||||
textcontainer_t *tc;
|
||||
|
@ -558,45 +602,29 @@ anything visible
|
|||
float fraction_through;
|
||||
|
||||
|
||||
if (cindex == [[_textStorage string] length])
|
||||
{ /* TODO: use extra line frag, etc. */
|
||||
if (!cindex)
|
||||
{
|
||||
return NSMakeRect(1,1,1,13); /* TODO! */
|
||||
}
|
||||
glyph_index = [self numberOfGlyphs] - 1;
|
||||
fraction_through = 1.0;
|
||||
}
|
||||
else
|
||||
glyph_index = [self _glyphIndexForCharacterIndex: cindex
|
||||
fractionThrough: &fraction_through];
|
||||
if (glyph_index == (unsigned int)-1)
|
||||
{
|
||||
NSRange glyphRange, charRange;
|
||||
|
||||
glyphRange = [self glyphRangeForCharacterRange: NSMakeRange(cindex, 1)
|
||||
actualCharacterRange: &charRange];
|
||||
|
||||
/* Magic to deal with composite characters and ligatures. */
|
||||
fraction_through = (cindex - charRange.location) / (float)charRange.length;
|
||||
fraction_through *= glyphRange.length;
|
||||
|
||||
glyph_index = glyphRange.location + floor(fraction_through);
|
||||
fraction_through -= floor(fraction_through);
|
||||
if (num_textcontainers > 0)
|
||||
*textContainer = 0;
|
||||
else
|
||||
*textContainer = -1;
|
||||
/* TODO: use extra rect, etc. */
|
||||
return NSMakeRect(1,1,1,13);
|
||||
}
|
||||
|
||||
|
||||
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
||||
if (tc->textContainer == textContainer)
|
||||
if (tc->pos + tc->length > glyph_index)
|
||||
break;
|
||||
[self _doLayoutToGlyph: glyph_index - 1];
|
||||
[self _doLayoutToGlyph: glyph_index];
|
||||
if (i == num_textcontainers)
|
||||
{
|
||||
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
||||
*textContainer = -1;
|
||||
return NSZeroRect;
|
||||
}
|
||||
|
||||
if (glyph_index < tc->pos || glyph_index >= tc->pos + tc->length)
|
||||
{
|
||||
return NSZeroRect;
|
||||
}
|
||||
*textContainer = i;
|
||||
|
||||
for (lf = tc->linefrags, i = 0; i < tc->num_linefrags; i++, lf++)
|
||||
if (lf->pos + lf->length > glyph_index)
|
||||
|
@ -637,6 +665,20 @@ anything visible
|
|||
r.origin.x = x0 + (x1 - x0) * fraction_through;
|
||||
r.size.width = 1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
-(NSRect) insertionPointRectForCharacterIndex: (unsigned int)cindex
|
||||
inTextContainer: (NSTextContainer *)textContainer
|
||||
{
|
||||
int i;
|
||||
NSRect r;
|
||||
|
||||
r = [self _insertionPointRectForCharacterIndex: cindex
|
||||
textContainer: &i];
|
||||
if (i == -1 || textcontainers[i].textContainer != textContainer)
|
||||
return NSZeroRect;
|
||||
|
||||
r.origin.y++;
|
||||
r.size.height -= 2;
|
||||
|
||||
|
@ -644,6 +686,210 @@ anything visible
|
|||
}
|
||||
|
||||
|
||||
-(unsigned int) characterIndexMoving: (GSInsertionPointMovementDirection)direction
|
||||
fromCharacterIndex: (unsigned int)from
|
||||
originalCharacterIndex: (unsigned int)original
|
||||
distance: (float)distance
|
||||
{
|
||||
NSRect from_rect, new_rect;
|
||||
int from_tc, new_tc;
|
||||
int i;
|
||||
unsigned int new;
|
||||
unsigned int length = [_textStorage length];
|
||||
|
||||
/* This call will ensure that layout is built to 'from', and that layout
|
||||
for the line 'from' is in is built. */
|
||||
from_rect = [self _insertionPointRectForCharacterIndex: from
|
||||
textContainer: &from_tc];
|
||||
if (from_tc == -1)
|
||||
{
|
||||
NSLog(@"%s: character index not in any text container",
|
||||
__PRETTY_FUNCTION__);
|
||||
return from;
|
||||
}
|
||||
|
||||
if (direction == GSInsertionPointMoveLeft ||
|
||||
direction == GSInsertionPointMoveRight)
|
||||
{
|
||||
float target;
|
||||
|
||||
if (distance == 0.0)
|
||||
{
|
||||
new = from;
|
||||
if (direction == GSInsertionPointMoveLeft && new > 0)
|
||||
new--;
|
||||
if (direction == GSInsertionPointMoveRight && new < length)
|
||||
new++;
|
||||
|
||||
[self _insertionPointRectForCharacterIndex: new
|
||||
textContainer: &i];
|
||||
|
||||
/* Don't leave the text container. */
|
||||
if (i == from_tc)
|
||||
return new;
|
||||
else
|
||||
return from;
|
||||
}
|
||||
|
||||
/*
|
||||
This is probably very inefficient, but it shouldn't be a bottleneck,
|
||||
and it guarantees that cursor movement matches insertion point
|
||||
positioning. It also lets us do this by character instead of by glyph.
|
||||
*/
|
||||
new = from;
|
||||
if (direction == GSInsertionPointMoveLeft)
|
||||
{
|
||||
target = from_rect.origin.x - distance;
|
||||
while (new > 0)
|
||||
{
|
||||
new_rect = [self _insertionPointRectForCharacterIndex: new - 1
|
||||
textContainer: &new_tc];
|
||||
if (new_tc != from_tc)
|
||||
break;
|
||||
if (new_rect.origin.y != from_rect.origin.y)
|
||||
break;
|
||||
new--;
|
||||
if (NSMaxX(new_rect) <= target)
|
||||
break;
|
||||
}
|
||||
return new;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = from_rect.origin.x + distance;
|
||||
while (new < length)
|
||||
{
|
||||
new_rect = [self _insertionPointRectForCharacterIndex: new + 1
|
||||
textContainer: &new_tc];
|
||||
if (new_tc != from_tc)
|
||||
break;
|
||||
if (new_rect.origin.y != from_rect.origin.y)
|
||||
break;
|
||||
new++;
|
||||
if (NSMinX(new_rect) >= target)
|
||||
break;
|
||||
}
|
||||
return new;
|
||||
}
|
||||
}
|
||||
|
||||
if (direction == GSInsertionPointMoveUp ||
|
||||
direction == GSInsertionPointMoveDown)
|
||||
{
|
||||
NSRect orig_rect;
|
||||
int orig_tc;
|
||||
float target;
|
||||
textcontainer_t *tc;
|
||||
linefrag_t *lf;
|
||||
int i;
|
||||
|
||||
orig_rect = [self _insertionPointRectForCharacterIndex: original
|
||||
textContainer: &orig_tc];
|
||||
if (orig_tc == from_tc)
|
||||
target = orig_rect.origin.x;
|
||||
else
|
||||
target = from_rect.origin.x;
|
||||
|
||||
tc = &textcontainers[from_tc];
|
||||
/* Find first line frag rect on the from line. */
|
||||
for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
|
||||
{
|
||||
if (lf->rect.origin.y == from_rect.origin.y)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we don't have a line frag rect that matches the from position,
|
||||
the from position is probably on the last line, in the extra rect,
|
||||
and i == tc->num_linefrags. The movement direction specific code
|
||||
handles this case, as long as tc->num_linefrags > 0. */
|
||||
if (!tc->num_linefrags)
|
||||
return from; /* Impossible? Should be, since from_tc!=-1. */
|
||||
|
||||
if (direction == GSInsertionPointMoveDown)
|
||||
{
|
||||
[self _doLayoutToContainer: from_tc
|
||||
point: NSMakePoint(target, distance + NSMaxY(from_rect))];
|
||||
/* Find the target line. Move at least (should be up to?)
|
||||
distance, and at least one line. */
|
||||
for (; i < tc->num_linefrags; i++, lf++)
|
||||
if (NSMaxY(lf->rect) >= distance + NSMaxY(from_rect) &&
|
||||
NSMinY(lf->rect) != NSMinY(from_rect))
|
||||
break;
|
||||
|
||||
if (i == tc->num_linefrags)
|
||||
{
|
||||
/* We can't move as far as we want to. In fact, we might not
|
||||
have been able to move at all.
|
||||
TODO: figure out how to handle this
|
||||
*/
|
||||
return from;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Find the target line. Move at least (should be up to?)
|
||||
distance, and at least one line. */
|
||||
for (; i >= 0; i--, lf--)
|
||||
if (NSMinY(lf->rect) <= NSMinY(from_rect) - distance &&
|
||||
NSMinY(lf->rect) != NSMinY(from_rect))
|
||||
break;
|
||||
/* Now we have the last line frag of the target line. Move
|
||||
backwards to the first one. */
|
||||
for (; i > 0; i--, lf--)
|
||||
if (NSMinY(lf->rect) != NSMinY(lf[-1].rect))
|
||||
break;
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
/* We can't move as far as we want to. In fact, we might not
|
||||
have been able to move at all.
|
||||
TODO: figure out how to handle this
|
||||
*/
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we have the first line frag of the target line and the
|
||||
target x position. */
|
||||
new = [self characterRangeForGlyphRange: NSMakeRange(lf->pos, 1)
|
||||
actualGlyphRange: NULL].location;
|
||||
|
||||
/* The first character index might not actually be in this line
|
||||
rect, so move forwards to the first character in the target line. */
|
||||
while (new < length)
|
||||
{
|
||||
new_rect = [self _insertionPointRectForCharacterIndex: new + 1
|
||||
textContainer: &new_tc];
|
||||
if (new_tc > from_tc)
|
||||
break;
|
||||
if (new_rect.origin.y >= lf->rect.origin.y)
|
||||
break;
|
||||
new++;
|
||||
}
|
||||
|
||||
/* Now find the target character in the line. */
|
||||
while (new < length)
|
||||
{
|
||||
new_rect = [self _insertionPointRectForCharacterIndex: new + 1
|
||||
textContainer: &new_tc];
|
||||
if (new_tc != from_tc)
|
||||
break;
|
||||
if (new_rect.origin.y != lf->rect.origin.y)
|
||||
break;
|
||||
new++;
|
||||
if (NSMinX(new_rect) >= target)
|
||||
break;
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
NSLog(@"(%s): invalid direction %i (distance %g)",
|
||||
__PRETTY_FUNCTION__, direction, distance);
|
||||
return from;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -656,7 +902,7 @@ anything visible
|
|||
|
||||
/*
|
||||
If a range passed to a drawing function isn't contained in the text
|
||||
container that containts its first glyph, the range is silently clamped.
|
||||
container that contains its first glyph, the range is silently clamped.
|
||||
My thought with this is that the requested glyphs might not fit in the
|
||||
text container (if it's the last text container, or there's only one).
|
||||
In that case, it isn't really the caller's fault, and drawing as much as
|
||||
|
|
|
@ -2690,6 +2690,7 @@ afterString in order over charRange.
|
|||
}
|
||||
|
||||
[self setSelectionGranularity: NSSelectByCharacter];
|
||||
_layoutManager->_selectionAffinity = affinity;
|
||||
|
||||
/* TODO: Remove the marking from marked text if the new selection is
|
||||
greater than the marked region. */
|
||||
|
@ -2710,7 +2711,7 @@ afterString in order over charRange.
|
|||
|
||||
- (NSSelectionAffinity) selectionAffinity
|
||||
{
|
||||
return NSSelectionAffinityDownstream;
|
||||
return _layoutManager->_selectionAffinity;
|
||||
}
|
||||
|
||||
- (void) setSelectionGranularity: (NSSelectionGranularity)granularity
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <Foundation/NSValue.h>
|
||||
#include <AppKit/NSGraphics.h>
|
||||
#include <AppKit/NSLayoutManager.h>
|
||||
#include <AppKit/NSScrollView.h>
|
||||
#include <AppKit/NSTextStorage.h>
|
||||
#include <AppKit/NSTextView.h>
|
||||
|
||||
|
@ -82,6 +83,9 @@ in the same way that the attributes of the text are changed.
|
|||
sure this holds.)
|
||||
|
||||
|
||||
TODO: can the selected range's location be NSNotFound? when?
|
||||
|
||||
|
||||
|
||||
Not all user actions are here. Exceptions:
|
||||
|
||||
|
@ -152,7 +156,9 @@ send -shouldChangeTextInRange:replacementString: or -didChangeText.
|
|||
&& ([_delegate textShouldEndEditing: self] == NO))
|
||||
return;
|
||||
|
||||
/* TODO: insertion point */
|
||||
/* TODO: insertion point.
|
||||
doesn't the -resignFirstResponder take care of that?
|
||||
*/
|
||||
|
||||
number = [NSNumber numberWithInt: textMovement];
|
||||
uiDictionary = [NSDictionary dictionaryWithObject: number
|
||||
|
@ -549,6 +555,7 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
return;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
//[self insertText: @"\t"];
|
||||
}
|
||||
|
||||
|
@ -639,237 +646,265 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
TODO: find out what affinity is supposed to mean
|
||||
|
||||
My current assumption:
|
||||
|
||||
Affinity deals with which direction we are selecting in, ie. which end of
|
||||
the selected range is the moving end, and which is the anchor.
|
||||
|
||||
NSSelectionAffinityUpstream means that the minimum index of the selected
|
||||
range is moving (ie. _selected_range.location).
|
||||
|
||||
NSSelectionAffinityDownstream means that the maximum index of the selected
|
||||
range is moving (ie. _selected_range.location+_selected_range.length).
|
||||
|
||||
Thus, when moving and selecting, we use the affinity to find out which end
|
||||
of the selected range to move, and after moving, we compare the character
|
||||
index we moved to with the anchor and set the range and affinity.
|
||||
|
||||
|
||||
The affinity is important when making keyboard selection have sensible
|
||||
behavior. Example:
|
||||
|
||||
If, in the string "abcd", the insertion point is between the "c" and the "d"
|
||||
(selected range is (3,0)), and the user hits shift-left twice, we select
|
||||
the "c" and "b" (1,2) and set the affinity to NSSelectionAffinityUpstream.
|
||||
If the user hits shift-right, only the "c" will be selected (2,1).
|
||||
|
||||
If the insertion point is between the "a" and the "b" (1,0) and the user hits
|
||||
shift-right twice, we again select the "b" and "c" (1,2), but the affinity
|
||||
is NSSelectionAffinityDownstream. If the user hits shift-right, the "d" is
|
||||
added to the selection (1,3).
|
||||
|
||||
*/
|
||||
|
||||
-(unsigned int) _movementOrigin
|
||||
{
|
||||
if (_layoutManager->_selectionAffinity == NSSelectionAffinityUpstream)
|
||||
return _layoutManager->_selected_range.location;
|
||||
else
|
||||
return NSMaxRange(_layoutManager->_selected_range);
|
||||
}
|
||||
|
||||
-(void) _moveTo: (unsigned int)cindex
|
||||
select: (BOOL)select
|
||||
{
|
||||
if (select)
|
||||
{
|
||||
unsigned int anchor;
|
||||
if (_layoutManager->_selectionAffinity == NSSelectionAffinityDownstream)
|
||||
anchor = _layoutManager->_selected_range.location;
|
||||
else
|
||||
anchor = NSMaxRange(_layoutManager->_selected_range);
|
||||
|
||||
if (anchor < cindex)
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange(anchor, cindex - anchor)
|
||||
affinity: NSSelectionAffinityDownstream
|
||||
stillSelecting: NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange(cindex, anchor - cindex)
|
||||
affinity: NSSelectionAffinityUpstream
|
||||
stillSelecting: NO];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange(cindex, 0)];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) _move: (GSInsertionPointMovementDirection)direction
|
||||
distance: (float)distance
|
||||
select: (BOOL)select
|
||||
{
|
||||
unsigned int cindex;
|
||||
|
||||
cindex = [self _movementOrigin];
|
||||
cindex = [_layoutManager characterIndexMoving: direction
|
||||
fromCharacterIndex: cindex
|
||||
originalCharacterIndex: cindex /* TODO */
|
||||
distance: distance];
|
||||
[self _moveTo: cindex
|
||||
select: select];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Insertion point movement actions.
|
||||
|
||||
TODO: should implement: (D marks done)
|
||||
|
||||
D-(void) moveBackward: (id)sender;
|
||||
D-(void) moveBackwardAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveForward: (id)sender;
|
||||
D-(void) moveForwardAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveDown: (id)sender;
|
||||
D-(void) moveDownAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveUp: (id)sender;
|
||||
D-(void) moveUpAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveLeft: (id)sender;
|
||||
D-(void) moveRight: (id)sender;
|
||||
|
||||
D-(void) moveWordBackward: (id)sender;
|
||||
D-(void) moveWordBackwardAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveWordForward: (id)sender;
|
||||
D-(void) moveWordForwardAndModifySelection: (id)sender;
|
||||
|
||||
D-(void) moveToBeginningOfDocument: (id)sender;
|
||||
D-(void) moveToEndOfDocument: (id)sender;
|
||||
|
||||
D-(void) moveToBeginningOfLine: (id)sender;
|
||||
D-(void) moveToEndOfLine: (id)sender;
|
||||
|
||||
-(void) moveToBeginningOfParagraph: (id)sender;
|
||||
-(void) moveToEndOfParagraph: (id)sender;
|
||||
|
||||
TODO: think hard about behavior for pageUp: and pageDown:
|
||||
D-(void) pageDown: (id)sender;
|
||||
D-(void) pageUp: (id)sender;
|
||||
|
||||
|
||||
TODO: some of these used to do nothing if self is a field editor. should
|
||||
check if there was a reason for that.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
-(void) moveUp: (id)sender
|
||||
{
|
||||
/* float originalInsertionPoint;
|
||||
float savedOriginalInsertionPoint;
|
||||
float startingY;
|
||||
unsigned newLocation;*/
|
||||
|
||||
if (_tf.is_field_editor) /* TODO: why? */
|
||||
return;
|
||||
|
||||
/* Do nothing if we are at beginning of text */
|
||||
if (_layoutManager->_selected_range.location == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 /* TODO */
|
||||
/* Read from memory the horizontal position we aim to move the cursor
|
||||
at on the next line */
|
||||
savedOriginalInsertionPoint = _originalInsertPoint;
|
||||
originalInsertionPoint = _originalInsertPoint;
|
||||
|
||||
/* Ask the layout manager to compute where to go */
|
||||
startingY = NSMidY (_insertionPointRect);
|
||||
|
||||
/* consider textContainerInset */
|
||||
startingY -= _textContainerInset.height;
|
||||
originalInsertionPoint -= _textContainerInset.width;
|
||||
|
||||
newLocation = [_layoutManager
|
||||
_charIndexForInsertionPointMovingFromY: startingY
|
||||
bestX: originalInsertionPoint
|
||||
up: YES
|
||||
textContainer: _textContainer];
|
||||
|
||||
/* Move the insertion point */
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
|
||||
/* Restore the _originalInsertPoint (which was changed
|
||||
by setSelectedRange:) because we don't want it to change between
|
||||
moveUp:/moveDown: operations. */
|
||||
_originalInsertPoint = savedOriginalInsertionPoint;
|
||||
#endif
|
||||
[self _move: GSInsertionPointMoveUp
|
||||
distance: 0.0
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveUpAndModifySelection: (id)sender
|
||||
{
|
||||
[self _move: GSInsertionPointMoveUp
|
||||
distance: 0.0
|
||||
select: YES];
|
||||
}
|
||||
|
||||
-(void) moveDown: (id)sender
|
||||
{
|
||||
/* float originalInsertionPoint;
|
||||
float savedOriginalInsertionPoint;
|
||||
float startingY;
|
||||
unsigned newLocation;*/
|
||||
|
||||
if (_tf.is_field_editor)
|
||||
return;
|
||||
|
||||
/* Do nothing if we are at end of text */
|
||||
if (_layoutManager->_selected_range.location == [_textStorage length])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 /* TODO */
|
||||
/* Read from memory the horizontal position we aim to move the cursor
|
||||
at on the next line */
|
||||
savedOriginalInsertionPoint = _originalInsertPoint;
|
||||
originalInsertionPoint = _originalInsertPoint;
|
||||
|
||||
/* Ask the layout manager to compute where to go */
|
||||
startingY = NSMidY (_insertionPointRect);
|
||||
|
||||
/* consider textContainerInset */
|
||||
startingY -= _textContainerInset.height;
|
||||
originalInsertionPoint -= _textContainerInset.width;
|
||||
|
||||
newLocation = [_layoutManager
|
||||
_charIndexForInsertionPointMovingFromY: startingY
|
||||
bestX: originalInsertionPoint
|
||||
up: NO
|
||||
textContainer: _textContainer];
|
||||
|
||||
/* Move the insertion point */
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
|
||||
/* Restore the _originalInsertPoint (which was changed
|
||||
by setSelectedRange:) because we don't want it to change between
|
||||
moveUp:/moveDown: operations. */
|
||||
_originalInsertPoint = savedOriginalInsertionPoint;
|
||||
#endif
|
||||
[self _move: GSInsertionPointMoveDown
|
||||
distance: 0.0
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveDownAndModifySelection: (id)sender
|
||||
{
|
||||
[self _move: GSInsertionPointMoveDown
|
||||
distance: 0.0
|
||||
select: YES];
|
||||
}
|
||||
|
||||
-(void) moveLeft: (id)sender
|
||||
{
|
||||
unsigned newLocation;
|
||||
|
||||
/* Do nothing if we are at beginning of text with no selection */
|
||||
if (_layoutManager->_selected_range.location == 0 && _layoutManager->_selected_range.length == 0)
|
||||
return;
|
||||
|
||||
if (_layoutManager->_selected_range.location == 0)
|
||||
{
|
||||
newLocation = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLocation = _layoutManager->_selected_range.location - 1;
|
||||
}
|
||||
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
[self _move: GSInsertionPointMoveLeft
|
||||
distance: 0.0
|
||||
select: NO];
|
||||
}
|
||||
|
||||
-(void) moveRight: (id)sender
|
||||
{
|
||||
unsigned int length = [_textStorage length];
|
||||
unsigned newLocation;
|
||||
|
||||
/* Do nothing if we are at end of text */
|
||||
if (_layoutManager->_selected_range.location == length)
|
||||
return;
|
||||
|
||||
newLocation = MIN (NSMaxRange (_layoutManager->_selected_range) + 1, length);
|
||||
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
[self _move: GSInsertionPointMoveRight
|
||||
distance: 0.0
|
||||
select: NO];
|
||||
}
|
||||
|
||||
|
||||
-(void) moveBackward: (id)sender
|
||||
{
|
||||
unsigned int to = [self _movementOrigin];
|
||||
if (to == 0)
|
||||
return;
|
||||
to--;
|
||||
[self _moveTo: to
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveBackwardAndModifySelection: (id)sender
|
||||
{
|
||||
NSRange newRange;
|
||||
|
||||
/* Do nothing if we are at beginning of text. */
|
||||
if (_layoutManager->_selected_range.location == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Turn to select by character. */
|
||||
[self setSelectionGranularity: NSSelectByCharacter];
|
||||
|
||||
/* Extend the selection on the left. */
|
||||
newRange = NSMakeRange (_layoutManager->_selected_range.location - 1,
|
||||
_layoutManager->_selected_range.length + 1);
|
||||
|
||||
newRange = [self selectionRangeForProposedRange: newRange
|
||||
granularity: NSSelectByCharacter];
|
||||
|
||||
[self setSelectedRange: newRange];
|
||||
unsigned int to = [self _movementOrigin];
|
||||
if (to == 0)
|
||||
return;
|
||||
to--;
|
||||
[self _moveTo: to
|
||||
select: YES];
|
||||
}
|
||||
|
||||
-(void) moveForward: (id)sender
|
||||
{
|
||||
unsigned int to = [self _movementOrigin];
|
||||
if (to == [_textStorage length])
|
||||
return;
|
||||
to++;
|
||||
[self _moveTo: to
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveForwardAndModifySelection: (id)sender
|
||||
{
|
||||
unsigned int length = [_textStorage length];
|
||||
NSRange newRange;
|
||||
|
||||
/* Do nothing if we are at end of text */
|
||||
if (_layoutManager->_selected_range.location == length)
|
||||
unsigned int to = [self _movementOrigin];
|
||||
if (to == [_textStorage length])
|
||||
return;
|
||||
|
||||
/* Turn to select by character. */
|
||||
[self setSelectionGranularity: NSSelectByCharacter];
|
||||
|
||||
/* Extend the selection on the right. */
|
||||
newRange = NSMakeRange (_layoutManager->_selected_range.location,
|
||||
_layoutManager->_selected_range.length + 1);
|
||||
|
||||
newRange = [self selectionRangeForProposedRange: newRange
|
||||
granularity: NSSelectByCharacter];
|
||||
|
||||
[self setSelectedRange: newRange];
|
||||
to++;
|
||||
[self _moveTo: to
|
||||
select: YES];
|
||||
}
|
||||
|
||||
-(void) moveWordBackward: (id)sender
|
||||
{
|
||||
unsigned newLocation;
|
||||
|
||||
unsigned int newLocation;
|
||||
newLocation = [_textStorage nextWordFromIndex: _layoutManager->_selected_range.location
|
||||
forward: NO];
|
||||
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
[self _moveTo: newLocation
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveWordBackwardAndModifySelection: (id)sender
|
||||
{
|
||||
unsigned int newLocation;
|
||||
newLocation = [_textStorage nextWordFromIndex: _layoutManager->_selected_range.location
|
||||
forward: NO];
|
||||
[self _moveTo: newLocation
|
||||
select: YES];
|
||||
}
|
||||
|
||||
-(void) moveWordForward: (id)sender
|
||||
{
|
||||
unsigned newLocation;
|
||||
|
||||
newLocation = [_textStorage nextWordFromIndex: _layoutManager->_selected_range.location
|
||||
forward: YES];
|
||||
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0)];
|
||||
[self _moveTo: newLocation
|
||||
select: NO];
|
||||
}
|
||||
|
||||
-(void) moveWordBackwardAndModifySelection: (id)sender
|
||||
{
|
||||
unsigned newLocation;
|
||||
NSRange newRange;
|
||||
|
||||
[self setSelectionGranularity: NSSelectByWord];
|
||||
|
||||
newLocation = [_textStorage nextWordFromIndex: _layoutManager->_selected_range.location
|
||||
forward: NO];
|
||||
|
||||
newRange = NSMakeRange (newLocation,
|
||||
NSMaxRange (_layoutManager->_selected_range) - newLocation);
|
||||
|
||||
newRange = [self selectionRangeForProposedRange: newRange
|
||||
granularity: NSSelectByCharacter];
|
||||
|
||||
[self setSelectedRange: newRange];
|
||||
}
|
||||
|
||||
-(void) moveWordForwardAndModifySelection: (id)sender
|
||||
{
|
||||
unsigned newMaxRange;
|
||||
NSRange newRange;
|
||||
|
||||
[self setSelectionGranularity: NSSelectByWord];
|
||||
|
||||
newMaxRange = [_textStorage nextWordFromIndex: NSMaxRange (_layoutManager->_selected_range)
|
||||
forward: YES];
|
||||
|
||||
newRange = NSMakeRange (_layoutManager->_selected_range.location,
|
||||
newMaxRange - _layoutManager->_selected_range.location);
|
||||
|
||||
newRange = [self selectionRangeForProposedRange: newRange
|
||||
granularity: NSSelectByCharacter];
|
||||
|
||||
[self setSelectedRange: newRange];
|
||||
unsigned newLocation;
|
||||
newLocation = [_textStorage nextWordFromIndex: _layoutManager->_selected_range.location
|
||||
forward: NO];
|
||||
[self _moveTo: newLocation
|
||||
select: NO];
|
||||
}
|
||||
|
||||
-(void) moveToBeginningOfDocument: (id)sender
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange (0, 0)];
|
||||
[self _moveTo: 0
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveToEndOfDocument: (id)sender
|
||||
{
|
||||
[self _moveTo: [_textStorage length]
|
||||
select: NO];
|
||||
}
|
||||
|
||||
|
||||
-(void) moveToBeginningOfParagraph: (id)sender
|
||||
{
|
||||
|
@ -879,38 +914,6 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
[self setSelectedRange: NSMakeRange (aRange.location, 0)];
|
||||
}
|
||||
|
||||
-(void) moveToBeginningOfLine: (id)sender
|
||||
{
|
||||
NSRange aRange;
|
||||
NSRect ignored;
|
||||
|
||||
/* We do nothing if we are at the beginning of the text. */
|
||||
if (_layoutManager->_selected_range.location == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ignored = [_layoutManager lineFragmentRectForGlyphAtIndex:
|
||||
_layoutManager->_selected_range.location
|
||||
effectiveRange: &aRange];
|
||||
|
||||
[self setSelectedRange: NSMakeRange (aRange.location, 0)];
|
||||
}
|
||||
|
||||
-(void) moveToEndOfDocument: (id)sender
|
||||
{
|
||||
unsigned length = [_textStorage length];
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange (length, 0)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setSelectedRange: NSMakeRange (0, 0)];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) moveToEndOfParagraph: (id)sender
|
||||
{
|
||||
NSRange aRange;
|
||||
|
@ -963,72 +966,23 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
[self setSelectedRange: NSMakeRange (newLocation, 0) ];
|
||||
}
|
||||
|
||||
|
||||
/* TODO: this is only the beginning and end of lines if lines are horizontal
|
||||
and layout is left-to-right */
|
||||
-(void) moveToBeginningOfLine: (id)sender
|
||||
{
|
||||
[self _move: GSInsertionPointMoveLeft
|
||||
distance: 1e8
|
||||
select: NO];
|
||||
}
|
||||
-(void) moveToEndOfLine: (id)sender
|
||||
{
|
||||
NSRect ignored;
|
||||
NSRange line, glyphs;
|
||||
unsigned newLocation;
|
||||
unsigned maxRange;
|
||||
|
||||
/* We do nothing if we are at the end of the text. */
|
||||
if (_layoutManager->_selected_range.location == [_textStorage length])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ignored = [_layoutManager lineFragmentRectForGlyphAtIndex:
|
||||
_layoutManager->_selected_range.location
|
||||
effectiveRange: &glyphs];
|
||||
|
||||
line = [_layoutManager characterRangeForGlyphRange: glyphs
|
||||
actualGlyphRange: NULL];
|
||||
|
||||
maxRange = NSMaxRange (line);
|
||||
|
||||
if (maxRange == 0)
|
||||
{
|
||||
/* Beginning of text is special only for technical reasons -
|
||||
since maxRange is an unsigned, we can't safely subtract 1
|
||||
from it if it is 0. */
|
||||
newLocation = maxRange;
|
||||
}
|
||||
else if (maxRange == [_textStorage length])
|
||||
{
|
||||
/* End of text is special - we want the insertion point to
|
||||
appear *after* the last character, which means as if before
|
||||
the next (virtual) character after the end of text ... unless
|
||||
the last character is a newline, and we are trying to go to
|
||||
the end of the line which is displayed as the
|
||||
one-before-the-last. (Please note that we do not check for
|
||||
spaces - spaces are ok, we want to move after the last space)
|
||||
Please note (maxRange - 1) is a valid char since the maxRange
|
||||
== 0 case has already been eliminated. */
|
||||
unichar u = [[_textStorage string] characterAtIndex: (maxRange - 1)];
|
||||
if (u == '\n' || u == '\r')
|
||||
{
|
||||
newLocation = maxRange - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLocation = maxRange;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Else, we want the insertion point to appear before the last
|
||||
character in the line range. Normally the last character in
|
||||
the line range is a space or a newline. */
|
||||
newLocation = maxRange - 1;
|
||||
}
|
||||
|
||||
if (newLocation < line.location)
|
||||
{
|
||||
newLocation = line.location;
|
||||
}
|
||||
|
||||
[self setSelectedRange: NSMakeRange (newLocation, 0) ];
|
||||
[self _move: GSInsertionPointMoveRight
|
||||
distance: 1e8
|
||||
select: NO];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to move the selection/insertion point down one page of the
|
||||
* visible rect in the receiver while trying to maintain the
|
||||
|
@ -1037,23 +991,9 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
*/
|
||||
-(void) pageDown: (id)sender
|
||||
{
|
||||
#if 0 /* TODO */
|
||||
float cachedInsertPointX;
|
||||
float scrollDelta;
|
||||
float oldOriginY;
|
||||
float newOriginY;
|
||||
unsigned glyphIDX;
|
||||
unsigned charIDX;
|
||||
NSPoint iPoint;
|
||||
|
||||
if (_tf.is_field_editor)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Save the current horizontal position cache as we will implictly
|
||||
* change it later.
|
||||
*/
|
||||
// cachedInsertPointX = _originalInsertPoint;
|
||||
|
||||
/*
|
||||
* Scroll; also determine how far to move the insertion point.
|
||||
|
@ -1070,34 +1010,12 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
* insertion point to the last line when the user clicks
|
||||
* 'PageDown' in that case ?
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate new insertion point.
|
||||
*/
|
||||
iPoint.x = _originalInsertPoint - _textContainerInset.width;
|
||||
iPoint.y = NSMidY(_insertionPointRect) - _textContainerInset.height;
|
||||
iPoint.y += scrollDelta;
|
||||
|
||||
/*
|
||||
* Ask the layout manager to compute where to go.
|
||||
*/
|
||||
glyphIDX = [_layoutManager glyphIndexForPoint: iPoint
|
||||
inTextContainer: _textContainer];
|
||||
charIDX = [_layoutManager characterIndexForGlyphAtIndex: glyphIDX];
|
||||
|
||||
/*
|
||||
* Move the insertion point (implicitly changing
|
||||
* _originalInsertPoint).
|
||||
*/
|
||||
[self setSelectedRange: NSMakeRange(charIDX, 0)];
|
||||
|
||||
/*
|
||||
* Restore the _originalInsertPoint because we do not want it to
|
||||
* change between moveUp:/moveDown:/pageUp:/pageDown: operations.
|
||||
*/
|
||||
_originalInsertPoint = cachedInsertPointX;
|
||||
#endif
|
||||
[self _move: GSInsertionPointMoveDown
|
||||
distance: scrollDelta
|
||||
select: NO];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1108,23 +1026,9 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
*/
|
||||
-(void) pageUp: (id)sender
|
||||
{
|
||||
#if 0 /* TODO */
|
||||
float cachedInsertPointX;
|
||||
float scrollDelta;
|
||||
float oldOriginY;
|
||||
float newOriginY;
|
||||
unsigned glyphIDX;
|
||||
unsigned charIDX;
|
||||
NSPoint iPoint;
|
||||
|
||||
if (_tf.is_field_editor)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Save the current horizontal position cache as we will implictly
|
||||
* change it later.
|
||||
*/
|
||||
cachedInsertPointX = _originalInsertPoint;
|
||||
|
||||
/*
|
||||
* Scroll; also determine how far to move the insertion point.
|
||||
|
@ -1141,37 +1045,17 @@ static NSNumber *float_plus_one(NSNumber *cur)
|
|||
* insertion point to the first line when the user clicks
|
||||
* 'PageUp' in that case ?
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate new insertion point.
|
||||
*/
|
||||
iPoint.x = _originalInsertPoint - _textContainerInset.width;
|
||||
iPoint.y = NSMidY(_insertionPointRect) - _textContainerInset.height;
|
||||
iPoint.y += scrollDelta;
|
||||
|
||||
/*
|
||||
* Ask the layout manager to compute where to go.
|
||||
*/
|
||||
glyphIDX = [_layoutManager glyphIndexForPoint: iPoint
|
||||
inTextContainer: _textContainer];
|
||||
charIDX = [_layoutManager characterIndexForGlyphAtIndex: glyphIDX];
|
||||
|
||||
/*
|
||||
* Move the insertion point (implicitly changing
|
||||
* _originalInsertPoint).
|
||||
*/
|
||||
[self setSelectedRange: NSMakeRange(charIDX, 0)];
|
||||
|
||||
/*
|
||||
* Restore the _originalInsertPoint because we do not want it to
|
||||
* change between moveUp:/moveDown:/pageUp:/pageDown: operations.
|
||||
*/
|
||||
_originalInsertPoint = cachedInsertPointX;
|
||||
#endif
|
||||
[self _move: GSInsertionPointMoveUp
|
||||
distance: -scrollDelta
|
||||
select: NO];
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(void) scrollLineDown: (id)sender
|
||||
{
|
||||
// TODO
|
||||
|
|
Loading…
Reference in a new issue