2003-01-26 19:21:40 +00:00
|
|
|
/*
|
|
|
|
NSLayoutManager.m
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-02-16 14:54:14 +00:00
|
|
|
Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
Author: Alexander Malmberg <alexander@malmberg.org>
|
2003-02-16 14:54:14 +00:00
|
|
|
Date: November 2002 - February 2003
|
|
|
|
|
|
|
|
Parts based on the old NSLayoutManager.m:
|
|
|
|
Author: Jonathan Gapen <jagapen@smithlab.chem.wisc.edu>
|
|
|
|
Date: July 1999
|
|
|
|
Author: Michael Hanni <mhanni@sprintmail.com>
|
|
|
|
Date: August 1999
|
|
|
|
Author: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
Date: January 2001
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-10-29 21:16:17 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1999-07-15 17:32:38 +00:00
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-10 04:01:49 +00:00
|
|
|
version 2 of the License, or (at your option) any later version.
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2007-10-29 21:16:17 +00:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2007-10-29 21:16:17 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1999-07-15 17:32:38 +00:00
|
|
|
License along with this library; see the file COPYING.LIB.
|
2007-10-29 21:16:17 +00:00
|
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
1999-07-15 17:32:38 +00:00
|
|
|
*/
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/*
|
|
|
|
TODO: document exact requirements on typesetting for this class
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
Roughly:
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
Line frag rects arranged in lines in which all line frag rects have same
|
|
|
|
y-origin and height. Lines must not overlap vertically, and must be arranged
|
|
|
|
with strictly increasing y-origin. Line frag rects go left->right, as do
|
|
|
|
points inside line frag rects.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
"Nominally spaced", to this layout manager, is described at:
|
|
|
|
http://wiki.gnustep.org/index.php/NominallySpacedGlyphs
|
2003-02-09 20:13:16 +00:00
|
|
|
|
2003-02-18 00:35:23 +00:00
|
|
|
Lines are laid out as one unit. Ie. we never do layout for only a part of a
|
|
|
|
line, or invalidate only some line frags in a line.
|
|
|
|
|
|
|
|
Also, we assume that the limit of context on layout is the previous line.
|
|
|
|
Thus, when we invalidate layout, we invalidate all lines with invalidated
|
|
|
|
characters, and the line before the first invalidated line, and
|
|
|
|
soft-invalidate everything after the last invalidated line.
|
|
|
|
|
|
|
|
Consider:
|
|
|
|
|
|
|
|
|... |
|
|
|
|
|foo bar zot |
|
|
|
|
|abcdefghij |
|
|
|
|
|... |
|
|
|
|
|
|
|
|
If we insert a space between the 'a' and the 'b' in "abcd...", the correct
|
|
|
|
result is:
|
|
|
|
|
|
|
|
|... |
|
|
|
|
|foo bar zot a |
|
|
|
|
|bcdefghij |
|
|
|
|
|... |
|
|
|
|
|
|
|
|
and to get this, we must invalidate the previous line.
|
|
|
|
|
|
|
|
TODO: This is an important assumption, and the typesetter needs to make
|
|
|
|
sure that it holds. I'm not entirely convinced that it holds for standard
|
|
|
|
latin-text layout, but I haven't been able to come up with any
|
|
|
|
counter-examples. If it turns out not to hold, we'll have to fix
|
|
|
|
invalidation here (invalidate the entire paragraph? not good for
|
|
|
|
performance, but correctness is more important), or change the typesetter
|
|
|
|
behavior.
|
|
|
|
|
|
|
|
Another assumption is that each text container will contain at least one
|
|
|
|
line frag (unless there are no more glyphs to typeset).
|
2003-07-05 16:04:05 +00:00
|
|
|
TODO: this doesn't hold for containers with 0 height or 0 width. need to
|
|
|
|
test. rare case, though
|
2003-02-18 00:35:23 +00:00
|
|
|
|
|
|
|
|
2003-02-09 20:13:16 +00:00
|
|
|
|
|
|
|
TODO: We often need to deal with the case where a glyph can't be typeset
|
|
|
|
(because there's nowhere to typeset it, eg. all text containers are full).
|
|
|
|
Need to figure out how to handle it.
|
2003-02-16 21:40:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
TODO: Don't do linear searches through the line frags if avoidable. Some
|
|
|
|
cases are considerably more important than others, and should be fixed
|
|
|
|
first. Remaining cases, highest priority first:
|
|
|
|
|
|
|
|
-glyphIndexForPoint:inTextContainer:fractionOfDistanceThroughGlyph:
|
|
|
|
Used when selecting with the mouse, and called for every event.
|
|
|
|
|
|
|
|
-characterIndexMoving:fromCharacterIndex:originalCharacterIndex:distance:
|
|
|
|
Keyboard insertion point movement. Performance isn't important.
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
2003-02-03 01:52:27 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
2010-12-30 15:29:05 +00:00
|
|
|
#import <Foundation/NSEnumerator.h>
|
|
|
|
#import <Foundation/NSException.h>
|
2011-03-12 07:45:14 +00:00
|
|
|
#import <Foundation/NSValue.h>
|
2010-12-30 15:29:05 +00:00
|
|
|
#import "AppKit/NSAttributedString.h"
|
2011-06-11 12:01:59 +00:00
|
|
|
#import "AppKit/NSBezierPath.h"
|
2010-12-30 15:29:05 +00:00
|
|
|
#import "AppKit/NSColor.h"
|
|
|
|
#import "AppKit/NSImage.h"
|
2011-06-11 12:01:59 +00:00
|
|
|
#import "AppKit/NSKeyValueBinding.h"
|
2010-12-30 15:29:05 +00:00
|
|
|
#import "AppKit/NSLayoutManager.h"
|
|
|
|
#import "AppKit/NSParagraphStyle.h"
|
|
|
|
#import "AppKit/NSRulerMarker.h"
|
|
|
|
#import "AppKit/NSTextContainer.h"
|
|
|
|
#import "AppKit/NSTextStorage.h"
|
|
|
|
#import "AppKit/NSWindow.h"
|
|
|
|
#import "AppKit/DPSOperators.h"
|
|
|
|
|
|
|
|
#import "GNUstepGUI/GSLayoutManager_internal.h"
|
2011-07-11 08:40:31 +00:00
|
|
|
#import "GSBindingHelpers.h"
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
2011-03-12 07:45:14 +00:00
|
|
|
@interface NSLayoutManager (spelling)
|
|
|
|
|
|
|
|
-(void) _drawSpellingState: (NSInteger)spellingState
|
|
|
|
forGylphRange: (NSRange)range
|
|
|
|
lineFragmentRect: (NSRect)fragmentRect
|
|
|
|
lineFragmentGlyphRange: (NSRange)fragmentGlyphRange
|
|
|
|
containerOrigin: (NSPoint)containerOrigin;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2006-11-06 10:19:20 +00:00
|
|
|
@interface NSLayoutManager (LayoutHelpers)
|
2003-01-26 19:21:40 +00:00
|
|
|
-(void) _doLayoutToContainer: (int)cindex point: (NSPoint)p;
|
|
|
|
@end
|
|
|
|
|
2006-11-06 10:19:20 +00:00
|
|
|
@implementation NSLayoutManager (LayoutHelpers)
|
2003-01-26 19:21:40 +00:00
|
|
|
-(void) _doLayoutToContainer: (int)cindex point: (NSPoint)p
|
2001-01-11 19:09:02 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
[self _doLayout];
|
2001-01-11 19:09:02 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
@end
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2011-03-09 05:28:47 +00:00
|
|
|
/**
|
|
|
|
* Temporary attributes are implemented using an NSMutableAttributedString
|
|
|
|
* stored in the _temporaryAttributes ivar. We only care about this attributed
|
2011-03-10 08:22:44 +00:00
|
|
|
* string's attributes, not its characters, so to save space,
|
|
|
|
* _temporaryAttributes is initialized with an instance of the following
|
|
|
|
* NSMutableString subclass, which doesn't store any character data.
|
|
|
|
*
|
|
|
|
* WARNING ... it's an unofficial implementation detail that the
|
|
|
|
* GSMutableAttributedString class creates it's internal storage by taking
|
|
|
|
* a mutable copy of its initialisation argument, and we have a comment in
|
|
|
|
* the source to warn about it. If we change the behavior here, we should
|
|
|
|
* remove the warning comment from the source in base.
|
2011-03-09 05:28:47 +00:00
|
|
|
*/
|
|
|
|
@interface GSDummyMutableString : NSMutableString
|
|
|
|
{
|
|
|
|
NSUInteger _length;
|
|
|
|
}
|
|
|
|
- (id)initWithLength: (NSUInteger)aLength;
|
|
|
|
@end
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/* Helper for searching for the line frag of a glyph. */
|
|
|
|
#define LINEFRAG_FOR_GLYPH(glyph) \
|
|
|
|
do { \
|
|
|
|
int lo, hi, mid; \
|
|
|
|
\
|
|
|
|
lf = tc->linefrags; \
|
2005-11-16 11:34:25 +00:00
|
|
|
for (lo = 0, hi = tc->num_linefrags - 1; lo < hi;) \
|
2003-02-17 01:13:27 +00:00
|
|
|
{ \
|
|
|
|
mid = (lo + hi) / 2; \
|
|
|
|
if (lf[mid].pos > glyph) \
|
|
|
|
hi = mid - 1; \
|
|
|
|
else if (lf[mid].pos + lf[mid].length <= glyph) \
|
|
|
|
lo = mid + 1; \
|
|
|
|
else \
|
|
|
|
lo = hi = mid; \
|
|
|
|
} \
|
|
|
|
lf = &lf[lo]; \
|
|
|
|
i = lo; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@implementation NSLayoutManager (layout)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2010-11-03 20:26:30 +00:00
|
|
|
-(NSPoint) locationForGlyphAtIndex: (NSUInteger)glyphIndex
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
|
|
|
NSRange r;
|
|
|
|
NSPoint p;
|
2010-11-03 20:26:30 +00:00
|
|
|
NSUInteger i;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
r = [self rangeOfNominallySpacedGlyphsContainingIndex: glyphIndex
|
|
|
|
startLocation: &p];
|
2003-02-09 20:13:16 +00:00
|
|
|
if (r.location == NSNotFound)
|
|
|
|
{
|
|
|
|
/* The glyph hasn't been typeset yet, probably because there isn't
|
2003-02-17 01:13:27 +00:00
|
|
|
enough space in the text containers to fit it. */
|
2003-02-16 21:40:47 +00:00
|
|
|
return NSMakePoint(0, 0);
|
2003-02-09 20:13:16 +00:00
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2016-10-23 20:13:37 +00:00
|
|
|
for (i = r.location; i < glyphIndex; i++)
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
2016-10-23 20:13:37 +00:00
|
|
|
p.x += [self advancementForGlyphAtIndex: i].width;
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
|
|
|
return p;
|
2001-01-11 19:09:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(void) textContainerChangedTextView: (NSTextContainer *)aContainer
|
2001-01-11 19:09:02 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO: what do we do here? invalidate the displayed range for that
|
|
|
|
container? necessary? */
|
|
|
|
int i;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* NSTextContainer will send the necessary messages to update the text
|
|
|
|
view that was disconnected from us. */
|
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
|
|
|
{
|
|
|
|
[[textcontainers[i].textContainer textView] _updateMultipleTextViews];
|
|
|
|
if (textcontainers[i].textContainer == aContainer)
|
|
|
|
{
|
|
|
|
[[aContainer textView] setNeedsDisplay: YES];
|
|
|
|
}
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(NSRect *) rectArrayForGlyphRange: (NSRange)glyphRange
|
|
|
|
withinSelectedGlyphRange: (NSRange)selGlyphRange
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
2010-11-03 20:26:30 +00:00
|
|
|
rectCount: (NSUInteger *)rectCount
|
2001-01-11 19:09:02 +00:00
|
|
|
{
|
2010-11-03 20:26:30 +00:00
|
|
|
NSUInteger last;
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
|
|
|
int num_rects;
|
|
|
|
float x0, x1;
|
|
|
|
NSRect r;
|
|
|
|
|
2003-02-10 14:14:34 +00:00
|
|
|
*rectCount = 0;
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
2003-02-03 01:52:27 +00:00
|
|
|
//printf("container %i %@, %i+%i\n",i,tc->textContainer,tc->pos,tc->length);
|
2003-02-10 14:14:34 +00:00
|
|
|
[self _doLayoutToGlyph: NSMaxRange(glyphRange) - 1];
|
2003-02-03 01:52:27 +00:00
|
|
|
//printf(" now %i+%i\n",tc->pos,tc->length);
|
2003-02-10 14:14:34 +00:00
|
|
|
if (i == num_textcontainers)
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-28 16:44:53 +00:00
|
|
|
if (i == num_textcontainers)
|
|
|
|
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
2003-01-26 19:21:40 +00:00
|
|
|
return NULL;
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-02-10 14:14:34 +00:00
|
|
|
/* Silently clamp range to the text container.
|
|
|
|
TODO: is this good? */
|
|
|
|
if (tc->pos > glyphRange.location)
|
|
|
|
{
|
|
|
|
if (tc->pos > NSMaxRange(glyphRange))
|
|
|
|
return NULL;
|
|
|
|
glyphRange.length = NSMaxRange(glyphRange) - tc->pos;
|
|
|
|
glyphRange.location = tc->pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tc->pos + tc->length < NSMaxRange(glyphRange))
|
|
|
|
{
|
|
|
|
if (tc->pos + tc->length < glyphRange.location)
|
|
|
|
return NULL;
|
|
|
|
glyphRange.length = tc->pos + tc->length - glyphRange.location;
|
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!glyphRange.length)
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
return NULL;
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-02-10 14:14:34 +00:00
|
|
|
last = NSMaxRange(glyphRange);
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
num_rects = 0;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
LINEFRAG_FOR_GLYPH(glyphRange.location);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/* Main loop. Work through all line frag rects and build the array of
|
|
|
|
rects. */
|
2003-01-26 19:21:40 +00:00
|
|
|
while (1)
|
|
|
|
{
|
2003-02-17 01:13:27 +00:00
|
|
|
/* Determine the starting x-coordinate for this line frag rect. */
|
2004-11-11 14:10:08 +00:00
|
|
|
if (lf->pos <= glyphRange.location)
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
2003-02-17 01:13:27 +00:00
|
|
|
/*
|
|
|
|
The start index is inside the line frag rect, so we need to
|
|
|
|
search through it to find the exact starting location.
|
|
|
|
*/
|
2003-06-21 23:25:27 +00:00
|
|
|
unsigned int i;
|
|
|
|
int j;
|
2003-01-26 19:21:40 +00:00
|
|
|
linefrag_point_t *lp;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-02-09 20:13:16 +00:00
|
|
|
for (j = 0, lp = lf->points; j < lf->num_points; j++, lp++)
|
2003-01-26 19:21:40 +00:00
|
|
|
if (lp->pos + lp->length > glyphRange.location)
|
|
|
|
break;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-09 20:13:16 +00:00
|
|
|
NSAssert(j < lf->num_points, @"can't find starting point of glyph");
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
x0 = lp->p.x + lf->rect.origin.x;
|
|
|
|
r = run_for_glyph_index(lp->pos, glyphs, &gpos, &cpos);
|
|
|
|
i = lp->pos - gpos;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
while (i + gpos < glyphRange.location)
|
|
|
|
{
|
|
|
|
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
|
|
|
|
r->glyphs[i].g != NSControlGlyph)
|
2003-02-03 01:52:27 +00:00
|
|
|
{
|
2016-10-23 20:13:37 +00:00
|
|
|
x0 += r->glyphs[i].advancement.width;
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
2003-02-03 01:52:27 +00:00
|
|
|
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
|
|
|
else
|
2003-02-17 01:13:27 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
The start index was before the this line frag, so the starting
|
|
|
|
x-coordinate is the left edge of the line frag.
|
|
|
|
*/
|
|
|
|
x0 = NSMinX(lf->rect);
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/* Determine the end x-coordinate for this line frag. */
|
2003-01-26 19:21:40 +00:00
|
|
|
if (lf->pos + lf->length > last)
|
|
|
|
{
|
2003-02-17 01:13:27 +00:00
|
|
|
/*
|
|
|
|
The end index is inside the line frag, so we need to find the
|
|
|
|
exact end location.
|
|
|
|
*/
|
2010-11-03 20:26:30 +00:00
|
|
|
NSUInteger i;
|
2003-06-21 23:25:27 +00:00
|
|
|
int j;
|
2003-01-26 19:21:40 +00:00
|
|
|
linefrag_point_t *lp;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/*
|
|
|
|
At this point there is a glyph in our range that is in this
|
|
|
|
line frag rect. If we're on the first line frag rect, it's
|
|
|
|
trivially true. If not, the check before the lf++; ensures it.
|
|
|
|
*/
|
2003-02-09 20:13:16 +00:00
|
|
|
for (j = 0, lp = lf->points; j < lf->num_points; j++, lp++)
|
|
|
|
if (lp->pos + lp->length > last)
|
2003-01-26 19:21:40 +00:00
|
|
|
break;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-02-09 20:13:16 +00:00
|
|
|
NSAssert(j < lf->num_points, @"can't find starting point of glyph");
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
x1 = lp->p.x + lf->rect.origin.x;
|
|
|
|
r = run_for_glyph_index(lp->pos, glyphs, &gpos, &cpos);
|
|
|
|
i = lp->pos - gpos;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
while (i + gpos < last)
|
|
|
|
{
|
|
|
|
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
|
|
|
|
r->glyphs[i].g != NSControlGlyph)
|
2003-02-03 01:52:27 +00:00
|
|
|
{
|
2016-10-23 20:13:37 +00:00
|
|
|
x1 += r->glyphs[i].advancement.width;
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
2003-02-03 01:52:27 +00:00
|
|
|
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
2010-12-09 18:04:25 +00:00
|
|
|
else if (lf->pos + lf->length == last)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The range ends in the last glyph of the line frag, so the end
|
|
|
|
x-coordinate is the right edge of this glyph. This egde is
|
|
|
|
equal to the right edge of the line fragment's rectangle if
|
|
|
|
the glyph is invisible or a control glyph, e.g., a newline,
|
|
|
|
and to the right edge of the line fragment's used rectangle
|
|
|
|
otherwise.
|
|
|
|
*/
|
|
|
|
NSUInteger i;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
|
|
|
|
|
|
|
r = run_for_glyph_index(last - 1, glyphs, &gpos, &cpos);
|
|
|
|
i = (last - 1) - gpos;
|
|
|
|
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
|
|
|
|
r->glyphs[i].g != NSControlGlyph)
|
|
|
|
x1 = NSMaxX(lf->used_rect);
|
|
|
|
else
|
|
|
|
x1 = NSMaxX(lf->rect);
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
else
|
2003-02-17 01:13:27 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
The range continues beyond the end of the line frag, so the end
|
|
|
|
x-coordinate is the right edge of the line frag.
|
|
|
|
*/
|
|
|
|
x1 = NSMaxX(lf->rect);
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/*
|
|
|
|
We have the start and end x-coordinates, and use the height of the
|
|
|
|
line frag for the y-coordinates.
|
|
|
|
*/
|
2003-01-26 19:21:40 +00:00
|
|
|
r = NSMakeRect(x0, lf->rect.origin.y, x1 - x0, lf->rect.size.height);
|
2003-02-17 01:13:27 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
As an optimization of the rectangle array, we check if the previous
|
|
|
|
rectangle had the same x-coordinates as the new rectangle and touches
|
|
|
|
it vertically. If so, we combine them.
|
|
|
|
*/
|
2003-01-26 19:21:40 +00:00
|
|
|
if (num_rects &&
|
|
|
|
r.origin.x == rect_array[num_rects - 1].origin.x &&
|
|
|
|
r.size.width == rect_array[num_rects - 1].size.width &&
|
|
|
|
r.origin.y == NSMaxY(rect_array[num_rects - 1]))
|
2001-01-22 18:13:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
rect_array[num_rects - 1].size.height += r.size.height;
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
else
|
2001-01-20 08:58:25 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
if (num_rects == rect_array_size)
|
2001-01-20 08:58:25 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
rect_array_size += 4;
|
2011-04-08 23:21:55 +00:00
|
|
|
rect_array = realloc(rect_array, sizeof(NSRect) * rect_array_size);
|
2001-01-20 08:58:25 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
rect_array[num_rects++] = r;
|
2001-01-20 08:58:25 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
if (lf->pos + lf->length >= last)
|
|
|
|
break;
|
|
|
|
lf++;
|
2001-01-17 22:11:52 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
*rectCount = num_rects;
|
|
|
|
return rect_array;
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(NSRect *) rectArrayForCharacterRange: (NSRange)charRange
|
|
|
|
withinSelectedCharacterRange: (NSRange)selCharRange
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
2010-11-03 20:26:30 +00:00
|
|
|
rectCount: (NSUInteger *)rectCount
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSRange r1, r2;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2011-05-25 21:47:30 +00:00
|
|
|
// FIXME: should accept {NSNotFound, 0} for selCharRange to ignore that parameter
|
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/* TODO: we can actually do better than this by using the insertion point
|
|
|
|
positioning behavior */
|
2003-01-26 19:21:40 +00:00
|
|
|
r1 = [self glyphRangeForCharacterRange: charRange
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
r2 = [self glyphRangeForCharacterRange: selCharRange
|
|
|
|
actualCharacterRange: NULL];
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
return [self rectArrayForGlyphRange: r1
|
|
|
|
withinSelectedGlyphRange: r2
|
|
|
|
inTextContainer: container
|
|
|
|
rectCount: rectCount];
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(NSRect) boundingRectForGlyphRange: (NSRange)glyphRange
|
|
|
|
inTextContainer: (NSTextContainer *)aTextContainer
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSRect *r;
|
|
|
|
NSRect result;
|
2010-11-03 20:26:30 +00:00
|
|
|
NSUInteger i, c;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO: This isn't correct. Need to handle glyphs that extend outside the
|
|
|
|
line frag rect. */
|
|
|
|
r = [self rectArrayForGlyphRange: glyphRange
|
|
|
|
withinSelectedGlyphRange: NSMakeRange(NSNotFound, 0)
|
|
|
|
inTextContainer: aTextContainer
|
|
|
|
rectCount: &c];
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!c)
|
|
|
|
return NSZeroRect;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
result = r[0];
|
|
|
|
for (r++, i = 1; i < c; i++, r++)
|
|
|
|
result = NSUnionRect(result, *r);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
return result;
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(NSRange) glyphRangeForBoundingRect: (NSRect)bounds
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-06-21 23:25:27 +00:00
|
|
|
int i;
|
|
|
|
unsigned int j;
|
2003-01-26 19:21:40 +00:00
|
|
|
int low, high, mid;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
NSRange range;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
|
|
|
return NSMakeRange(0, 0);
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[self _doLayoutToContainer: i
|
2003-02-16 21:40:47 +00:00
|
|
|
point: NSMakePoint(NSMaxX(bounds), NSMaxY(bounds))];
|
2001-01-22 18:57:06 +00:00
|
|
|
|
2003-02-10 14:14:34 +00:00
|
|
|
tc = textcontainers + i;
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!tc->num_linefrags)
|
|
|
|
return NSMakeRange(0, 0);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Find first glyph in bounds. */
|
|
|
|
|
|
|
|
/* Find right "line", ie. the first "line" not above bounds. */
|
2005-11-16 11:34:25 +00:00
|
|
|
for (low = 0, high = tc->num_linefrags - 1; low < high;)
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
mid = (low + high) / 2;
|
|
|
|
lf = &tc->linefrags[mid];
|
|
|
|
if (NSMaxY(lf->rect) > NSMinY(bounds))
|
2002-02-20 08:52:39 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
high = mid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
low = mid + 1;
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
|
|
|
}
|
2000-10-12 23:01:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
i = low;
|
|
|
|
lf = &tc->linefrags[i];
|
2000-10-12 23:01:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (NSMaxY(lf->rect) < NSMinY(bounds))
|
2000-10-12 23:01:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
return NSMakeRange(0, 0);
|
2000-10-12 23:01:43 +00:00
|
|
|
}
|
2002-02-11 02:42:12 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Scan to first line frag intersecting bounds horizontally. */
|
|
|
|
while (i < tc->num_linefrags - 1 &&
|
|
|
|
NSMinY(lf[0].rect) == NSMinY(lf[1].rect) &&
|
2003-02-26 16:11:55 +00:00
|
|
|
NSMaxX(lf[0].rect) < NSMinX(bounds))
|
2003-01-26 19:21:40 +00:00
|
|
|
i++, lf++;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO: find proper position in line frag rect */
|
|
|
|
range.location = lf->pos;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Find last glyph in bounds. */
|
2002-02-11 02:42:12 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Find right "line", ie. last "line" not below bounds. */
|
2005-11-16 11:34:25 +00:00
|
|
|
for (low = 0, high = tc->num_linefrags - 1; low < high;)
|
2001-01-22 18:13:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
mid = (low + high) / 2;
|
|
|
|
lf = &tc->linefrags[mid];
|
|
|
|
if (NSMinY(lf->rect) > NSMaxY(bounds))
|
2002-02-11 02:42:12 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
high = mid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
low = mid + 1;
|
2002-02-11 02:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
i = low;
|
|
|
|
lf = &tc->linefrags[i];
|
2000-10-12 23:01:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (i && NSMinY(lf->rect) > NSMaxY(bounds))
|
|
|
|
i--, lf--;
|
2000-08-27 22:32:29 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (NSMinY(lf->rect) > NSMaxY(bounds))
|
|
|
|
{
|
|
|
|
return NSMakeRange(0, 0);
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Scan to last line frag intersecting bounds horizontally. */
|
|
|
|
while (i > 0 &&
|
|
|
|
NSMinY(lf[0].rect) == NSMinY(lf[-1].rect) &&
|
2003-02-26 16:11:55 +00:00
|
|
|
NSMinX(lf[-1].rect) > NSMaxX(bounds))
|
2003-01-26 19:21:40 +00:00
|
|
|
i--, lf--;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO: find proper position in line frag rect */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
j = lf->pos + lf->length;
|
|
|
|
if (j <= range.location)
|
2001-01-12 12:32:32 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
return NSMakeRange(0, 0);
|
2001-01-12 12:32:32 +00:00
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
range.length = j - range.location;
|
|
|
|
return range;
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(NSRange) glyphRangeForBoundingRectWithoutAdditionalLayout: (NSRect)bounds
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-02-17 01:13:27 +00:00
|
|
|
/* TODO: this should be the same as
|
|
|
|
-glyphRangeForBoundingRect:inTextContainer: but without the _doLayout...
|
|
|
|
call.
|
|
|
|
|
|
|
|
In other words, it returns the range of glyphs in the rect that have
|
|
|
|
already been laid out.
|
|
|
|
*/
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
return [self glyphRangeForBoundingRect: bounds
|
|
|
|
inTextContainer: container];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2000-12-21 17:29:51 +00:00
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(unsigned int) glyphIndexForPoint: (NSPoint)aPoint
|
|
|
|
inTextContainer: (NSTextContainer *)aTextContainer
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
|
|
|
return [self glyphIndexForPoint: aPoint
|
|
|
|
inTextContainer: aTextContainer
|
|
|
|
fractionOfDistanceThroughGlyph: NULL];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 20:10:21 +00:00
|
|
|
/*
|
|
|
|
TODO: decide on behavior wrt. invisible glyphs and pointer far away from
|
|
|
|
anything visible
|
|
|
|
*/
|
2013-02-16 21:37:29 +00:00
|
|
|
-(NSUInteger) glyphIndexForPoint: (NSPoint)point
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
|
|
|
fractionOfDistanceThroughGlyph: (CGFloat *)partialFraction
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2000-12-21 17:29:51 +00:00
|
|
|
int i;
|
2003-01-26 19:21:40 +00:00
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
|
|
|
linefrag_point_t *lp;
|
2013-02-16 21:37:29 +00:00
|
|
|
CGFloat dummy;
|
2000-12-21 17:29:51 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!partialFraction)
|
|
|
|
partialFraction = &dummy;
|
2002-02-11 02:01:29 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
2000-12-21 17:29:51 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
2013-02-16 21:37:29 +00:00
|
|
|
return NSNotFound;
|
2000-12-21 17:29:51 +00:00
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[self _doLayoutToContainer: i point: point];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-02-10 14:14:34 +00:00
|
|
|
tc = textcontainers + i;
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
/* Find the line frag rect that contains the point, and handle the case
|
|
|
|
where the point isn't inside a line frag rect. */
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
|
2003-01-26 20:10:21 +00:00
|
|
|
{
|
2003-02-16 21:40:47 +00:00
|
|
|
/* The point is inside a rect; we're done. */
|
2003-01-26 20:10:21 +00:00
|
|
|
if (NSPointInRect(point, lf->rect))
|
2007-09-04 23:29:05 +00:00
|
|
|
break;
|
2003-01-26 20:10:21 +00:00
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
/* If the current line frag rect is below the point, the point must
|
|
|
|
be between the line with the current line frag rect and the line
|
|
|
|
with the previous line frag rect. */
|
2003-01-26 20:10:21 +00:00
|
|
|
if (NSMinY(lf->rect) > point.y)
|
2007-09-04 23:29:05 +00:00
|
|
|
{
|
|
|
|
/* If this is not the first line frag rect in the text container,
|
|
|
|
we consider the point to be after the last glyph on the previous
|
|
|
|
line. Otherwise, we consider it to be before the first glyph on
|
|
|
|
the current line. */
|
|
|
|
if (i > 0)
|
|
|
|
{
|
|
|
|
*partialFraction = 1.0;
|
2013-02-16 21:37:29 +00:00
|
|
|
if (lf->pos == 0)
|
|
|
|
{
|
|
|
|
return NSNotFound;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return lf->pos - 1;
|
|
|
|
}
|
2007-09-04 23:29:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*partialFraction = 0.0;
|
|
|
|
return lf->pos;
|
|
|
|
}
|
|
|
|
}
|
2003-02-16 21:40:47 +00:00
|
|
|
/* We know that NSMinY(lf->rect) <= point.y. If the point is on the
|
|
|
|
current line and to the left of the current line frag rect, we
|
|
|
|
consider the point to be before the first glyph in the current line
|
|
|
|
frag rect.
|
|
|
|
|
|
|
|
(This will happen if the point is between two line frag rects, or
|
|
|
|
before the first line frag rect. If the point is to the right of the
|
|
|
|
current line frag rect, it will be inside a subsequent line frag rect
|
|
|
|
on this line, or to the left of one, which will be handled by the here
|
|
|
|
or by the first check in the loop, or it will be after all line frag
|
|
|
|
rects on the line, which will be detected and handled as a 'between
|
|
|
|
two lines' case, or by the 'after all line frags' code below.)
|
|
|
|
*/
|
|
|
|
if (NSMaxY(lf->rect) >= point.y && NSMinX(lf->rect) > point.x)
|
2007-09-04 23:29:05 +00:00
|
|
|
{
|
|
|
|
*partialFraction = 0.0;
|
|
|
|
return lf->pos;
|
|
|
|
}
|
2003-01-26 20:10:21 +00:00
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
/* Point is after all line frags. */
|
2003-01-26 19:21:40 +00:00
|
|
|
if (i == tc->num_linefrags)
|
2003-01-26 20:10:21 +00:00
|
|
|
{
|
|
|
|
*partialFraction = 1.0;
|
2013-02-16 21:37:29 +00:00
|
|
|
/* TODO: this should return the correct thing even if the container is empty */
|
|
|
|
if (tc->pos + tc->length == 0)
|
|
|
|
{
|
|
|
|
return NSNotFound;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return tc->pos + tc->length - 1;
|
|
|
|
}
|
2003-01-26 20:10:21 +00:00
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* only interested in x from here on */
|
|
|
|
point.x -= lf->rect.origin.x;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* scan to the first point beyond the target */
|
|
|
|
for (i = 0, lp = lf->points; i < lf->num_points; i++, lp++)
|
|
|
|
if (lp->p.x > point.x)
|
|
|
|
break;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!i)
|
2002-02-24 07:39:18 +00:00
|
|
|
{
|
2003-02-16 21:40:47 +00:00
|
|
|
/* Before the first glyph on the line. */
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO: what if it isn't shown? */
|
|
|
|
*partialFraction = 0;
|
|
|
|
return lp->pos;
|
2002-02-24 07:39:18 +00:00
|
|
|
}
|
|
|
|
else
|
2001-01-22 18:13:43 +00:00
|
|
|
{
|
2003-02-16 21:40:47 +00:00
|
|
|
/* There are points in this line frag before the point we're looking
|
|
|
|
for. */
|
2003-01-26 19:21:40 +00:00
|
|
|
float cur, prev, next;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int glyph_pos, char_pos, last_visible;
|
2003-06-21 23:25:27 +00:00
|
|
|
unsigned j;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (i < lf->num_points)
|
2007-09-04 23:29:05 +00:00
|
|
|
next = lp->p.x;
|
2003-01-26 19:21:40 +00:00
|
|
|
else
|
2007-09-04 23:29:05 +00:00
|
|
|
next = NSMinX(lf->rect);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
lp--; /* Valid since we checked for !i above. */
|
2003-01-26 19:21:40 +00:00
|
|
|
r = run_for_glyph_index(lp->pos, glyphs, &glyph_pos, &char_pos);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
prev = lp->p.x;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
2003-01-26 20:10:21 +00:00
|
|
|
last_visible = lf->pos;
|
2005-11-16 11:34:25 +00:00
|
|
|
for (j = lp->pos - glyph_pos; j + glyph_pos < lp->pos + lp->length;)
|
2007-09-04 23:29:05 +00:00
|
|
|
{
|
|
|
|
// Don't ignore invisble glyphs.
|
|
|
|
// if (r->glyphs[j].isNotShown || r->glyphs[j].g == NSControlGlyph ||
|
|
|
|
if (!r->glyphs[j].g)
|
|
|
|
{
|
|
|
|
GLYPH_STEP_FORWARD(r, j, glyph_pos, char_pos)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
last_visible = j + glyph_pos;
|
|
|
|
|
2016-10-23 20:13:37 +00:00
|
|
|
cur = prev + r->glyphs[j].advancement.width;
|
2007-09-04 23:29:05 +00:00
|
|
|
if (j + glyph_pos + 1 == lp->pos + lp->length && next > cur)
|
|
|
|
cur = next;
|
|
|
|
|
|
|
|
if (cur >= point.x)
|
|
|
|
{
|
|
|
|
*partialFraction = (point.x - prev) / (cur - prev);
|
|
|
|
if (*partialFraction < 0)
|
|
|
|
*partialFraction = 0;
|
|
|
|
return j + glyph_pos;
|
|
|
|
}
|
|
|
|
prev = cur;
|
|
|
|
GLYPH_STEP_FORWARD(r, j, glyph_pos, char_pos)
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
*partialFraction = 1;
|
|
|
|
return last_visible;
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-02-03 01:52:27 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/*** Insertion point positioning and movement. ***/
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
/*
|
2003-02-17 01:13:27 +00:00
|
|
|
Determines at which glyph, and how far through it, the insertion point
|
|
|
|
should be placed for a certain character index.
|
2003-02-08 16:55:51 +00:00
|
|
|
*/
|
|
|
|
-(unsigned int) _glyphIndexForCharacterIndex: (unsigned int)cindex
|
|
|
|
fractionThrough: (float *)fraction
|
|
|
|
{
|
2003-02-03 01:52:27 +00:00
|
|
|
if (cindex == [[_textStorage string] length])
|
2003-02-08 16:55:51 +00:00
|
|
|
{
|
2003-02-16 03:18:35 +00:00
|
|
|
*fraction = 0.0;
|
|
|
|
return (unsigned int)-1;
|
2003-02-03 01:52:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSRange glyphRange, charRange;
|
2003-02-08 16:55:51 +00:00
|
|
|
unsigned int glyph_index;
|
|
|
|
float fraction_through;
|
2003-02-03 01:52:27 +00:00
|
|
|
|
|
|
|
glyphRange = [self glyphRangeForCharacterRange: NSMakeRange(cindex, 1)
|
2003-02-08 16:55:51 +00:00
|
|
|
actualCharacterRange: &charRange];
|
2003-02-03 01:52:27 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
/*
|
|
|
|
Deal with composite characters and ligatures.
|
|
|
|
|
|
|
|
We determine how far through the character range this character is a
|
|
|
|
part of the character is, and then determine the glyph index and
|
|
|
|
fraction that is the same distance through the glyph range it is
|
|
|
|
mapped to.
|
|
|
|
|
|
|
|
(This gives good behavior when dealing with ligatures, at least.)
|
|
|
|
|
|
|
|
Eg. if the character index is at character 3 in a 5 character range,
|
|
|
|
we are 3/5=0.6 through the entire character range. If this range was
|
|
|
|
mapped to 4 glyphs, we get 0.6*4=2.4, so the glyph index is 2 and
|
|
|
|
the fraction is 0.4.
|
|
|
|
*/
|
2003-02-03 01:52:27 +00:00
|
|
|
fraction_through = (cindex - charRange.location) / (float)charRange.length;
|
|
|
|
fraction_through *= glyphRange.length;
|
|
|
|
|
|
|
|
glyph_index = glyphRange.location + floor(fraction_through);
|
|
|
|
fraction_through -= floor(fraction_through);
|
2003-02-08 16:55:51 +00:00
|
|
|
|
|
|
|
*fraction = fraction_through;
|
|
|
|
return glyph_index;
|
2003-02-03 01:52:27 +00:00
|
|
|
}
|
2003-02-08 16:55:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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;
|
|
|
|
linefrag_t *lf;
|
|
|
|
float x0, x1;
|
|
|
|
NSRect r;
|
|
|
|
|
|
|
|
unsigned int glyph_index;
|
|
|
|
float fraction_through;
|
2003-02-03 01:52:27 +00:00
|
|
|
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
glyph_index = [self _glyphIndexForCharacterIndex: cindex
|
|
|
|
fractionThrough: &fraction_through];
|
|
|
|
if (glyph_index == (unsigned int)-1)
|
|
|
|
{
|
2003-02-16 03:18:35 +00:00
|
|
|
/* Need complete layout information. */
|
|
|
|
[self _doLayout];
|
|
|
|
if (extra_textcontainer)
|
|
|
|
{
|
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
2010-02-12 15:27:02 +00:00
|
|
|
if (tc->textContainer == extra_textcontainer)
|
2003-02-16 03:18:35 +00:00
|
|
|
break;
|
|
|
|
NSAssert(i < num_textcontainers, @"invalid extraTextContainer");
|
|
|
|
*textContainer = i;
|
|
|
|
r = extra_rect;
|
|
|
|
r.size.width = 1;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
glyph_index = [self numberOfGlyphs] - 1;
|
|
|
|
if (glyph_index == (unsigned int)-1)
|
2007-02-14 17:42:07 +00:00
|
|
|
{ /* No information is available. Get default font height. */
|
2014-12-08 10:09:42 +00:00
|
|
|
NSFont *f = [_typingAttributes objectForKey: NSFontAttributeName];
|
2003-02-16 03:18:35 +00:00
|
|
|
|
|
|
|
/* will be -1 if there are no text containers */
|
|
|
|
*textContainer = num_textcontainers - 1;
|
2011-12-29 18:34:27 +00:00
|
|
|
r = NSMakeRect(0, 0, 1, [f boundingRectForFont].size.height);
|
|
|
|
if (num_textcontainers > 0)
|
|
|
|
{
|
2014-12-08 10:09:42 +00:00
|
|
|
NSParagraphStyle *paragraph = [_typingAttributes objectForKey: NSParagraphStyleAttributeName];
|
|
|
|
NSTextAlignment alignment = [paragraph alignment];
|
|
|
|
|
2011-12-29 18:34:27 +00:00
|
|
|
tc = textcontainers + num_textcontainers - 1;
|
|
|
|
r.origin.x += [tc->textContainer lineFragmentPadding];
|
2014-12-08 10:09:42 +00:00
|
|
|
|
|
|
|
// Apply left/right/center justification...
|
|
|
|
if (alignment == NSRightTextAlignment)
|
|
|
|
{
|
|
|
|
r.origin.x += [tc->textContainer containerSize].width;
|
|
|
|
}
|
|
|
|
else if (alignment == NSCenterTextAlignment)
|
|
|
|
{
|
|
|
|
r.origin.x += [tc->textContainer containerSize].width / 2;
|
|
|
|
}
|
2011-12-29 18:34:27 +00:00
|
|
|
}
|
|
|
|
return r;
|
2003-02-16 03:18:35 +00:00
|
|
|
}
|
|
|
|
fraction_through = 1.0;
|
2003-02-08 16:55:51 +00:00
|
|
|
}
|
2010-02-12 15:27:02 +00:00
|
|
|
else
|
|
|
|
[self _doLayoutToGlyph: glyph_index];
|
2003-02-08 16:55:51 +00:00
|
|
|
|
2003-02-03 01:52:27 +00:00
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
2003-02-08 16:55:51 +00:00
|
|
|
if (tc->pos + tc->length > glyph_index)
|
2003-02-03 01:52:27 +00:00
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
|
|
|
{
|
2003-02-08 16:55:51 +00:00
|
|
|
*textContainer = -1;
|
2003-02-03 01:52:27 +00:00
|
|
|
return NSZeroRect;
|
|
|
|
}
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
*textContainer = i;
|
2003-02-03 01:52:27 +00:00
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
LINEFRAG_FOR_GLYPH(glyph_index);
|
2003-02-03 01:52:27 +00:00
|
|
|
|
2010-02-12 15:27:02 +00:00
|
|
|
/* Special case if we are asked for the insertion point rectangle at the
|
|
|
|
end of text, since the standard code yields an incorrect result if the
|
|
|
|
last line fragment ends with an invisible character (e.g., a tab).
|
|
|
|
Note that fraction_through is always less than 1 except when
|
|
|
|
-_glyphIndexForCharacterIndex:fractionThrough: is called for
|
|
|
|
cindex == [_textStorage length], in which case we set it to 1. */
|
|
|
|
if (fraction_through == 1.0)
|
|
|
|
{
|
2010-02-26 05:20:59 +00:00
|
|
|
r = (lf == 0) ? NSZeroRect : lf->used_rect;
|
2010-02-12 15:27:02 +00:00
|
|
|
r.origin.x += r.size.width;
|
|
|
|
r.size.width = 1;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2003-02-03 01:52:27 +00:00
|
|
|
{
|
2003-06-21 23:25:27 +00:00
|
|
|
unsigned int i;
|
|
|
|
int j;
|
2003-02-03 01:52:27 +00:00
|
|
|
linefrag_point_t *lp;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
|
|
|
|
2003-02-09 20:13:16 +00:00
|
|
|
for (j = 0, lp = lf->points; j < lf->num_points; j++, lp++)
|
2003-02-03 01:52:27 +00:00
|
|
|
if (lp->pos + lp->length > glyph_index)
|
|
|
|
break;
|
|
|
|
|
|
|
|
x0 = lp->p.x + lf->rect.origin.x;
|
|
|
|
r = run_for_glyph_index(lp->pos, glyphs, &gpos, &cpos);
|
|
|
|
i = lp->pos - gpos;
|
|
|
|
|
|
|
|
while (i + gpos < glyph_index)
|
|
|
|
{
|
|
|
|
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
|
|
|
|
r->glyphs[i].g != NSControlGlyph)
|
|
|
|
{
|
2016-10-23 20:13:37 +00:00
|
|
|
x0 += r->glyphs[i].advancement.width;
|
2003-02-03 01:52:27 +00:00
|
|
|
}
|
|
|
|
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
|
|
|
|
}
|
|
|
|
x1 = x0;
|
|
|
|
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
|
|
|
|
r->glyphs[i].g != NSControlGlyph)
|
|
|
|
{
|
2016-10-23 20:13:37 +00:00
|
|
|
x1 += r->glyphs[i].advancement.width;
|
2003-02-03 01:52:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = lf->rect;
|
|
|
|
r.origin.x = x0 + (x1 - x0) * fraction_through;
|
|
|
|
r.size.width = 1;
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
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;
|
|
|
|
|
2003-02-03 01:52:27 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
-(unsigned int) characterIndexMoving: (GSInsertionPointMovementDirection)direction
|
|
|
|
fromCharacterIndex: (unsigned int)from
|
|
|
|
originalCharacterIndex: (unsigned int)original
|
2003-07-22 16:13:46 +00:00
|
|
|
distance: (float)distance
|
2003-02-08 16:55:51 +00:00
|
|
|
{
|
|
|
|
NSRect from_rect, new_rect;
|
|
|
|
int from_tc, new_tc;
|
|
|
|
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)
|
|
|
|
{
|
2011-05-01 22:06:45 +00:00
|
|
|
/* The from character index is not in a text container, so move the
|
|
|
|
cursor to the start of the text. */
|
|
|
|
return 0;
|
2003-02-08 16:55:51 +00:00
|
|
|
}
|
|
|
|
|
2011-05-01 22:06:45 +00:00
|
|
|
/* Simple case which moves one character left/right or one line up/down,
|
|
|
|
but supports moving between text containers. */
|
|
|
|
if (distance == 0.0)
|
2003-02-08 16:55:51 +00:00
|
|
|
{
|
2011-05-01 22:06:45 +00:00
|
|
|
if (direction == GSInsertionPointMoveLeft ||
|
|
|
|
direction == GSInsertionPointMoveRight)
|
2003-02-08 16:55:51 +00:00
|
|
|
{
|
|
|
|
new = from;
|
|
|
|
if (direction == GSInsertionPointMoveLeft && new > 0)
|
|
|
|
new--;
|
|
|
|
if (direction == GSInsertionPointMoveRight && new < length)
|
|
|
|
new++;
|
2011-05-01 22:06:45 +00:00
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
else if (direction == GSInsertionPointMoveUp ||
|
|
|
|
direction == GSInsertionPointMoveDown)
|
|
|
|
{
|
|
|
|
int orig_tc;
|
|
|
|
const float target = [self _insertionPointRectForCharacterIndex: original
|
|
|
|
textContainer: &orig_tc].origin.x;
|
2003-02-08 16:55:51 +00:00
|
|
|
|
2011-05-01 22:06:45 +00:00
|
|
|
const int delta = (direction == GSInsertionPointMoveUp) ? -1 : 1;
|
2003-02-08 16:55:51 +00:00
|
|
|
|
2011-05-01 22:06:45 +00:00
|
|
|
/* First scan forward or backwards until we end up on a new line */
|
2012-01-01 11:29:20 +00:00
|
|
|
for (new = from; (direction == GSInsertionPointMoveUp) ? (new > 0) : (new < length); new += delta)
|
2011-05-01 22:06:45 +00:00
|
|
|
{
|
|
|
|
new_rect = [self _insertionPointRectForCharacterIndex: new
|
|
|
|
textContainer: &new_tc];
|
|
|
|
if (new_rect.origin.y != from_rect.origin.y || new_tc != from_tc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We found the start of the line, now find the target character on that line. */
|
|
|
|
new_rect = [self _insertionPointRectForCharacterIndex: new
|
|
|
|
textContainer: &new_tc];
|
|
|
|
while ((direction == GSInsertionPointMoveUp) ? (new > 0) : (new < length))
|
|
|
|
{
|
|
|
|
int prev_tc = new_tc;
|
|
|
|
NSRect prev_rect = new_rect;
|
|
|
|
new_rect = [self _insertionPointRectForCharacterIndex: new + delta
|
|
|
|
textContainer: &new_tc];
|
|
|
|
|
|
|
|
/* 'new+delta' is on a different line than the target line, so the
|
|
|
|
target character must be 'new'.*/
|
|
|
|
if (new_rect.origin.y != prev_rect.origin.y || new_tc != prev_tc)
|
|
|
|
return new;
|
|
|
|
|
|
|
|
/* If we pass the target point, the character we want is either
|
|
|
|
'new' or 'new+delta' */
|
|
|
|
if ((direction == GSInsertionPointMoveDown && NSMinX(new_rect) >= target)
|
|
|
|
|| (direction == GSInsertionPointMoveUp && NSMinX(new_rect) <= target))
|
|
|
|
{
|
|
|
|
if (fabs(NSMinX(new_rect) - target) > fabs(NSMinX(prev_rect) - target))
|
|
|
|
return new;
|
|
|
|
|
|
|
|
return new + delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
new += delta;
|
|
|
|
}
|
2003-02-08 16:55:51 +00:00
|
|
|
return new;
|
|
|
|
}
|
2011-05-01 22:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The following more complex cases are for when a minimum distance is specified.
|
|
|
|
However, they will not move out of from's text container.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (direction == GSInsertionPointMoveLeft ||
|
|
|
|
direction == GSInsertionPointMoveRight)
|
|
|
|
{
|
|
|
|
float target;
|
2003-02-08 16:55:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
This is probably very inefficient, but it shouldn't be a bottleneck,
|
2003-02-16 21:40:47 +00:00
|
|
|
and it guarantees that insertion point movement matches insertion point
|
2003-02-08 16:55:51 +00:00
|
|
|
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)
|
|
|
|
{
|
2003-02-14 17:00:04 +00:00
|
|
|
NSRect orig_rect, prev_rect;
|
2003-02-08 16:55:51 +00:00
|
|
|
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))];
|
2003-02-10 14:14:34 +00:00
|
|
|
tc = textcontainers + from_tc;
|
2003-02-08 16:55:51 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2010-02-12 15:27:02 +00:00
|
|
|
/* Special case for moving into the extra line at the end */
|
|
|
|
if (tc->textContainer == extra_textcontainer &&
|
|
|
|
NSMaxY(extra_rect) >= distance + NSMaxY(from_rect) &&
|
|
|
|
NSMinY(extra_rect) != NSMinY(from_rect))
|
|
|
|
return length;
|
|
|
|
|
2003-02-08 16:55:51 +00:00
|
|
|
/* 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
|
|
|
|
{
|
2003-02-16 03:18:35 +00:00
|
|
|
if (i == tc->num_linefrags)
|
|
|
|
i--, lf--;
|
2003-02-08 16:55:51 +00:00
|
|
|
/* 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. */
|
2003-02-14 17:00:04 +00:00
|
|
|
new_rect = [self _insertionPointRectForCharacterIndex: new
|
|
|
|
textContainer: &new_tc];
|
2003-02-08 16:55:51 +00:00
|
|
|
while (new < length)
|
|
|
|
{
|
2003-02-14 17:00:04 +00:00
|
|
|
prev_rect = new_rect;
|
2003-02-08 16:55:51 +00:00
|
|
|
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;
|
|
|
|
if (NSMinX(new_rect) >= target)
|
2003-02-14 17:00:04 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
'new+1' is beyond 'target', so either 'new' or 'new+1' is the
|
|
|
|
character we want. Pick the closest one. (Note that 'new' might
|
|
|
|
also be beyond 'target'.)
|
|
|
|
*/
|
|
|
|
if (fabs(NSMinX(new_rect) - target) < fabs(NSMinX(prev_rect) - target))
|
|
|
|
new++;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
new++;
|
2003-02-08 16:55:51 +00:00
|
|
|
}
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSLog(@"(%s): invalid direction %i (distance %g)",
|
|
|
|
__PRETTY_FUNCTION__, direction, distance);
|
|
|
|
return from;
|
|
|
|
}
|
|
|
|
|
2012-12-23 23:24:14 +00:00
|
|
|
- (void) ensureGlyphsForGlyphRange: (NSRange)glyphRange
|
|
|
|
{
|
|
|
|
[self _generateGlyphsUpToGlyph: NSMaxRange(glyphRange) - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ensureGlyphsForCharacterRange: (NSRange)charRange
|
|
|
|
{
|
|
|
|
[self _generateGlyphsUpToCharacter: NSMaxRange(charRange) - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ensureLayoutForGlyphRange: (NSRange)glyphRange
|
|
|
|
{
|
|
|
|
[self _doLayoutToGlyph: NSMaxRange(glyphRange) - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ensureLayoutForCharacterRange: (NSRange)charRange
|
|
|
|
{
|
|
|
|
NSRange glyphRange;
|
|
|
|
|
|
|
|
glyphRange = [self glyphRangeForCharacterRange: charRange
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
[self ensureLayoutForGlyphRange: glyphRange];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ensureLayoutForTextContainer: (NSTextContainer*)container
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
NSSize size;
|
|
|
|
|
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
|
|
|
{
|
|
|
|
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = [container containerSize];
|
|
|
|
[self _doLayoutToContainer: i
|
|
|
|
point: NSMakePoint(size.width, size.height)];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ensureLayoutForBoundingRect: (NSRect)bounds
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
|
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
|
|
|
{
|
|
|
|
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
2003-02-08 16:55:51 +00:00
|
|
|
|
2012-12-23 23:24:14 +00:00
|
|
|
[self _doLayoutToContainer: i
|
|
|
|
point: NSMakePoint(NSMaxX(bounds), NSMaxY(bounds))];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) invalidateLayoutForCharacterRange: (NSRange)charRange
|
|
|
|
actualCharacterRange: (NSRangePointer)actualCharRange
|
|
|
|
{
|
|
|
|
[self invalidateLayoutForCharacterRange: charRange
|
|
|
|
isSoft: NO
|
|
|
|
actualCharacterRange: actualCharRange];
|
|
|
|
}
|
|
|
|
|
2013-01-19 18:22:45 +00:00
|
|
|
- (void) invalidateGlyphsOnLayoutInvalidationForGlyphRange: (NSRange)glyphRange
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
}
|
|
|
|
|
2012-12-23 23:24:14 +00:00
|
|
|
- (BOOL) allowsNonContiguousLayout
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2023-11-20 21:43:49 +00:00
|
|
|
- (void) setAllowsNonContiguousLayout: (BOOL)flag
|
2012-12-23 23:24:14 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-11-20 21:43:49 +00:00
|
|
|
- (BOOL) hasNonContiguousLayout
|
2012-12-23 23:24:14 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2003-02-08 16:55:51 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@end
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@implementation NSLayoutManager (drawing)
|
|
|
|
|
2002-02-11 16:37:21 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/** Drawing **/
|
|
|
|
|
|
|
|
/*
|
|
|
|
If a range passed to a drawing function isn't contained in the text
|
2003-02-08 16:55:51 +00:00
|
|
|
container that contains its first glyph, the range is silently clamped.
|
2003-01-26 19:21:40 +00:00
|
|
|
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
|
|
|
|
will fit in the text container makes sense.
|
|
|
|
|
|
|
|
TODO: reconsider silently clamping ranges in these methods; might
|
|
|
|
want to make sure we don't do it if part of the range is in a second
|
2003-02-17 01:13:27 +00:00
|
|
|
container
|
|
|
|
*/
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
-(void) drawBackgroundForGlyphRange: (NSRange)range
|
|
|
|
atPoint: (NSPoint)containerOrigin
|
|
|
|
{
|
|
|
|
NSTextContainer *textContainer;
|
|
|
|
glyph_run_t *glyph_run;
|
2003-01-26 23:02:55 +00:00
|
|
|
unsigned int glyph_pos, char_pos, first_char_pos;
|
2003-01-26 19:21:40 +00:00
|
|
|
int i, j;
|
|
|
|
NSRect *rects;
|
2010-11-03 20:26:30 +00:00
|
|
|
NSUInteger count;
|
2014-12-08 10:09:42 +00:00
|
|
|
NSColor *color = nil;
|
|
|
|
NSColor *last_color = nil;
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
|
|
|
|
|
|
|
|
|
|
|
if (!range.length)
|
|
|
|
return;
|
|
|
|
[self _doLayoutToGlyph: range.location + range.length - 1];
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
|
|
|
|
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->pos + tc->length > range.location)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
|
|
|
{
|
|
|
|
NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (range.location + range.length > tc->pos + tc->length)
|
|
|
|
range.length = tc->pos + tc->length - range.location;
|
|
|
|
|
|
|
|
textContainer = tc->textContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
glyph_run = run_for_glyph_index(range.location, glyphs, &glyph_pos, &char_pos);
|
|
|
|
i = range.location - glyph_pos;
|
2003-01-26 23:02:55 +00:00
|
|
|
first_char_pos = char_pos;
|
2014-12-08 10:09:42 +00:00
|
|
|
while ((glyph_run != NULL) && (i + glyph_pos < range.location + range.length))
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
2003-02-10 14:14:34 +00:00
|
|
|
NSRange r = NSMakeRange(glyph_pos + i, glyph_run->head.glyph_length - i);
|
|
|
|
|
|
|
|
if (NSMaxRange(r) > NSMaxRange(range))
|
2014-10-31 14:12:44 +00:00
|
|
|
{
|
|
|
|
r.length = NSMaxRange(range) - r.location;
|
|
|
|
}
|
2003-02-10 14:14:34 +00:00
|
|
|
|
2003-02-24 02:54:32 +00:00
|
|
|
color = [_textStorage attribute: NSBackgroundColorAttributeName
|
|
|
|
atIndex: char_pos
|
|
|
|
effectiveRange: NULL];
|
|
|
|
if (color)
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
2003-02-24 02:54:32 +00:00
|
|
|
rects = [self rectArrayForGlyphRange: r
|
|
|
|
withinSelectedGlyphRange: NSMakeRange(NSNotFound, 0)
|
|
|
|
inTextContainer: textContainer
|
|
|
|
rectCount: &count];
|
|
|
|
|
|
|
|
if (count)
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
|
|
|
if (last_color != color)
|
2001-01-07 00:57:23 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
[color set];
|
|
|
|
last_color = color;
|
|
|
|
}
|
|
|
|
for (j = 0; j < count; j++, rects++)
|
|
|
|
{
|
|
|
|
DPSrectfill(ctxt,
|
|
|
|
rects->origin.x + containerOrigin.x,
|
|
|
|
rects->origin.y + containerOrigin.y,
|
|
|
|
rects->size.width, rects->size.height);
|
2001-01-07 00:57:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-01-26 23:02:55 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph_pos += glyph_run->head.glyph_length;
|
|
|
|
char_pos += glyph_run->head.char_length;
|
|
|
|
i = 0;
|
|
|
|
glyph_run = (glyph_run_t *)glyph_run->head.next;
|
2001-01-07 00:57:23 +00:00
|
|
|
}
|
2003-01-26 23:02:55 +00:00
|
|
|
|
|
|
|
if (!_selected_range.length || _selected_range.location == NSNotFound)
|
|
|
|
return;
|
|
|
|
|
2007-02-27 06:07:01 +00:00
|
|
|
if (_selected_range.location >= char_pos
|
|
|
|
|| _selected_range.location + _selected_range.length <= first_char_pos)
|
2003-01-26 23:02:55 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The selection (might) intersect our glyph range. */
|
|
|
|
{
|
|
|
|
NSRange r = [self glyphRangeForCharacterRange: _selected_range
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
NSRange sel = r;
|
2007-02-21 23:47:58 +00:00
|
|
|
NSTextView *ftv;
|
2003-01-26 23:02:55 +00:00
|
|
|
|
|
|
|
if (r.location < range.location)
|
|
|
|
{
|
2003-02-10 14:14:34 +00:00
|
|
|
if (range.location - r.location > r.length)
|
|
|
|
return;
|
2003-01-26 23:02:55 +00:00
|
|
|
r.length -= range.location - r.location;
|
|
|
|
r.location = range.location;
|
|
|
|
}
|
|
|
|
if (r.location + r.length > range.location + range.length)
|
|
|
|
{
|
2003-02-10 14:14:34 +00:00
|
|
|
if (r.location > range.location + range.length)
|
|
|
|
return;
|
2003-01-26 23:02:55 +00:00
|
|
|
r.length = range.location + range.length - r.location;
|
|
|
|
}
|
|
|
|
|
2007-02-21 23:47:58 +00:00
|
|
|
/* Use the text view's selected text attributes */
|
2007-02-27 06:07:01 +00:00
|
|
|
if ((ftv = [self textViewForBeginningOfSelection]))
|
2007-02-21 23:47:58 +00:00
|
|
|
color = [[ftv selectedTextAttributes]
|
2007-02-27 06:07:01 +00:00
|
|
|
objectForKey: NSBackgroundColorAttributeName];
|
2007-02-21 23:47:58 +00:00
|
|
|
|
|
|
|
if (!color)
|
|
|
|
color = [NSColor selectedTextBackgroundColor];
|
|
|
|
|
2003-01-26 23:02:55 +00:00
|
|
|
if (!color)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rects = [self rectArrayForGlyphRange: r
|
|
|
|
withinSelectedGlyphRange: sel
|
|
|
|
inTextContainer: textContainer
|
|
|
|
rectCount: &count];
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
{
|
|
|
|
[color set];
|
|
|
|
for (j = 0; j < count; j++, rects++)
|
|
|
|
{
|
|
|
|
DPSrectfill(ctxt,
|
|
|
|
rects->origin.x + containerOrigin.x,
|
|
|
|
rects->origin.y + containerOrigin.y,
|
|
|
|
rects->size.width, rects->size.height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2009-01-27 19:16:04 +00:00
|
|
|
static inline NSSize
|
|
|
|
attachmentSize(linefrag_t *lf, NSUInteger glyphIndex)
|
|
|
|
{
|
|
|
|
linefrag_attachment_t *la;
|
|
|
|
int la_i;
|
|
|
|
|
|
|
|
la = lf->attachments;
|
|
|
|
la_i = 0;
|
|
|
|
|
2011-01-01 16:43:34 +00:00
|
|
|
if (la)
|
2009-01-27 19:16:04 +00:00
|
|
|
{
|
2011-01-01 16:43:34 +00:00
|
|
|
while (la->pos != glyphIndex && la_i < lf->num_attachments)
|
|
|
|
{
|
|
|
|
la++;
|
|
|
|
la_i++;
|
|
|
|
}
|
2009-01-27 19:16:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (la_i >= lf->num_attachments)
|
|
|
|
return NSMakeSize(-1.0, -1.0);
|
|
|
|
|
|
|
|
return la->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSSize) attachmentSizeForGlyphAtIndex: (NSUInteger)glyphIndex
|
|
|
|
{
|
|
|
|
textcontainer_t *tc;
|
|
|
|
int i;
|
|
|
|
linefrag_t *lf;
|
|
|
|
|
|
|
|
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->pos + tc->length > glyphIndex)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
|
|
|
{
|
|
|
|
NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
|
|
|
|
return NSMakeSize(-1.0, -1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LINEFRAG_FOR_GLYPH(glyphIndex);
|
|
|
|
|
|
|
|
return attachmentSize(lf, glyphIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) showAttachmentCell: (NSCell *)cell
|
|
|
|
inRect: (NSRect)rect
|
|
|
|
characterIndex: (NSUInteger)attachmentIndex
|
|
|
|
{
|
|
|
|
[(id <NSTextAttachmentCell>)cell drawWithFrame: rect
|
|
|
|
inView: [NSView focusView]
|
|
|
|
characterIndex: attachmentIndex
|
|
|
|
layoutManager: self];
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
-(void) drawGlyphsForGlyphRange: (NSRange)range
|
|
|
|
atPoint: (NSPoint)containerOrigin
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i, j;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
|
|
|
linefrag_point_t *lp;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
NSPoint p;
|
|
|
|
unsigned int g;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
NSDictionary *attributes;
|
|
|
|
NSFont *f;
|
2007-02-27 06:07:01 +00:00
|
|
|
NSColor *color, *run_color;
|
|
|
|
NSRange selectedGlyphRange;
|
|
|
|
BOOL currentGlyphIsSelected;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph_run_t *glyph_run;
|
|
|
|
unsigned int glyph_pos, char_pos;
|
|
|
|
glyph_t *glyph;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-11-01 17:26:12 +00:00
|
|
|
/*
|
|
|
|
For performance, it might (if benchmarks or profiling backs it up) be
|
|
|
|
worthwhile to cache this across calls to this method. However, this
|
|
|
|
color can change at runtime, so care would have to be taken to keep the
|
|
|
|
cache in sync with the actual color.
|
|
|
|
*/
|
|
|
|
NSColor *defaultTextColor = [NSColor textColor];
|
2007-02-27 06:07:01 +00:00
|
|
|
NSColor *selectedTextColor = defaultTextColor;
|
2011-07-02 22:15:17 +00:00
|
|
|
NSColor *link_color = nil;
|
|
|
|
id linkValue;
|
2003-11-01 17:26:12 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
#define GBUF_SIZE 16 /* TODO: tweak */
|
|
|
|
NSGlyph gbuf[GBUF_SIZE];
|
2011-05-26 03:55:55 +00:00
|
|
|
NSSize advancementbuf[GBUF_SIZE];
|
2004-07-11 12:21:41 +00:00
|
|
|
int gbuf_len, gbuf_size;
|
2005-01-21 21:43 Alexander Malmberg <alexander@malmberg.org>
Various whitespace cleanups, comment type fixes, and changes
to avoid warnings from recent versions of gcc.
* Headers/Additions/GNUstepGUI/GSToolbar.h (-_toolbars): Declare.
* Source/NSWindow+Toolbar.m: Remove conflicting declaration of
[NSToolbar -_toolbars].
* Headers/Additions/GNUstepGUI/GSServicesManager.h,
Source/GSServicesMananger.m (-item2title:, -validateMenuItem:):
Adjust argument types.
* Headers/AppKit/NSMenu.h (-validateMenuItem:): Adjust argument
type.
* Source/NSTextView.m (-sizeToFit): Don't use size uninitialized
if neither resizable flags is set.
(-insertText:): Adjust argument type.
* Headers/AppKit/NSResponder.h, Source/NSResponder.m (-insertText:):
Adjust argument type. Document.
* Headers/AppKit/NSView.h: Change type of ivar _window to NSWindow *.
* Source/GSTitleView.m (-mouseDown:): Always initialize
startWindowOrigin.
* Source/NSApplication.m (-setApplicationIconImage:): Add casts
to avoid warnings.
* Source/NSCell.m (-cellSize): Add default: case.
* Source/NSPasteboard.m
([GSFiltered -pasteboard:provideDataForType:]): Detect and warn if we
can't find a filter that will get us the desired type.
* Source/NSProgressIndicator.m: Comment out unused variable 'images'.
* Source/NSBezierPath.m: Declare GSBezierPath fully before using it.
(-bezierPathByFlatteningPath, -bezierPathByReversingPath): Make sure
variables are always initialized.
* Source/NSMenuView.m,
* Source/NSPrintOperation.m,
* Source/NSSplitView.m,
* Source/NSTableHeaderView.m: Make sure variables are always
initialized.
* Source/NSBox.m,
* Source/NSImageview.m,
* Source/NSText.m,
* Source/NSTextStorage.m: Add missing includes.
* Source/GSKeyBindingTable.m,
* Source/GSLayoutManager.m,
* Source/NSBitmapImageRep+PNM.m,
* Source/NSBundleAdditions.m,
* Source/NSLayoutManager.m,
* Source/nsimage-tiff.h,
* Source/tiff.m,
* Headers/Additions/GNUstepGUI/GSDisplayServer.h,
* Source/GSDisplayServer.m: Change signedness of various variables.
* Source/NSPanel.m (-sendEvent:): Remove.
* Source/NSWindow.m (-becomesKeyOnlyIfNeeded): New method.
(-_sendEvent:becomesKeyOnlyIfNeeded:): Remove. Move code ...
(-sendEvent:): ... here. Use -becomesKeyOnlyIfNeeded instead
of the argument.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@20590 72102866-910b-0410-8b05-ffd578937521
2005-01-21 20:39:18 +00:00
|
|
|
NSPoint gbuf_point = NSZeroPoint;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!range.length)
|
|
|
|
return;
|
|
|
|
[self _doLayoutToGlyph: range.location + range.length - 1];
|
|
|
|
|
2007-02-27 06:07:01 +00:00
|
|
|
/* Find the selected range of glyphs as it overlaps with the range we
|
|
|
|
* are about to display.
|
|
|
|
*/
|
|
|
|
if (_selected_range.length == 0)
|
|
|
|
{
|
|
|
|
selectedGlyphRange.location = 0;
|
|
|
|
selectedGlyphRange.length = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
selectedGlyphRange = [self glyphRangeForCharacterRange: _selected_range
|
|
|
|
actualCharacterRange: 0];
|
|
|
|
}
|
|
|
|
selectedGlyphRange = NSIntersectionRange(selectedGlyphRange, range);
|
|
|
|
|
2004-07-11 12:21:41 +00:00
|
|
|
if ([ctxt isDrawingToScreen])
|
|
|
|
gbuf_size = GBUF_SIZE;
|
|
|
|
else
|
2004-07-11 14:36:51 +00:00
|
|
|
gbuf_size = 1;
|
2004-07-11 12:21:41 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->pos + tc->length > range.location)
|
|
|
|
break;
|
|
|
|
if (i == num_textcontainers)
|
2001-01-19 23:22:16 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
2001-01-19 23:22:16 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
|
|
|
|
if (range.location + range.length > tc->pos + tc->length)
|
|
|
|
range.length = tc->pos + tc->length - range.location;
|
|
|
|
|
2003-02-17 01:13:27 +00:00
|
|
|
LINEFRAG_FOR_GLYPH(range.location);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
j = 0;
|
|
|
|
lp = lf->points;
|
|
|
|
while (lp->pos + lp->length < range.location)
|
|
|
|
lp++, j++;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph_run = run_for_glyph_index(lp->pos, glyphs, &glyph_pos, &char_pos);
|
2007-02-27 06:07:01 +00:00
|
|
|
currentGlyphIsSelected = NSLocationInRange(lp->pos, selectedGlyphRange);
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph = glyph_run->glyphs + lp->pos - glyph_pos;
|
|
|
|
attributes = [_textStorage attributesAtIndex: char_pos
|
2007-02-27 06:07:01 +00:00
|
|
|
effectiveRange: NULL];
|
|
|
|
run_color = [attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
if (run_color == nil)
|
|
|
|
run_color = defaultTextColor;
|
|
|
|
|
2011-07-02 22:15:17 +00:00
|
|
|
linkValue = [attributes objectForKey: NSLinkAttributeName];
|
|
|
|
if (linkValue != nil)
|
|
|
|
{
|
|
|
|
if (link_color == nil)
|
|
|
|
{
|
|
|
|
NSDictionary *link_attributes = [[self firstTextView] linkTextAttributes];
|
|
|
|
link_color = [link_attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
}
|
|
|
|
if (link_color != nil)
|
|
|
|
run_color = link_color;
|
|
|
|
}
|
|
|
|
|
2007-02-27 06:07:01 +00:00
|
|
|
if (selectedGlyphRange.length > 0)
|
|
|
|
{
|
|
|
|
/* Get the text view's color setting for selected text as we will
|
|
|
|
* be needing to draw some selected glyphs.
|
|
|
|
*/
|
|
|
|
selectedTextColor = [[[self textViewForBeginningOfSelection]
|
|
|
|
selectedTextAttributes] objectForKey: NSForegroundColorAttributeName];
|
|
|
|
|
|
|
|
/* FIXME ... should we fall back to using selectedTextColor or
|
|
|
|
* defaultTextColor?
|
|
|
|
*/
|
|
|
|
if (selectedTextColor == nil)
|
|
|
|
{
|
|
|
|
selectedTextColor = [NSColor selectedTextColor];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
color = (currentGlyphIsSelected ? selectedTextColor : run_color);
|
2003-11-01 17:26:12 +00:00
|
|
|
[color set];
|
2003-01-26 19:21:40 +00:00
|
|
|
f = glyph_run->font;
|
|
|
|
[f set];
|
2001-01-17 22:11:52 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
p = lp->p;
|
|
|
|
p.x += lf->rect.origin.x + containerOrigin.x;
|
|
|
|
p.y += lf->rect.origin.y + containerOrigin.y;
|
|
|
|
gbuf_len = 0;
|
|
|
|
for (g = lp->pos; g < range.location + range.length; g++, glyph++)
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2007-02-27 06:07:01 +00:00
|
|
|
if (currentGlyphIsSelected != NSLocationInRange(g, selectedGlyphRange))
|
|
|
|
{
|
|
|
|
/* When we change between drawing selected and unselected glyphs
|
|
|
|
* we must flush any glyphs from the buffer and change trhe color
|
|
|
|
* we use for the text.
|
|
|
|
*/
|
|
|
|
if (gbuf_len)
|
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
2011-05-26 03:55:55 +00:00
|
|
|
GSShowGlyphsWithAdvances(ctxt, gbuf, advancementbuf, gbuf_len);
|
2007-02-27 06:07:01 +00:00
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
}
|
|
|
|
if (currentGlyphIsSelected == YES)
|
|
|
|
{
|
|
|
|
currentGlyphIsSelected = NO;
|
|
|
|
if (color != run_color)
|
|
|
|
{
|
|
|
|
color = run_color;
|
|
|
|
[color set];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currentGlyphIsSelected = YES;
|
|
|
|
if (color != selectedTextColor)
|
|
|
|
{
|
|
|
|
color = selectedTextColor;
|
|
|
|
[color set];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (g == lp->pos + lp->length)
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
if (gbuf_len)
|
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
2011-05-26 03:55:55 +00:00
|
|
|
GSShowGlyphsWithAdvances(ctxt, gbuf, advancementbuf, gbuf_len);
|
2003-01-26 19:21:40 +00:00
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
lp++;
|
|
|
|
if (j == lf->num_points)
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
lf++;
|
|
|
|
j = 0;
|
|
|
|
lp = lf->points;
|
|
|
|
}
|
|
|
|
p = lp->p;
|
|
|
|
p.x += lf->rect.origin.x + containerOrigin.x;
|
|
|
|
p.y += lf->rect.origin.y + containerOrigin.y;
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
if (g == glyph_pos + glyph_run->head.glyph_length)
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph_pos += glyph_run->head.glyph_length;
|
|
|
|
char_pos += glyph_run->head.char_length;
|
|
|
|
glyph_run = (glyph_run_t *)glyph_run->head.next;
|
|
|
|
attributes = [_textStorage attributesAtIndex: char_pos
|
|
|
|
effectiveRange: NULL];
|
2007-02-27 06:07:01 +00:00
|
|
|
run_color = [attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
if (run_color == nil)
|
|
|
|
{
|
|
|
|
run_color = defaultTextColor;
|
|
|
|
}
|
2011-07-02 22:15:17 +00:00
|
|
|
|
|
|
|
linkValue = [attributes objectForKey: NSLinkAttributeName];
|
|
|
|
if (linkValue != nil)
|
|
|
|
{
|
|
|
|
if (link_color == nil)
|
|
|
|
{
|
|
|
|
NSDictionary *link_attributes = [[self firstTextView] linkTextAttributes];
|
|
|
|
link_color = [link_attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
}
|
|
|
|
if (link_color != nil)
|
|
|
|
run_color = link_color;
|
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
glyph = glyph_run->glyphs;
|
2007-02-27 06:07:01 +00:00
|
|
|
|
|
|
|
/* If the font has changed or the color has changed (and we are
|
|
|
|
* not drawing using the selected text color) then we must flush
|
|
|
|
* any buffered glyphs and set the new font and color.
|
|
|
|
*/
|
|
|
|
if (glyph_run->font != f
|
|
|
|
|| (currentGlyphIsSelected == NO && run_color != color))
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
|
|
|
if (gbuf_len)
|
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
2011-05-26 03:55:55 +00:00
|
|
|
GSShowGlyphsWithAdvances(ctxt, gbuf, advancementbuf, gbuf_len);
|
2003-01-26 19:21:40 +00:00
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
}
|
|
|
|
if (f != glyph_run->font)
|
|
|
|
{
|
|
|
|
f = glyph_run->font;
|
|
|
|
[f set];
|
|
|
|
}
|
2007-02-27 06:07:01 +00:00
|
|
|
if (currentGlyphIsSelected == NO && run_color != color)
|
|
|
|
{
|
|
|
|
color = run_color;
|
|
|
|
[color set];
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!glyph->isNotShown && glyph->g && glyph->g != NSControlGlyph)
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-01-31 21:08:24 +00:00
|
|
|
if (glyph->g == GSAttachmentGlyph)
|
|
|
|
{
|
2009-01-27 19:16:04 +00:00
|
|
|
if (g >= range.location)
|
2003-01-31 21:08:24 +00:00
|
|
|
{
|
|
|
|
unsigned int char_index =
|
2003-02-16 21:40:47 +00:00
|
|
|
[self characterRangeForGlyphRange: NSMakeRange(g, 1)
|
2003-01-31 21:08:24 +00:00
|
|
|
actualGlyphRange: NULL].location;
|
2017-04-16 11:54:21 +00:00
|
|
|
id<NSTextAttachmentCell> cell = [[_textStorage attribute: NSAttachmentAttributeName
|
2003-01-31 21:08:24 +00:00
|
|
|
atIndex: char_index
|
|
|
|
effectiveRange: NULL] attachmentCell];
|
|
|
|
NSRect cellFrame;
|
|
|
|
|
|
|
|
cellFrame.origin = p;
|
2009-01-27 19:16:04 +00:00
|
|
|
cellFrame.size = attachmentSize(lf, g);
|
2003-01-31 21:08:24 +00:00
|
|
|
cellFrame.origin.y -= cellFrame.size.height;
|
|
|
|
|
2009-01-27 19:16:04 +00:00
|
|
|
/* Silently ignore if we don't have any size information for
|
|
|
|
it. */
|
|
|
|
if (NSEqualSizes(cellFrame.size, NSMakeSize(-1.0, -1.0)))
|
|
|
|
continue;
|
|
|
|
|
2004-03-13 02:22:12 +00:00
|
|
|
/* Drawing the cell might mess up our state, so we reset
|
|
|
|
the font and color afterwards. */
|
2003-01-31 21:08:24 +00:00
|
|
|
/* TODO:
|
2004-03-13 02:22:12 +00:00
|
|
|
optimize this?
|
|
|
|
collect attachments and draw them in bunches of eg. 4?
|
|
|
|
|
|
|
|
probably not worth effort. better to optimize font and
|
|
|
|
color setting :)
|
2003-01-31 21:08:24 +00:00
|
|
|
|
|
|
|
should they really be drawn in our coordinate system?
|
|
|
|
*/
|
2009-01-27 19:16:04 +00:00
|
|
|
[self showAttachmentCell: (NSCell*)cell
|
|
|
|
inRect: cellFrame
|
2010-12-30 15:29:05 +00:00
|
|
|
characterIndex: char_index];
|
2003-01-31 21:08:24 +00:00
|
|
|
[f set];
|
2004-02-08 23:58:41 +00:00
|
|
|
[color set];
|
2003-01-31 21:08:24 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
if (g >= range.location)
|
|
|
|
{
|
|
|
|
if (!gbuf_len)
|
|
|
|
{
|
|
|
|
gbuf[0] = glyph->g;
|
2016-10-23 20:13:37 +00:00
|
|
|
advancementbuf[0] = glyph->advancement;
|
2003-01-26 19:21:40 +00:00
|
|
|
gbuf_point = p;
|
|
|
|
gbuf_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-07-11 12:21:41 +00:00
|
|
|
if (gbuf_len == gbuf_size)
|
2003-01-26 19:21:40 +00:00
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
2011-05-26 03:55:55 +00:00
|
|
|
GSShowGlyphsWithAdvances(ctxt, gbuf, advancementbuf, gbuf_size);
|
2003-01-26 19:21:40 +00:00
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
gbuf_point = p;
|
|
|
|
}
|
2011-05-26 03:55:55 +00:00
|
|
|
gbuf[gbuf_len] = glyph->g;
|
2016-10-23 20:13:37 +00:00
|
|
|
advancementbuf[gbuf_len] = glyph->advancement;
|
2011-05-26 03:55:55 +00:00
|
|
|
gbuf_len++;
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
|
|
|
}
|
2016-10-23 20:13:37 +00:00
|
|
|
p.x += glyph->advancement.width;
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
if (gbuf_len)
|
2001-01-11 19:09:02 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
/*int i;
|
|
|
|
printf("%i at (%g %g) 4\n", gbuf_len, gbuf_point.x, gbuf_point.y);
|
|
|
|
for (i = 0; i < gbuf_len; i++) printf(" %3i : %04x\n", i, gbuf[i]); */
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
2011-05-26 03:55:55 +00:00
|
|
|
GSShowGlyphsWithAdvances(ctxt, gbuf, advancementbuf, gbuf_len);
|
2003-01-26 19:21:40 +00:00
|
|
|
DPSnewpath(ctxt);
|
2001-01-11 19:09:02 +00:00
|
|
|
}
|
2003-01-31 21:08:24 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
#undef GBUF_SIZE
|
2011-02-22 20:53:13 +00:00
|
|
|
|
|
|
|
// Draw underline where necessary
|
|
|
|
// FIXME: Also draw strikeout
|
|
|
|
{
|
|
|
|
const NSRange characterRange = [self characterRangeForGlyphRange: range
|
|
|
|
actualGlyphRange: NULL];
|
2011-07-02 22:15:17 +00:00
|
|
|
id linkUnderlineValue = nil;
|
2011-02-22 20:53:13 +00:00
|
|
|
|
|
|
|
for (i=characterRange.location; i<NSMaxRange(characterRange); )
|
|
|
|
{
|
|
|
|
NSRange underlinedCharacterRange;
|
2011-07-02 22:15:17 +00:00
|
|
|
NSRange linkCharacterRange;
|
|
|
|
id underlineValue = nil;
|
|
|
|
|
|
|
|
linkValue = [_textStorage attribute: NSLinkAttributeName
|
|
|
|
atIndex: i
|
|
|
|
longestEffectiveRange: &linkCharacterRange
|
|
|
|
inRange: characterRange];
|
|
|
|
if (linkValue != nil)
|
|
|
|
{
|
|
|
|
if (linkUnderlineValue == nil)
|
|
|
|
{
|
|
|
|
NSDictionary *link_attributes = [[self firstTextView] linkTextAttributes];
|
|
|
|
linkUnderlineValue = [link_attributes valueForKey: NSUnderlineStyleAttributeName];
|
|
|
|
}
|
|
|
|
underlineValue = linkUnderlineValue;
|
|
|
|
underlinedCharacterRange = linkCharacterRange;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
underlineValue = [_textStorage attribute: NSUnderlineStyleAttributeName
|
|
|
|
atIndex: i
|
|
|
|
longestEffectiveRange: &underlinedCharacterRange
|
|
|
|
inRange: characterRange];
|
|
|
|
underlinedCharacterRange = NSIntersectionRange(underlinedCharacterRange,
|
|
|
|
linkCharacterRange);
|
|
|
|
}
|
2011-02-22 20:53:13 +00:00
|
|
|
if (underlineValue != nil && [underlineValue integerValue] != NSUnderlineStyleNone)
|
|
|
|
{
|
|
|
|
const NSRange underlinedGylphRange = [self glyphRangeForCharacterRange: underlinedCharacterRange
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
|
|
|
|
// we have a range of glpyhs that need underlining, which might span
|
|
|
|
// multiple line fragments, so we need to iterate though the line fragments
|
|
|
|
for (j=underlinedGylphRange.location; j<NSMaxRange(underlinedGylphRange); )
|
|
|
|
{
|
|
|
|
NSRange lineFragmentGlyphRange;
|
|
|
|
const NSRect lineFragmentRect = [self lineFragmentRectForGlyphAtIndex: j
|
|
|
|
effectiveRange: &lineFragmentGlyphRange];
|
|
|
|
const NSRange rangeToUnderline = NSIntersectionRange(underlinedGylphRange, lineFragmentGlyphRange);
|
|
|
|
|
|
|
|
[self underlineGylphRange: rangeToUnderline
|
|
|
|
underlineType: [underlineValue integerValue]
|
|
|
|
lineFragmentRect: lineFragmentRect
|
|
|
|
lineFragmentGlyphRange: lineFragmentGlyphRange
|
|
|
|
containerOrigin: containerOrigin];
|
|
|
|
|
|
|
|
j = NSMaxRange(rangeToUnderline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i += underlinedCharacterRange.length;
|
|
|
|
}
|
2011-03-12 07:45:14 +00:00
|
|
|
|
|
|
|
// Draw spelling state (i.e. red underline for misspelled words)
|
2011-03-14 09:05:29 +00:00
|
|
|
|
|
|
|
if ([NSGraphicsContext currentContextDrawingToScreen])
|
2011-03-12 07:45:14 +00:00
|
|
|
{
|
2011-03-14 09:05:29 +00:00
|
|
|
for (i=characterRange.location; i<NSMaxRange(characterRange); )
|
2011-03-12 07:45:14 +00:00
|
|
|
{
|
2011-03-14 09:05:29 +00:00
|
|
|
NSRange underlinedCharacterRange;
|
|
|
|
id underlineValue = [self temporaryAttribute: NSSpellingStateAttributeName
|
|
|
|
atCharacterIndex: i
|
|
|
|
longestEffectiveRange: &underlinedCharacterRange
|
|
|
|
inRange: characterRange];
|
|
|
|
if (underlineValue != nil && [underlineValue integerValue] != 0)
|
2011-03-12 07:45:14 +00:00
|
|
|
{
|
2011-03-14 09:05:29 +00:00
|
|
|
const NSRange underlinedGylphRange = [self glyphRangeForCharacterRange: underlinedCharacterRange
|
|
|
|
actualCharacterRange: NULL];
|
2011-03-12 07:45:14 +00:00
|
|
|
|
2011-03-14 09:05:29 +00:00
|
|
|
// we have a range of glpyhs that need underlining, which might span
|
|
|
|
// multiple line fragments, so we need to iterate though the line fragments
|
|
|
|
for (j=underlinedGylphRange.location; j<NSMaxRange(underlinedGylphRange); )
|
|
|
|
{
|
|
|
|
NSRange lineFragmentGlyphRange;
|
|
|
|
const NSRect lineFragmentRect = [self lineFragmentRectForGlyphAtIndex: j
|
|
|
|
effectiveRange: &lineFragmentGlyphRange];
|
|
|
|
const NSRange rangeToUnderline = NSIntersectionRange(underlinedGylphRange, lineFragmentGlyphRange);
|
|
|
|
|
|
|
|
[self _drawSpellingState: [underlineValue integerValue]
|
|
|
|
forGylphRange: rangeToUnderline
|
|
|
|
lineFragmentRect: lineFragmentRect
|
|
|
|
lineFragmentGlyphRange: lineFragmentGlyphRange
|
|
|
|
containerOrigin: containerOrigin];
|
|
|
|
|
|
|
|
j = NSMaxRange(rangeToUnderline);
|
|
|
|
}
|
2011-03-12 07:45:14 +00:00
|
|
|
}
|
2011-03-14 09:05:29 +00:00
|
|
|
i += underlinedCharacterRange.length;
|
2011-03-12 07:45:14 +00:00
|
|
|
}
|
|
|
|
}
|
2011-02-22 20:53:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void) underlineGylphRange: (NSRange)range
|
|
|
|
underlineType: (NSInteger)type
|
|
|
|
lineFragmentRect: (NSRect)fragmentRect
|
|
|
|
lineFragmentGlyphRange: (NSRange)fragmentGlyphRange
|
|
|
|
containerOrigin: (NSPoint)containerOrigin
|
|
|
|
{
|
|
|
|
// FIXME: Implement underlining by word
|
|
|
|
/*if ((type & NSUnderlineByWordMask) != 0)
|
|
|
|
{
|
|
|
|
NSCharacterSet *setToSkip = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
|
|
|
NSRange characterRange = [self characterRangeForGlyphRange: range
|
|
|
|
actualGlyphRange: NULL];
|
|
|
|
NSString *string = [[self textStorage] string];
|
|
|
|
NSRange examiningRange = range;
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
NSRange nextRangeToSkip = [string rangeOfCharacterInSet: setToSkip
|
|
|
|
option: 0
|
|
|
|
range: NSMakeRange(i, range.length - (i - range.location))];
|
|
|
|
if (nextRangeToSkip.location == NSNotFound)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else*/
|
|
|
|
{
|
|
|
|
[self drawUnderlineForGlyphRange: range
|
|
|
|
underlineType: type
|
|
|
|
baselineOffset: 0 // FIXME:
|
|
|
|
lineFragmentRect: fragmentRect
|
|
|
|
lineFragmentGlyphRange: fragmentGlyphRange
|
|
|
|
containerOrigin: containerOrigin];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GSDrawPatternLine(NSPoint start, NSPoint end, NSInteger pattern, CGFloat thickness, CGFloat phase)
|
|
|
|
{
|
|
|
|
NSBezierPath *path = [NSBezierPath bezierPath];
|
|
|
|
// FIXME: setLineDash should take CGFloat
|
|
|
|
if ((pattern & NSUnderlinePatternDot) == NSUnderlinePatternDot)
|
|
|
|
{
|
2013-02-01 14:03:08 +00:00
|
|
|
const CGFloat dot[2] = {2.5 * thickness, 2.5 * thickness};
|
2011-02-22 20:53:13 +00:00
|
|
|
[path setLineDash: dot count: 2 phase: phase];
|
|
|
|
}
|
|
|
|
else if ((pattern & NSUnderlinePatternDash) == NSUnderlinePatternDash)
|
|
|
|
{
|
2013-02-01 14:03:08 +00:00
|
|
|
const CGFloat dash[2] = {10 * thickness, 5 * thickness};
|
2011-02-22 20:53:13 +00:00
|
|
|
[path setLineDash: dash count: 2 phase: phase];
|
|
|
|
}
|
|
|
|
else if ((pattern & NSUnderlinePatternDashDot) == NSUnderlinePatternDashDot)
|
|
|
|
{
|
2013-02-01 14:03:08 +00:00
|
|
|
const CGFloat dashdot[4] = {10 * thickness, 3 * thickness, 3 * thickness, 3 * thickness};
|
2011-02-22 20:53:13 +00:00
|
|
|
[path setLineDash: dashdot count: 4 phase: phase];
|
|
|
|
}
|
|
|
|
else if ((pattern & NSUnderlinePatternDashDotDot) == NSUnderlinePatternDashDotDot)
|
|
|
|
{
|
2013-02-01 14:03:08 +00:00
|
|
|
const CGFloat dashdotdot[6] = {10 * thickness, 3 * thickness, 3 * thickness, 3 * thickness, 3 * thickness, 3 * thickness};
|
2011-02-22 20:53:13 +00:00
|
|
|
[path setLineDash: dashdotdot count: 6 phase: phase];
|
|
|
|
}
|
|
|
|
|
|
|
|
[path setLineWidth: thickness];
|
|
|
|
[path moveToPoint: start];
|
|
|
|
[path lineToPoint: end];
|
|
|
|
[path stroke];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void) drawUnderlineForGlyphRange: (NSRange)underlineRange
|
|
|
|
underlineType: (NSInteger)type
|
|
|
|
baselineOffset: (CGFloat)offset
|
|
|
|
lineFragmentRect: (NSRect)fragmentRect
|
|
|
|
lineFragmentGlyphRange: (NSRange)fragmentGlyphRange
|
|
|
|
containerOrigin: (NSPoint)containerOrigin
|
|
|
|
{
|
|
|
|
/*NSLog(@"drawUnderlineForGlyphRange:%@ underlineType:%d baselineOffset:%f lineFragmentRect:%@ lineFragmentGlyphRange:%@ containerOrigin:%@",
|
|
|
|
NSStringFromRange(underlineRange),
|
|
|
|
(int)type,
|
|
|
|
(float)offset,
|
|
|
|
NSStringFromRect(fragmentRect),
|
|
|
|
NSStringFromRange(fragmentGlyphRange),
|
|
|
|
NSStringFromPoint(containerOrigin));*/
|
|
|
|
|
|
|
|
// We have to iterate through the attributes (again..) to find
|
|
|
|
// contiguous regions with the same underline color.
|
|
|
|
|
|
|
|
NSUInteger i;
|
2011-07-02 22:15:17 +00:00
|
|
|
NSColor *link_color = nil;
|
2011-02-22 20:53:13 +00:00
|
|
|
const NSRange characterRange = [self characterRangeForGlyphRange: underlineRange
|
|
|
|
actualGlyphRange: NULL];
|
|
|
|
|
|
|
|
if (!(underlineRange.location >= fragmentGlyphRange.location &&
|
|
|
|
NSMaxRange(underlineRange) <= NSMaxRange(fragmentGlyphRange)))
|
|
|
|
{
|
|
|
|
NSLog(@"Error, underlineRange must be inside fragmentGlyphRange");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-07-02 22:15:17 +00:00
|
|
|
for (i = characterRange.location; i < NSMaxRange(characterRange); )
|
2011-02-22 20:53:13 +00:00
|
|
|
{
|
|
|
|
NSRange underlineColorCharacterRange, foregroundColorCharacterRange, rangeToDraw;
|
2011-07-02 22:15:17 +00:00
|
|
|
NSColor *underlineColor = nil;
|
|
|
|
NSRange glyphRangeToDraw;
|
|
|
|
NSRange linkCharacterRange;
|
|
|
|
id linkValue;
|
|
|
|
|
|
|
|
linkValue = [_textStorage attribute: NSLinkAttributeName
|
|
|
|
atIndex: i
|
|
|
|
longestEffectiveRange: &linkCharacterRange
|
|
|
|
inRange: NSMakeRange(i, NSMaxRange(characterRange)-i)];
|
|
|
|
if (linkValue != nil)
|
2011-02-22 20:53:13 +00:00
|
|
|
{
|
2011-07-02 22:15:17 +00:00
|
|
|
if (link_color == nil)
|
|
|
|
{
|
|
|
|
NSDictionary *link_attributes = [[self firstTextView] linkTextAttributes];
|
|
|
|
link_color = [link_attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
}
|
|
|
|
if (link_color != nil)
|
|
|
|
underlineColor = link_color;
|
|
|
|
|
|
|
|
underlineColorCharacterRange = linkCharacterRange;
|
2011-02-22 20:53:13 +00:00
|
|
|
}
|
2011-07-02 22:15:17 +00:00
|
|
|
else
|
2011-02-22 20:53:13 +00:00
|
|
|
{
|
2011-07-02 22:15:17 +00:00
|
|
|
underlineColor = (NSColor*)[[self textStorage]
|
|
|
|
attribute: NSUnderlineColorAttributeName
|
|
|
|
atIndex: i
|
|
|
|
longestEffectiveRange: &underlineColorCharacterRange
|
|
|
|
inRange: NSMakeRange(i, NSMaxRange(characterRange)-i)];
|
|
|
|
underlineColorCharacterRange = NSIntersectionRange(underlineColorCharacterRange,
|
|
|
|
linkCharacterRange);
|
2011-02-22 20:53:13 +00:00
|
|
|
}
|
2011-07-02 22:15:17 +00:00
|
|
|
|
|
|
|
if (underlineColor != nil)
|
2011-02-22 20:53:13 +00:00
|
|
|
{
|
2011-07-02 22:15:17 +00:00
|
|
|
[underlineColor set];
|
|
|
|
rangeToDraw = underlineColorCharacterRange;
|
2011-02-22 20:53:13 +00:00
|
|
|
}
|
2011-07-02 22:15:17 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
NSColor *foregroundColor = (NSColor*)[[self textStorage]
|
|
|
|
attribute: NSForegroundColorAttributeName
|
|
|
|
atIndex: i
|
|
|
|
longestEffectiveRange: &foregroundColorCharacterRange
|
|
|
|
inRange: NSMakeRange(i, NSMaxRange(characterRange)-i)];
|
2011-02-22 20:53:13 +00:00
|
|
|
|
2011-07-02 22:15:17 +00:00
|
|
|
if (foregroundColor != nil)
|
|
|
|
{
|
|
|
|
[foregroundColor set];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[[NSColor textColor] set];
|
|
|
|
}
|
2011-02-22 20:53:13 +00:00
|
|
|
|
2011-07-02 22:15:17 +00:00
|
|
|
// Draw the smaller range
|
|
|
|
rangeToDraw = underlineColorCharacterRange.length < foregroundColorCharacterRange.length ?
|
|
|
|
underlineColorCharacterRange : foregroundColorCharacterRange;
|
|
|
|
}
|
|
|
|
|
|
|
|
glyphRangeToDraw = [self glyphRangeForCharacterRange: rangeToDraw
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
if (glyphRangeToDraw.length > 0)
|
|
|
|
{
|
|
|
|
// do the actual underline
|
|
|
|
|
2011-02-22 20:53:13 +00:00
|
|
|
// FIXME: find the largest font within the range to underline
|
2011-07-02 22:15:17 +00:00
|
|
|
// NOTE: GS private method
|
|
|
|
NSFont *largestFont = [self effectiveFontForGlyphAtIndex: glyphRangeToDraw.location
|
|
|
|
range: NULL];
|
2011-02-22 20:53:13 +00:00
|
|
|
|
|
|
|
const CGFloat underlineWidth = [largestFont pointSize] *
|
|
|
|
(((type & NSUnderlineStyleDouble) != 0) ? 0.05 : 0.07);
|
|
|
|
|
|
|
|
NSPoint start = [self locationForGlyphAtIndex: glyphRangeToDraw.location];
|
2011-07-02 22:15:17 +00:00
|
|
|
NSPoint end = [self locationForGlyphAtIndex: NSMaxRange(glyphRangeToDraw) - 1];
|
2011-02-22 20:53:13 +00:00
|
|
|
|
|
|
|
// FIXME: remove this hack lowers the underline slightly
|
|
|
|
start.y += [largestFont pointSize] * 0.07;
|
|
|
|
end.y += [largestFont pointSize] * 0.07;
|
|
|
|
|
2016-10-23 20:13:37 +00:00
|
|
|
end.x += [self advancementForGlyphAtIndex: (NSMaxRange(glyphRangeToDraw) - 1)].width;
|
2011-02-22 20:53:13 +00:00
|
|
|
|
|
|
|
start = NSMakePoint(start.x + containerOrigin.x + fragmentRect.origin.x, start.y + containerOrigin.y + fragmentRect.origin.y);
|
|
|
|
end = NSMakePoint(end.x + containerOrigin.x + fragmentRect.origin.x, end.y + containerOrigin.y + fragmentRect.origin.y);
|
|
|
|
|
|
|
|
if ((type & NSUnderlineStyleDouble) == NSUnderlineStyleDouble)
|
|
|
|
{
|
|
|
|
GSDrawPatternLine(NSMakePoint(start.x, start.y - (underlineWidth / 2)),
|
|
|
|
NSMakePoint(end.x, end.y - (underlineWidth / 2)),
|
|
|
|
type, underlineWidth / 2, start.x);
|
|
|
|
GSDrawPatternLine(NSMakePoint(start.x, start.y + (underlineWidth / 2)),
|
|
|
|
NSMakePoint(end.x, end.y + (underlineWidth / 2)),
|
|
|
|
type, underlineWidth / 2, start.x);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GSDrawPatternLine(start, end, type, underlineWidth, start.x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i += rangeToDraw.length;
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@end
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2011-03-12 07:45:14 +00:00
|
|
|
@implementation NSLayoutManager (spelling)
|
|
|
|
|
|
|
|
-(void) _drawSpellingState: (NSInteger)spellingState
|
|
|
|
forGylphRange: (NSRange)range
|
|
|
|
lineFragmentRect: (NSRect)fragmentRect
|
|
|
|
lineFragmentGlyphRange: (NSRange)fragmentGlyphRange
|
|
|
|
containerOrigin: (NSPoint)containerOrigin
|
|
|
|
{
|
|
|
|
NSBezierPath *path;
|
2013-02-01 14:03:08 +00:00
|
|
|
const CGFloat pattern[2] = {2.5, 1.0};
|
2011-03-12 07:45:14 +00:00
|
|
|
NSFont *largestFont = [self effectiveFontForGlyphAtIndex: range.location // NOTE: GS private method
|
|
|
|
range: NULL];
|
|
|
|
NSPoint start = [self locationForGlyphAtIndex: range.location];
|
|
|
|
NSPoint end = [self locationForGlyphAtIndex: NSMaxRange(range) - 1]; //FIXME: check length > 0
|
|
|
|
|
|
|
|
if (spellingState == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: calculate the underline position correctly, using the font on both the start and end glyph
|
|
|
|
start.y += [largestFont pointSize] * 0.07;
|
|
|
|
end.y += [largestFont pointSize] * 0.07;
|
|
|
|
|
2016-10-23 20:13:37 +00:00
|
|
|
end.x += [self advancementForGlyphAtIndex: (NSMaxRange(range) - 1)].width;
|
2011-03-12 07:45:14 +00:00
|
|
|
|
|
|
|
start = NSMakePoint(start.x + containerOrigin.x + fragmentRect.origin.x, start.y + containerOrigin.y + fragmentRect.origin.y);
|
|
|
|
end = NSMakePoint(end.x + containerOrigin.x + fragmentRect.origin.x, end.y + containerOrigin.y + fragmentRect.origin.y);
|
|
|
|
|
|
|
|
path = [NSBezierPath bezierPath];
|
|
|
|
[path setLineDash: pattern count: 2 phase: 0];
|
|
|
|
[path setLineWidth: 1.5];
|
|
|
|
[path moveToPoint: start];
|
|
|
|
[path lineToPoint: end];
|
|
|
|
|
|
|
|
if ((spellingState & NSSpellingStateGrammarFlag) != 0)
|
|
|
|
{
|
|
|
|
[[NSColor greenColor] set];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[[NSColor redColor] set];
|
|
|
|
}
|
|
|
|
|
|
|
|
[path stroke];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@implementation NSLayoutManager
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2011-06-11 12:01:59 +00:00
|
|
|
/*
|
|
|
|
* Class methods
|
|
|
|
*/
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [NSLayoutManager class])
|
|
|
|
{
|
|
|
|
[self exposeBinding: @"hyphenationFactor"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(void) insertTextContainer: (NSTextContainer *)aTextContainer
|
|
|
|
atIndex: (unsigned int)index
|
2000-10-12 23:01:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
2000-10-12 23:01:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[super insertTextContainer: aTextContainer
|
|
|
|
atIndex: index];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
|
|
|
[[textcontainers[i].textContainer textView] _updateMultipleTextViews];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
-(void) removeTextContainerAtIndex: (unsigned int)index
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
|
|
|
NSTextView *tv = [textcontainers[index].textContainer textView];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
RETAIN(tv);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[super removeTextContainerAtIndex: index];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[tv _updateMultipleTextViews];
|
|
|
|
RELEASE(tv);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
|
|
|
[[textcontainers[i].textContainer textView] _updateMultipleTextViews];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(void) dealloc
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2011-07-11 08:40:31 +00:00
|
|
|
// Remove all key value bindings for this object.
|
|
|
|
[GSKeyValueBinding unbindAllForObject: self];
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
DESTROY(_typingAttributes);
|
2011-03-09 05:28:47 +00:00
|
|
|
DESTROY(_temporaryAttributes);
|
2003-01-26 19:21:40 +00:00
|
|
|
[super dealloc];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
2003-02-16 21:40:47 +00:00
|
|
|
/*
|
|
|
|
TODO: Add a general typesetterAttributes dictionary. Implement the
|
|
|
|
hyphenation factor methods by setting/getting an attribute in this
|
|
|
|
dictionary.
|
|
|
|
*/
|
2003-01-26 19:21:40 +00:00
|
|
|
-(float) hyphenationFactor
|
2000-08-27 22:32:29 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
return 0.0;
|
1999-08-19 23:18:25 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(void) setHyphenationFactor: (float)factor
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-02-16 21:40:47 +00:00
|
|
|
NSLog(@"Warning: (NSLayoutManager) %s not implemented", __PRETTY_FUNCTION__);
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(NSTextView *) firstTextView
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
|
|
|
NSTextView *tv;
|
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
2002-02-06 16:20:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
tv = [textcontainers[i].textContainer textView];
|
|
|
|
if (tv)
|
|
|
|
return tv;
|
2002-02-06 16:20:43 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
return nil;
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(NSTextView *) textViewForBeginningOfSelection
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
/* TODO */
|
2003-01-26 19:21:40 +00:00
|
|
|
return [self firstTextView];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(BOOL) layoutManagerOwnsFirstResponderInWindow: (NSWindow *)window
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
2006-07-04 21:31:49 +00:00
|
|
|
NSResponder *tv;
|
|
|
|
NSResponder *v = [window firstResponder];
|
2000-12-21 17:29:51 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
2000-12-21 17:29:51 +00:00
|
|
|
{
|
2006-07-04 21:31:49 +00:00
|
|
|
tv = [textcontainers[i].textContainer textView];
|
2003-01-26 19:21:40 +00:00
|
|
|
if (tv == v)
|
|
|
|
return YES;
|
2000-12-21 17:29:51 +00:00
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(NSArray *) rulerMarkersForTextView: (NSTextView *)textView
|
2003-02-16 14:54:14 +00:00
|
|
|
paragraphStyle: (NSParagraphStyle *)paragraphStyle
|
|
|
|
ruler: (NSRulerView *)aRulerView
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-02-16 14:54:14 +00:00
|
|
|
NSRulerMarker *marker;
|
|
|
|
NSTextTab *tab;
|
|
|
|
NSImage *image;
|
|
|
|
NSArray *tabs = [paragraphStyle tabStops];
|
|
|
|
NSEnumerator *enumerator = [tabs objectEnumerator];
|
|
|
|
NSMutableArray *markers = [NSMutableArray arrayWithCapacity: [tabs count]];
|
|
|
|
|
|
|
|
while ((tab = [enumerator nextObject]) != nil)
|
|
|
|
{
|
|
|
|
switch ([tab tabStopType])
|
|
|
|
{
|
|
|
|
case NSLeftTabStopType:
|
|
|
|
image = [NSImage imageNamed: @"common_LeftTabStop"];
|
|
|
|
break;
|
|
|
|
case NSRightTabStopType:
|
|
|
|
image = [NSImage imageNamed: @"common_RightTabStop"];
|
|
|
|
break;
|
|
|
|
case NSCenterTabStopType:
|
|
|
|
image = [NSImage imageNamed: @"common_CenterTabStop"];
|
|
|
|
break;
|
|
|
|
case NSDecimalTabStopType:
|
|
|
|
image = [NSImage imageNamed: @"common_DecimalTabStop"];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
image = nil;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
marker = [[NSRulerMarker alloc]
|
|
|
|
initWithRulerView: aRulerView
|
|
|
|
markerLocation: [tab location]
|
|
|
|
image: image
|
|
|
|
imageOrigin: NSMakePoint(0, 0)];
|
|
|
|
[marker setRepresentedObject: tab];
|
|
|
|
[markers addObject: marker];
|
2010-09-12 16:34:22 +00:00
|
|
|
RELEASE(marker);
|
2003-02-16 14:54:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return markers;
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(NSView *) rulerAccessoryViewForTextView: (NSTextView *)textView
|
|
|
|
paragraphStyle: (NSParagraphStyle *)style
|
|
|
|
ruler: (NSRulerView *)ruler
|
|
|
|
enabled: (BOOL)isEnabled
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
/* TODO */
|
2003-01-26 19:21:40 +00:00
|
|
|
return nil;
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2000-11-03 00:21:29 +00:00
|
|
|
/*
|
2003-01-26 19:21:40 +00:00
|
|
|
TODO: not really clear what these should do
|
|
|
|
*/
|
|
|
|
-(void) invalidateDisplayForGlyphRange: (NSRange)aRange
|
2000-11-03 00:21:29 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
int i;
|
|
|
|
unsigned int m;
|
|
|
|
NSRange r;
|
|
|
|
NSRect rect;
|
|
|
|
NSPoint p;
|
|
|
|
NSTextView *tv;
|
2000-11-03 00:21:29 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
1999-08-19 23:18:25 +00:00
|
|
|
{
|
2003-02-23 01:01:13 +00:00
|
|
|
if (!textcontainers[i].num_linefrags)
|
1999-08-19 23:18:25 +00:00
|
|
|
break;
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (textcontainers[i].pos >= aRange.location + aRange.length)
|
|
|
|
break; /* we're past the end of the range */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
m = textcontainers[i].pos + textcontainers[i].length;
|
|
|
|
if (m < aRange.location)
|
|
|
|
continue;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
r.location = textcontainers[i].pos;
|
|
|
|
if (aRange.location > r.location)
|
|
|
|
r.location = aRange.location;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (m > aRange.location + aRange.length)
|
|
|
|
m = aRange.location + aRange.length;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
r.length = m - r.location;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Range r in this text view should be invalidated. */
|
|
|
|
rect = [self boundingRectForGlyphRange: r
|
|
|
|
inTextContainer: textcontainers[i].textContainer];
|
|
|
|
tv = [textcontainers[i].textContainer textView];
|
|
|
|
p = [tv textContainerOrigin];
|
|
|
|
rect.origin.x += p.x;
|
|
|
|
rect.origin.y += p.y;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
[tv setNeedsDisplayInRect: rect];
|
1999-08-19 23:18:25 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
}
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
-(void) invalidateDisplayForCharacterRange: (NSRange)aRange
|
|
|
|
{
|
2004-01-25 03:20:52 +00:00
|
|
|
if (layout_char <= aRange.location)
|
2003-01-26 19:21:40 +00:00
|
|
|
return;
|
|
|
|
if (layout_char < aRange.location + aRange.length)
|
|
|
|
aRange.length = layout_char - aRange.location;
|
|
|
|
[self invalidateDisplayForGlyphRange:
|
|
|
|
[self glyphRangeForCharacterRange: aRange
|
|
|
|
actualCharacterRange: NULL]];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
|
2003-02-18 00:35:23 +00:00
|
|
|
-(void) _didInvalidateLayout
|
1999-08-19 23:18:25 +00:00
|
|
|
{
|
2003-02-18 00:35:23 +00:00
|
|
|
unsigned int g;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Invalidate from the first glyph not laid out (which will
|
|
|
|
generally be the first glyph to have been invalidated). */
|
|
|
|
g = layout_glyph;
|
|
|
|
|
|
|
|
[super _didInvalidateLayout];
|
|
|
|
|
|
|
|
for (i = 0; i < num_textcontainers; i++)
|
|
|
|
{
|
|
|
|
if (textcontainers[i].complete &&
|
|
|
|
g < textcontainers[i].pos + textcontainers[i].length)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
[[textcontainers[i].textContainer textView] _layoutManagerDidInvalidateLayout];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
-(void) _dumpLayout
|
|
|
|
{
|
|
|
|
int i, j, k;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
|
|
|
linefrag_point_t *lp;
|
|
|
|
linefrag_attachment_t *la;
|
|
|
|
|
|
|
|
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
|
|
|
{
|
2003-02-23 01:01:13 +00:00
|
|
|
printf("tc %2i, %5i+%5i (complete %i)\n",
|
|
|
|
i,tc->pos,tc->length,tc->complete);
|
2003-02-18 17:15:25 +00:00
|
|
|
printf(" lfs: (%3i)\n", tc->num_linefrags);
|
|
|
|
for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++)
|
|
|
|
{
|
|
|
|
printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n",
|
|
|
|
j,lf->pos,lf->length,
|
|
|
|
lf->rect.origin.x,lf->rect.origin.y,
|
|
|
|
lf->rect.size.width,lf->rect.size.height);
|
|
|
|
for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++)
|
|
|
|
printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length);
|
|
|
|
for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++)
|
|
|
|
printf(" a%3i : %5i+%5i\n",k,la->pos,la->length);
|
|
|
|
}
|
|
|
|
printf(" softs: (%3i)\n", tc->num_soft);
|
|
|
|
for (; j < tc->num_linefrags + tc->num_soft; j++, lf++)
|
|
|
|
{
|
|
|
|
printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n",
|
|
|
|
j,lf->pos,lf->length,
|
|
|
|
lf->rect.origin.x,lf->rect.origin.y,
|
|
|
|
lf->rect.size.width,lf->rect.size.height);
|
|
|
|
for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++)
|
|
|
|
printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length);
|
|
|
|
for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++)
|
|
|
|
printf(" a%3i : %5i+%5i\n",k,la->pos,la->length);
|
|
|
|
}
|
|
|
|
}
|
2003-02-23 01:01:13 +00:00
|
|
|
printf("layout to: char %i, glyph %i\n",layout_char,layout_glyph);
|
2003-02-18 17:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-18 00:35:23 +00:00
|
|
|
/*
|
|
|
|
We completely override this method and use the extra information we have
|
|
|
|
about layout to do smarter invalidation. The comments at the beginning of
|
|
|
|
this file describes this.
|
|
|
|
*/
|
|
|
|
- (void) textStorage: (NSTextStorage *)aTextStorage
|
|
|
|
edited: (unsigned int)mask
|
|
|
|
range: (NSRange)range
|
|
|
|
changeInLength: (int)lengthChange
|
|
|
|
invalidatedRange: (NSRange)invalidatedRange
|
|
|
|
{
|
|
|
|
NSRange r;
|
2004-03-22 00:17:18 +00:00
|
|
|
unsigned int original_last_glyph;
|
2003-02-18 17:15:25 +00:00
|
|
|
|
|
|
|
/* printf("\n*** invalidating\n");
|
|
|
|
[self _dumpLayout];*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Using -glyphRangeForChara... here would be safer, but we must make
|
|
|
|
absolutely sure that we don't cause any glyph generation until the
|
|
|
|
invalidation is done.
|
|
|
|
|
|
|
|
TODO: make sure last_glyph is set as expected
|
|
|
|
*/
|
|
|
|
original_last_glyph = layout_glyph;
|
2003-02-18 00:35:23 +00:00
|
|
|
|
|
|
|
if (!(mask & NSTextStorageEditedCharacters))
|
|
|
|
lengthChange = 0;
|
|
|
|
|
2011-03-09 05:28:47 +00:00
|
|
|
if (_temporaryAttributes != nil && (mask & NSTextStorageEditedCharacters) != 0)
|
|
|
|
{
|
2011-03-14 04:19:32 +00:00
|
|
|
int i;
|
|
|
|
NSArray *attrs;
|
2011-03-09 05:28:47 +00:00
|
|
|
NSRange oldRange = NSMakeRange(range.location, range.length - lengthChange);
|
|
|
|
|
|
|
|
NSString *replacementString = [[GSDummyMutableString alloc] initWithLength: range.length];
|
|
|
|
[_temporaryAttributes replaceCharactersInRange: oldRange
|
|
|
|
withString: replacementString];
|
|
|
|
[replacementString release];
|
2011-03-14 04:19:32 +00:00
|
|
|
|
|
|
|
// In addition, clear any temporary attributes that may have been extended
|
|
|
|
// over the affected range
|
|
|
|
|
|
|
|
if (range.length > 0)
|
|
|
|
{
|
|
|
|
attrs = [[self temporaryAttributesAtCharacterIndex: range.location
|
|
|
|
effectiveRange: NULL] allKeys];
|
|
|
|
for (i=0; i<[attrs count]; i++)
|
|
|
|
{
|
|
|
|
[self removeTemporaryAttribute: [attrs objectAtIndex: i]
|
|
|
|
forCharacterRange: range];
|
|
|
|
}
|
|
|
|
}
|
2011-03-09 05:28:47 +00:00
|
|
|
}
|
|
|
|
|
2003-02-18 00:35:23 +00:00
|
|
|
[self invalidateGlyphsForCharacterRange: invalidatedRange
|
2003-01-26 19:21:40 +00:00
|
|
|
changeInLength: lengthChange
|
2003-02-18 00:35:23 +00:00
|
|
|
actualCharacterRange: &r];
|
|
|
|
|
2004-03-22 00:17:18 +00:00
|
|
|
/*
|
|
|
|
If we had layout information and we had layout information for the range
|
|
|
|
of characters that was modified, we need to invalidate layout information.
|
|
|
|
|
|
|
|
TODO: This is broken. Even if we don't have layout for the modified
|
|
|
|
characters, we might have layout for the preceeding line, and we then need
|
|
|
|
to invalidate that line. Need to rework this a bit... I really really need
|
|
|
|
to know the glyph length change here. :/
|
|
|
|
(Alexander Malmberg 2004-03-22)
|
|
|
|
*/
|
|
|
|
if (layout_char > 0 && layout_char >= r.location)
|
2003-02-18 17:15:25 +00:00
|
|
|
{
|
|
|
|
unsigned int glyph_index, last_glyph;
|
2003-02-18 00:35:23 +00:00
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
2003-02-18 17:15:25 +00:00
|
|
|
int i, j, k;
|
2003-02-18 00:35:23 +00:00
|
|
|
int new_num;
|
|
|
|
NSRange char_range;
|
2004-03-22 00:17:18 +00:00
|
|
|
unsigned int new_last_glyph;
|
|
|
|
int glyph_delta;
|
|
|
|
|
|
|
|
/*
|
|
|
|
If we had layout beyond the modified characters, update layout_char.
|
|
|
|
Otherwise, just pretend that we have layout up to the end of the range
|
|
|
|
after the change. This will give glyph_delta and last_glyph incorrect
|
|
|
|
values, strictly speaking, but glyph_delta is only used if we have
|
|
|
|
layout beyond the modified range, and last_glyph is used in a way that
|
|
|
|
makes it safe to overestimate it (as we do here).
|
|
|
|
|
|
|
|
When I can get exact information about the modified glyphs (TODO above),
|
|
|
|
all this will become much cleaner...
|
|
|
|
*/
|
|
|
|
if (layout_char >= r.location + r.length - lengthChange)
|
|
|
|
layout_char += lengthChange;
|
|
|
|
else
|
|
|
|
layout_char = r.location + r.length;
|
|
|
|
|
|
|
|
if (!layout_char)
|
|
|
|
new_last_glyph = 0;
|
2015-01-31 18:19:29 +00:00
|
|
|
else if (layout_char >= [_textStorage length])
|
2004-03-22 00:17:18 +00:00
|
|
|
new_last_glyph = [self numberOfGlyphs];
|
|
|
|
else
|
|
|
|
new_last_glyph = [self glyphRangeForCharacterRange: NSMakeRange(layout_char, 1)
|
|
|
|
actualCharacterRange: NULL].location;
|
|
|
|
|
|
|
|
glyph_delta = new_last_glyph - original_last_glyph;
|
2003-02-18 00:35:23 +00:00
|
|
|
|
2003-02-18 20:26:13 +00:00
|
|
|
/*
|
2004-03-22 00:17:18 +00:00
|
|
|
Note that r.location might not actually have any text container or
|
|
|
|
line frag.
|
2003-02-18 20:26:13 +00:00
|
|
|
*/
|
2004-01-25 03:20:52 +00:00
|
|
|
if (!r.location)
|
|
|
|
{
|
|
|
|
glyph_index = 0;
|
|
|
|
}
|
|
|
|
else if (r.location == [_textStorage length])
|
2003-02-18 00:35:23 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Since layout was built beyond r.location, glyphs must have been
|
|
|
|
too, so invalidation only removed trailing glyphs and we still
|
|
|
|
have glyphs built up to the end. Thus, -numberOfGlyphs is cheap
|
|
|
|
to call.
|
|
|
|
*/
|
|
|
|
glyph_index = [self numberOfGlyphs];
|
|
|
|
char_range.location = [_textStorage length];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Will cause generation of glyphs, but I consider that acceptable
|
|
|
|
for now. Soft-invalidation will cause even more glyph generation,
|
|
|
|
anyway.
|
|
|
|
*/
|
|
|
|
glyph_index =
|
|
|
|
[self glyphRangeForCharacterRange: NSMakeRange(r.location,1)
|
|
|
|
actualCharacterRange: &char_range].location;
|
|
|
|
}
|
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
/*
|
|
|
|
For soft invalidation, we need to know where to stop hard-invalidating.
|
|
|
|
This will cause immediate glyph generation to fill the gaps the
|
|
|
|
invalidation caused.
|
|
|
|
*/
|
|
|
|
if (NSMaxRange(r) == [_textStorage length])
|
|
|
|
{
|
|
|
|
last_glyph = [self numberOfGlyphs];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last_glyph =
|
|
|
|
[self glyphRangeForCharacterRange: NSMakeRange(NSMaxRange(r),1)
|
|
|
|
actualCharacterRange: NULL].location;
|
|
|
|
}
|
|
|
|
last_glyph -= glyph_delta;
|
2003-02-18 00:35:23 +00:00
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
/* glyph_index is the first index we should invalidate for. */
|
|
|
|
for (j = 0, tc = textcontainers; j < num_textcontainers; j++, tc++)
|
|
|
|
if (tc->pos + tc->length >= glyph_index)
|
|
|
|
break;
|
2003-02-18 00:35:23 +00:00
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
LINEFRAG_FOR_GLYPH(glyph_index);
|
|
|
|
|
|
|
|
/*
|
|
|
|
We invalidate the entire line containing lf, and the entire
|
|
|
|
previous line. Thus, we scan backwards to find the first line frag
|
|
|
|
on the previous line.
|
|
|
|
*/
|
|
|
|
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
|
|
|
lf--, i--;
|
|
|
|
/* Now we have the first line frag on this line. */
|
|
|
|
if (i > 0)
|
|
|
|
{
|
2003-02-18 00:35:23 +00:00
|
|
|
lf--, i--;
|
2003-02-18 17:15:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The previous line isn't in this text container, so we move
|
|
|
|
to the previous text container.
|
|
|
|
*/
|
|
|
|
if (j > 0)
|
|
|
|
{
|
|
|
|
j--;
|
|
|
|
tc--;
|
|
|
|
i = tc->num_linefrags - 1;
|
|
|
|
lf = tc->linefrags + i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Last line frag on previous line. */
|
|
|
|
while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y)
|
|
|
|
lf--, i--;
|
|
|
|
/* First line frag on previous line. */
|
|
|
|
|
|
|
|
/* Invalidate all line frags that intersect the invalidated range. */
|
|
|
|
new_num = i;
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
for (; i < tc->num_linefrags + tc->num_soft; i++, lf++)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Since we must invalidate whole lines, we can only stop if
|
|
|
|
the line frag is beyond the invalidated range, and the line
|
|
|
|
frag is the first line frag in a line.
|
|
|
|
*/
|
|
|
|
if (lf->pos >= last_glyph &&
|
|
|
|
(!i || lf[-1].rect.origin.y != lf->rect.origin.y))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lf->points)
|
|
|
|
{
|
2011-04-08 23:21:55 +00:00
|
|
|
free(lf->points);
|
2003-02-18 17:15:25 +00:00
|
|
|
lf->points = NULL;
|
|
|
|
}
|
|
|
|
if (lf->attachments)
|
|
|
|
{
|
2011-04-08 23:21:55 +00:00
|
|
|
free(lf->attachments);
|
2003-02-18 17:15:25 +00:00
|
|
|
lf->attachments = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < tc->num_linefrags + tc->num_soft)
|
|
|
|
break;
|
|
|
|
tc->num_linefrags = new_num;
|
|
|
|
tc->num_soft = 0;
|
|
|
|
tc->was_invalidated = YES;
|
|
|
|
tc->complete = NO;
|
|
|
|
if (new_num)
|
|
|
|
{
|
|
|
|
tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tc->pos = tc->length = 0;
|
|
|
|
}
|
2003-02-18 00:35:23 +00:00
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
j++, tc++;
|
|
|
|
if (j == num_textcontainers)
|
|
|
|
break;
|
|
|
|
|
|
|
|
new_num = 0;
|
|
|
|
i = 0;
|
|
|
|
lf = tc->linefrags;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j == num_textcontainers)
|
|
|
|
goto no_soft_invalidation;
|
2003-02-18 00:35:23 +00:00
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
if (new_num != i)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
There's a gap between the last valid line frag and the first
|
|
|
|
soft line frag. Compact the linefrags.
|
|
|
|
*/
|
|
|
|
memmove(tc->linefrags + new_num, lf, sizeof(linefrag_t) * (tc->num_linefrags + tc->num_soft - i));
|
|
|
|
tc->num_linefrags -= i - new_num;
|
|
|
|
i = new_num;
|
|
|
|
lf = tc->linefrags + i;
|
|
|
|
}
|
|
|
|
tc->num_soft += tc->num_linefrags - new_num;
|
|
|
|
tc->num_linefrags = new_num;
|
|
|
|
tc->was_invalidated = YES;
|
|
|
|
tc->complete = NO;
|
|
|
|
if (new_num)
|
|
|
|
{
|
2003-02-23 01:01:13 +00:00
|
|
|
tc->length = tc->linefrags[new_num - 1].pos + tc->linefrags[new_num - 1].length - tc->pos;
|
2003-02-18 17:15:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tc->pos = tc->length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Soft invalidate all remaining layout. Update their glyph positions
|
|
|
|
and set the soft-invalidate markers in the text containers.
|
|
|
|
*/
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
for (; i < tc->num_linefrags + tc->num_soft; i++, lf++)
|
|
|
|
{
|
|
|
|
lf->pos += glyph_delta;
|
|
|
|
for (k = 0; k < lf->num_points; k++)
|
|
|
|
lf->points[k].pos += glyph_delta;
|
|
|
|
for (k = 0; k < lf->num_attachments; k++)
|
|
|
|
lf->attachments[k].pos += glyph_delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
j++, tc++;
|
|
|
|
if (j == num_textcontainers)
|
|
|
|
break;
|
|
|
|
i = 0;
|
|
|
|
lf = tc->linefrags;
|
2003-02-25 16:40:20 +00:00
|
|
|
tc->num_soft += tc->num_linefrags;
|
|
|
|
tc->num_linefrags = 0;
|
|
|
|
tc->was_invalidated = YES;
|
|
|
|
tc->complete = NO;
|
2003-02-18 17:15:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
no_soft_invalidation:
|
|
|
|
/* Set layout_glyph and layout_char. */
|
|
|
|
for (i = num_textcontainers - 1, tc = textcontainers + i; i >= 0; i--, tc--)
|
|
|
|
{
|
2003-02-23 01:01:13 +00:00
|
|
|
if (tc->num_linefrags)
|
2003-02-18 17:15:25 +00:00
|
|
|
{
|
|
|
|
layout_glyph = tc->pos + tc->length;
|
|
|
|
if (layout_glyph == glyphs->glyph_length)
|
|
|
|
layout_char = glyphs->char_length;
|
|
|
|
else
|
|
|
|
layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < 0)
|
|
|
|
layout_glyph = layout_char = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
linefrag_t *lf;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
/*
|
|
|
|
TODO: could handle this better, but it should be a rare case,
|
|
|
|
handling it efficiently is tricky.
|
|
|
|
|
|
|
|
For now, we simply clear out all soft invalidation information.
|
|
|
|
*/
|
|
|
|
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
|
|
|
{
|
2003-02-19 14:12:55 +00:00
|
|
|
for (j = 0, lf = tc->linefrags + tc->num_linefrags; j < tc->num_soft; j++, lf++)
|
2003-02-18 17:15:25 +00:00
|
|
|
{
|
|
|
|
if (lf->points)
|
|
|
|
{
|
2011-04-08 23:21:55 +00:00
|
|
|
free(lf->points);
|
2003-02-18 17:15:25 +00:00
|
|
|
lf->points = NULL;
|
|
|
|
}
|
|
|
|
if (lf->attachments)
|
|
|
|
{
|
2011-04-08 23:21:55 +00:00
|
|
|
free(lf->attachments);
|
2003-02-18 17:15:25 +00:00
|
|
|
lf->attachments = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tc->num_soft = 0;
|
2003-02-18 17:24:24 +00:00
|
|
|
if (tc->pos + tc->length == r.location)
|
|
|
|
{
|
|
|
|
tc->complete = NO;
|
|
|
|
}
|
2003-02-18 17:15:25 +00:00
|
|
|
}
|
2003-02-18 00:35:23 +00:00
|
|
|
}
|
|
|
|
|
2003-02-22 01:43:11 +00:00
|
|
|
/* Clear the extra line fragment information. */
|
|
|
|
extra_textcontainer = nil;
|
|
|
|
|
2003-02-18 17:15:25 +00:00
|
|
|
/* [self _dumpLayout];
|
|
|
|
printf("*** done\n");*/
|
|
|
|
|
2003-02-18 00:35:23 +00:00
|
|
|
[self _didInvalidateLayout];
|
|
|
|
|
2004-08-11 23:46:37 +00:00
|
|
|
if (mask & NSTextStorageEditedCharacters)
|
2003-01-28 21:57:22 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Adjust the selected range so it's still valid. We don't try to
|
|
|
|
be smart here (smart adjustments will have to be done by whoever
|
|
|
|
made the change), we just want to keep it in range to avoid crashes.
|
|
|
|
|
|
|
|
TODO: It feels slightly ugly to be doing this here, but there aren't
|
|
|
|
many other places that can do this, and it gives reasonable behavior
|
|
|
|
for select-only text views.
|
|
|
|
|
|
|
|
One option is to only adjust when absolutely necessary to keep the
|
|
|
|
selected range valid.
|
2003-07-05 16:04:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
Current behavior for all cases:
|
|
|
|
|
|
|
|
Start End Action
|
|
|
|
(of selection, wrt range, before change)
|
|
|
|
--------------------------
|
|
|
|
after after location += lengthChange;
|
2015-09-13 21:36:40 +00:00
|
|
|
in after length = NSMaxRange(sel)-(NSMaxRange(range)-lengthChange); location=NSMaxRange(range);
|
2004-08-11 23:46:37 +00:00
|
|
|
in in length = 0; location=NSMaxRange(range);
|
2003-07-05 16:04:05 +00:00
|
|
|
before after length += lengthChange;
|
|
|
|
before in length = range.location-location;
|
|
|
|
before before do nothing
|
|
|
|
|
|
|
|
In other words, unless the selection spans over the entire changed
|
|
|
|
range, the changed range is deselected.
|
|
|
|
|
2004-03-13 02:22:12 +00:00
|
|
|
One important property of this behavior is that if length is 0 before,
|
2003-07-05 16:04:05 +00:00
|
|
|
it will be 0 after.
|
2003-01-28 21:57:22 +00:00
|
|
|
*/
|
2004-08-11 23:46:37 +00:00
|
|
|
NSRange newRange = _selected_range;
|
|
|
|
|
2003-07-05 16:04:05 +00:00
|
|
|
if (_selected_range.location >= NSMaxRange(range) - lengthChange)
|
|
|
|
{ /* after after */
|
2004-08-11 23:46:37 +00:00
|
|
|
newRange.location += lengthChange;
|
2003-01-28 21:57:22 +00:00
|
|
|
}
|
2003-07-05 16:04:05 +00:00
|
|
|
else if (_selected_range.location >= range.location)
|
2003-01-28 21:57:22 +00:00
|
|
|
{
|
2003-07-05 16:04:05 +00:00
|
|
|
if (NSMaxRange(_selected_range) > NSMaxRange(range) - lengthChange)
|
|
|
|
{ /* in after */
|
2015-09-13 21:36:40 +00:00
|
|
|
newRange.length = NSMaxRange(_selected_range) - (NSMaxRange(range) - lengthChange);
|
2004-08-11 23:46:37 +00:00
|
|
|
newRange.location = NSMaxRange(range);
|
2003-01-29 15:05:12 +00:00
|
|
|
}
|
|
|
|
else
|
2003-07-05 16:04:05 +00:00
|
|
|
{ /* in in */
|
2004-08-11 23:46:37 +00:00
|
|
|
newRange.length = 0;
|
|
|
|
newRange.location = NSMaxRange(range);
|
2003-01-29 15:05:12 +00:00
|
|
|
}
|
2003-01-28 21:57:22 +00:00
|
|
|
}
|
2003-07-05 16:04:05 +00:00
|
|
|
else if (NSMaxRange(_selected_range) > NSMaxRange(range) - lengthChange)
|
|
|
|
{ /* before after */
|
2004-08-11 23:46:37 +00:00
|
|
|
newRange.length += lengthChange;
|
2003-07-05 16:04:05 +00:00
|
|
|
}
|
|
|
|
else if (NSMaxRange(_selected_range) > range.location)
|
|
|
|
{ /* before in */
|
2004-08-11 23:46:37 +00:00
|
|
|
newRange.length = range.location - _selected_range.location;
|
2003-07-05 16:04:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* before before */
|
|
|
|
}
|
2004-08-11 23:46:37 +00:00
|
|
|
|
2015-09-13 21:36:40 +00:00
|
|
|
/* sanity check */
|
|
|
|
if (NSMaxRange(newRange) > [_textStorage length])
|
|
|
|
{
|
|
|
|
newRange = NSMakeRange(MIN(range.location, [_textStorage length]), 0);
|
|
|
|
}
|
|
|
|
|
2005-01-21 21:43 Alexander Malmberg <alexander@malmberg.org>
Various whitespace cleanups, comment type fixes, and changes
to avoid warnings from recent versions of gcc.
* Headers/Additions/GNUstepGUI/GSToolbar.h (-_toolbars): Declare.
* Source/NSWindow+Toolbar.m: Remove conflicting declaration of
[NSToolbar -_toolbars].
* Headers/Additions/GNUstepGUI/GSServicesManager.h,
Source/GSServicesMananger.m (-item2title:, -validateMenuItem:):
Adjust argument types.
* Headers/AppKit/NSMenu.h (-validateMenuItem:): Adjust argument
type.
* Source/NSTextView.m (-sizeToFit): Don't use size uninitialized
if neither resizable flags is set.
(-insertText:): Adjust argument type.
* Headers/AppKit/NSResponder.h, Source/NSResponder.m (-insertText:):
Adjust argument type. Document.
* Headers/AppKit/NSView.h: Change type of ivar _window to NSWindow *.
* Source/GSTitleView.m (-mouseDown:): Always initialize
startWindowOrigin.
* Source/NSApplication.m (-setApplicationIconImage:): Add casts
to avoid warnings.
* Source/NSCell.m (-cellSize): Add default: case.
* Source/NSPasteboard.m
([GSFiltered -pasteboard:provideDataForType:]): Detect and warn if we
can't find a filter that will get us the desired type.
* Source/NSProgressIndicator.m: Comment out unused variable 'images'.
* Source/NSBezierPath.m: Declare GSBezierPath fully before using it.
(-bezierPathByFlatteningPath, -bezierPathByReversingPath): Make sure
variables are always initialized.
* Source/NSMenuView.m,
* Source/NSPrintOperation.m,
* Source/NSSplitView.m,
* Source/NSTableHeaderView.m: Make sure variables are always
initialized.
* Source/NSBox.m,
* Source/NSImageview.m,
* Source/NSText.m,
* Source/NSTextStorage.m: Add missing includes.
* Source/GSKeyBindingTable.m,
* Source/GSLayoutManager.m,
* Source/NSBitmapImageRep+PNM.m,
* Source/NSBundleAdditions.m,
* Source/NSLayoutManager.m,
* Source/nsimage-tiff.h,
* Source/tiff.m,
* Headers/Additions/GNUstepGUI/GSDisplayServer.h,
* Source/GSDisplayServer.m: Change signedness of various variables.
* Source/NSPanel.m (-sendEvent:): Remove.
* Source/NSWindow.m (-becomesKeyOnlyIfNeeded): New method.
(-_sendEvent:becomesKeyOnlyIfNeeded:): Remove. Move code ...
(-sendEvent:): ... here. Use -becomesKeyOnlyIfNeeded instead
of the argument.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@20590 72102866-910b-0410-8b05-ffd578937521
2005-01-21 20:39:18 +00:00
|
|
|
/* If there are text views attached to us, let them handle the
|
2004-08-11 23:46:37 +00:00
|
|
|
change. */
|
|
|
|
if ([self firstTextView])
|
|
|
|
[[self firstTextView] setSelectedRange: newRange];
|
|
|
|
else
|
|
|
|
_selected_range = newRange;
|
2003-01-28 21:57:22 +00:00
|
|
|
}
|
2003-02-09 17:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-30 17:51:29 +00:00
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
|
|
{
|
|
|
|
int flags =
|
|
|
|
// FIXME attribute not yet supported by GNUstep
|
|
|
|
//defaultAttachementScaling |
|
|
|
|
(backgroundLayoutEnabled ? 0x04 : 0) |
|
|
|
|
(showsInvisibleCharacters ? 0x08 : 0) |
|
|
|
|
(showsControlCharacters ? 0x10 : 0);
|
|
|
|
|
|
|
|
[aCoder encodeObject: [self textContainers] forKey: @"NSTextContainers"];
|
|
|
|
[aCoder encodeObject: [self textStorage] forKey: @"NSTextStorage"];
|
|
|
|
[aCoder encodeObject: [self delegate] forKey: @"NSDelegate"];
|
|
|
|
[aCoder encodeInt: flags forKey: @"NSLMFlags"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-15 18:23:13 +00:00
|
|
|
- (id) initWithCoder: (NSCoder*)aDecoder
|
|
|
|
{
|
|
|
|
self = [self init];
|
|
|
|
|
|
|
|
if ([aDecoder allowsKeyedCoding])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int flags;
|
|
|
|
NSArray *array = [aDecoder decodeObjectForKey: @"NSTextContainers"];
|
|
|
|
NSTextStorage *storage = [aDecoder decodeObjectForKey: @"NSTextStorage"];
|
2009-02-04 06:48:06 +00:00
|
|
|
id delegate = [aDecoder decodeObjectForKey: @"NSDelegate"];
|
2004-02-15 18:23:13 +00:00
|
|
|
|
|
|
|
if ([aDecoder containsValueForKey: @"NSLMFlags"])
|
|
|
|
{
|
|
|
|
flags = [aDecoder decodeIntForKey: @"NSLMFlags"];
|
2010-05-30 17:51:29 +00:00
|
|
|
|
|
|
|
// FIXME attribute not yet supported by GNUstep
|
|
|
|
//defaultAttachementScaling = (NSImageScaling)(flags & 0x03);
|
|
|
|
backgroundLayoutEnabled = (flags & 0x04) != 0;
|
|
|
|
showsInvisibleCharacters = (flags & 0x08) != 0;
|
|
|
|
showsControlCharacters = (flags & 0x10) != 0;
|
2004-02-15 18:23:13 +00:00
|
|
|
}
|
|
|
|
[self setDelegate: delegate];
|
|
|
|
[storage addLayoutManager: self];
|
|
|
|
for (i = 0; i < [array count]; i++)
|
|
|
|
{
|
|
|
|
[self addTextContainer: [array objectAtIndex: i]];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 17:23:04 +00:00
|
|
|
- (NSDictionary *) typingAttributes
|
|
|
|
{
|
|
|
|
return _typingAttributes;
|
|
|
|
}
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
@end
|
2003-01-26 19:21:40 +00:00
|
|
|
|
2011-03-09 05:28:47 +00:00
|
|
|
|
|
|
|
@implementation GSDummyMutableString
|
|
|
|
- (id)initWithLength: (NSUInteger)aLength
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
if (self != nil)
|
|
|
|
{
|
|
|
|
self->_length = aLength;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
- (NSUInteger)length
|
|
|
|
{
|
|
|
|
return _length;
|
|
|
|
}
|
|
|
|
- (unichar)characterAtIndex: (NSUInteger)index
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString
|
|
|
|
{
|
|
|
|
_length = (_length - range.length) + [aString length];
|
|
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
|
|
{
|
|
|
|
return [self mutableCopyWithZone: zone];
|
|
|
|
}
|
|
|
|
- (id) mutableCopyWithZone: (NSZone*)zone
|
|
|
|
{
|
|
|
|
return [[GSDummyMutableString allocWithZone: zone] initWithLength: _length];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2008-11-23 23:54:00 +00:00
|
|
|
@implementation NSLayoutManager (temporaryattributes)
|
2010-12-30 15:29:05 +00:00
|
|
|
|
2011-03-09 05:28:47 +00:00
|
|
|
- (NSMutableAttributedString*) _temporaryAttributes
|
|
|
|
{
|
|
|
|
if (_temporaryAttributes == nil)
|
|
|
|
{
|
|
|
|
NSString *dummyString = [[GSDummyMutableString alloc] initWithLength: [[self textStorage] length]];
|
|
|
|
_temporaryAttributes = [[NSMutableAttributedString alloc] initWithString: dummyString];
|
|
|
|
[dummyString release];
|
|
|
|
}
|
|
|
|
return _temporaryAttributes;
|
|
|
|
}
|
|
|
|
|
2010-12-30 15:29:05 +00:00
|
|
|
- (void) setTemporaryAttributes: (NSDictionary *)attrs
|
|
|
|
forCharacterRange: (NSRange)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
[[self _temporaryAttributes] setAttributes: attrs range: range];
|
|
|
|
[self invalidateDisplayForCharacterRange: range];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-12-30 15:29:05 +00:00
|
|
|
- (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index
|
|
|
|
effectiveRange: (NSRange*)longestRange
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
return [[self _temporaryAttributes] attributesAtIndex: index effectiveRange: longestRange];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-12-30 15:29:05 +00:00
|
|
|
- (void) addTemporaryAttributes: (NSDictionary *)attrs
|
2010-08-29 18:49:03 +00:00
|
|
|
forCharacterRange: (NSRange)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
[[self _temporaryAttributes] addAttributes: attrs range: range];
|
|
|
|
[self invalidateDisplayForCharacterRange: range];
|
2010-12-30 15:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) addTemporaryAttribute: (NSString *)attr
|
|
|
|
value: (id)value
|
|
|
|
forCharacterRange: (NSRange)range
|
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
[[self _temporaryAttributes] addAttribute: attr value: value range: range];
|
|
|
|
[self invalidateDisplayForCharacterRange: range];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 18:49:03 +00:00
|
|
|
- (void) removeTemporaryAttribute: (NSString *)attr
|
|
|
|
forCharacterRange: (NSRange)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
[[self _temporaryAttributes] removeAttribute: attr range: range];
|
|
|
|
[self invalidateDisplayForCharacterRange: range];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 18:49:03 +00:00
|
|
|
- (id) temporaryAttribute: (NSString *)attr
|
|
|
|
atCharacterIndex: (NSUInteger)index
|
|
|
|
effectiveRange: (NSRange*)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
return [[self _temporaryAttributes] attribute: attr atIndex: index effectiveRange: range];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 18:49:03 +00:00
|
|
|
- (id) temporaryAttribute: (NSString *)attr
|
|
|
|
atCharacterIndex: (NSUInteger)index
|
|
|
|
longestEffectiveRange: (NSRange*)longestRange
|
|
|
|
inRange: (NSRange)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
return [[self _temporaryAttributes] attribute: attr atIndex: index longestEffectiveRange: longestRange inRange: range];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 18:49:03 +00:00
|
|
|
- (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index
|
|
|
|
longestEffectiveRange: (NSRange*)longestRange
|
|
|
|
inRange: (NSRange)range
|
2008-11-23 23:54:00 +00:00
|
|
|
{
|
2011-03-09 05:28:47 +00:00
|
|
|
return [[self _temporaryAttributes] attributesAtIndex: index longestEffectiveRange: longestRange inRange: range];
|
|
|
|
}
|
2010-12-30 15:29:05 +00:00
|
|
|
|
2011-03-09 05:28:47 +00:00
|
|
|
/**
|
|
|
|
* Most of this is implemented in GSLayoutManager
|
|
|
|
*/
|
|
|
|
- (void) setTextStorage: (NSTextStorage *)aTextStorage
|
|
|
|
{
|
|
|
|
DESTROY(_temporaryAttributes);
|
|
|
|
[super setTextStorage: aTextStorage];
|
2008-11-23 23:54:00 +00:00
|
|
|
}
|
2010-12-30 15:29:05 +00:00
|
|
|
|
2008-11-23 23:54:00 +00:00
|
|
|
@end
|