2003-01-26 19:21:40 +00:00
|
|
|
/*
|
|
|
|
NSLayoutManager.m
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
Copyright (C) 2002 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>
|
|
|
|
Date: 2002
|
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
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2003-01-26 19:21:40 +00:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
1999-07-15 17:32:38 +00:00
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
If not, write to the Free Software Foundation,
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
#include "AppKit/NSLayoutManager.h"
|
|
|
|
#include "AppKit/GSLayoutManager_internal.h"
|
2000-12-21 17:29:51 +00:00
|
|
|
|
2003-01-28 16:44:53 +00:00
|
|
|
#include <Foundation/NSException.h>
|
2003-01-26 19:21:40 +00:00
|
|
|
#include <AppKit/NSColor.h>
|
|
|
|
#include <AppKit/NSTextContainer.h>
|
|
|
|
#include <AppKit/NSTextStorage.h>
|
|
|
|
#include <AppKit/NSWindow.h>
|
|
|
|
#include <AppKit/DPSOperators.h>
|
2001-01-11 19:09:02 +00:00
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@interface NSLayoutManager (layout_helpers)
|
|
|
|
-(void) _doLayoutToContainer: (int)cindex point: (NSPoint)p;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSLayoutManager (layout_helpers)
|
|
|
|
-(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
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@implementation NSLayoutManager (layout)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
- (NSPoint) locationForGlyphAtIndex: (unsigned int)glyphIndex
|
|
|
|
{
|
|
|
|
NSRange r;
|
|
|
|
NSPoint p;
|
|
|
|
NSFont *f;
|
|
|
|
unsigned int i;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
r = [self rangeOfNominallySpacedGlyphsContainingIndex: glyphIndex
|
|
|
|
startLocation: &p];
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
i = r.location;
|
|
|
|
f = [self effectiveFontForGlyphAtIndex: i
|
|
|
|
range: &r];
|
|
|
|
for (; i < glyphIndex; i++)
|
|
|
|
{
|
|
|
|
if (i == r.location + r.length)
|
|
|
|
{
|
|
|
|
f = [self effectiveFontForGlyphAtIndex: i
|
|
|
|
range: &r];
|
|
|
|
}
|
|
|
|
p.x += [f advancementForGlyph: [self glyphAtIndex: i]].width;
|
|
|
|
}
|
|
|
|
return p;
|
2001-01-11 19:09:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +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-01-26 19:21:40 +00:00
|
|
|
- (NSRect *) rectArrayForGlyphRange: (NSRange)glyphRange
|
|
|
|
withinSelectedGlyphRange: (NSRange)selGlyphRange
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
|
|
|
rectCount: (unsigned int *)rectCount
|
2001-01-11 19:09:02 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
unsigned int last = glyphRange.location + glyphRange.length;
|
|
|
|
int i;
|
|
|
|
textcontainer_t *tc;
|
|
|
|
linefrag_t *lf;
|
|
|
|
int num_rects;
|
|
|
|
float x0, x1;
|
|
|
|
NSRect r;
|
|
|
|
|
|
|
|
|
|
|
|
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
|
|
|
|
if (tc->textContainer == container)
|
|
|
|
break;
|
2003-01-28 16:44:53 +00:00
|
|
|
[self _doLayoutToGlyph: last - 1];
|
2003-01-26 19:21:40 +00:00
|
|
|
if (i == num_textcontainers ||
|
|
|
|
tc->pos + tc->length < last ||
|
|
|
|
tc->pos > glyphRange.location)
|
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__);
|
|
|
|
else
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
format: @"%s invalid glyph range", __PRETTY_FUNCTION__];
|
2003-01-26 19:21:40 +00:00
|
|
|
*rectCount = 0;
|
|
|
|
return NULL;
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
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
|
|
|
*rectCount = 0;
|
|
|
|
return NULL;
|
2002-02-20 08:52:39 +00:00
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
num_rects = 0;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (lf = tc->linefrags, i = 0; i < tc->num_linefrags; i++, lf++)
|
|
|
|
if (lf->pos + lf->length > glyphRange.location)
|
|
|
|
break;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (lf->pos < glyphRange.location)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
linefrag_point_t *lp;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
for (j = 0, lp = lf->points; j < lf->num_points; j++)
|
|
|
|
if (lp->pos + lp->length > glyphRange.location)
|
|
|
|
break;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
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)
|
|
|
|
x0 += [r->font advancementForGlyph: r->glyphs[i].g].width;
|
|
|
|
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
x0 = NSMinX(lf->rect);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (lf->pos + lf->length > last)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
linefrag_point_t *lp;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int gpos, cpos;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
2003-01-26 19:21:40 +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. */
|
|
|
|
for (j = 0, lp = lf->points; j < lf->num_points; j++)
|
|
|
|
if (lp->pos < last)
|
|
|
|
break;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
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)
|
|
|
|
x1 += [r->font advancementForGlyph: r->glyphs[i].g].width;
|
|
|
|
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
|
|
|
else
|
2003-01-26 19:21:40 +00:00
|
|
|
x1 = NSMaxX(lf->rect);
|
|
|
|
|
|
|
|
r = NSMakeRect(x0, lf->rect.origin.y, x1 - x0, lf->rect.size.height);
|
|
|
|
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;
|
|
|
|
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-01-26 19:21:40 +00:00
|
|
|
- (NSRect *) rectArrayForCharacterRange: (NSRange)charRange
|
|
|
|
withinSelectedCharacterRange: (NSRange)selCharRange
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
|
|
|
rectCount: (unsigned int *)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
|
|
|
|
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-01-26 19:21:40 +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;
|
|
|
|
int 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-01-26 19:21:40 +00:00
|
|
|
int i, j;
|
|
|
|
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
|
|
|
/* NSLog(@"%@ %s (%g %g)+(%g %g) in %@\n", self, __PRETTY_FUNCTION__,
|
|
|
|
bounds.origin.x, bounds.origin.y,
|
|
|
|
bounds.size.width, bounds.size.height,
|
|
|
|
container);*/
|
2001-01-22 18:13:43 +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
|
|
|
|
point: NSMakePoint(NSMaxX(bounds),NSMaxY(bounds))];
|
2001-01-22 18:57:06 +00:00
|
|
|
|
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. */
|
|
|
|
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
|
|
|
// printf("low=%i (%g+%g) %g\n",low,lf->rect.origin.y,lf->rect.size.height,NSMinY(bounds));
|
1999-07-15 17:32:38 +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
|
|
|
// printf(" empty (bounds below text)\n");
|
|
|
|
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) &&
|
|
|
|
NSMaxX(lf[1].rect) < NSMinX(bounds))
|
|
|
|
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. */
|
|
|
|
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
|
|
|
// printf("low=%i (%i) (%g+%g) %g\n",low,tc->num_linefrags,lf->rect.origin.y,lf->rect.size.height,NSMaxY(bounds));
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (NSMinY(lf->rect) > NSMaxY(bounds))
|
|
|
|
{
|
|
|
|
// printf(" empty (bounds above text)\n");
|
|
|
|
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) &&
|
|
|
|
NSMinX(lf[1].rect) > NSMaxX(bounds))
|
|
|
|
i--, lf--;
|
|
|
|
// printf("i=%i\n",i);
|
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
|
|
|
// printf(" empty (bound between lines?)\n");
|
|
|
|
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;
|
|
|
|
/* printf(" range= %i - %i |%@|\n",
|
|
|
|
range.location,range.length,
|
|
|
|
[[_textStorage string] substringWithRange: range]);*/
|
|
|
|
return range;
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
- (NSRange) glyphRangeForBoundingRectWithoutAdditionalLayout: (NSRect)bounds
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
1999-07-15 17:32:38 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
/* OPT: handle faster? how? */
|
|
|
|
return [self glyphRangeForBoundingRect: bounds
|
|
|
|
inTextContainer: container];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2000-12-21 17:29:51 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
- (unsigned int) glyphIndexForPoint: (NSPoint)aPoint
|
|
|
|
inTextContainer: (NSTextContainer *)aTextContainer
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
2003-01-26 19:21:40 +00:00
|
|
|
- (unsigned int) glyphIndexForPoint: (NSPoint)point
|
|
|
|
inTextContainer: (NSTextContainer *)container
|
|
|
|
fractionOfDistanceThroughGlyph: (float *)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;
|
|
|
|
float 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__);
|
|
|
|
return -1;
|
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-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
|
|
|
{
|
|
|
|
if (NSPointInRect(point, lf->rect))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Point is between two lines. */
|
|
|
|
if (NSMinY(lf->rect) > point.y)
|
|
|
|
{
|
|
|
|
if (lf->pos > 0)
|
|
|
|
{
|
|
|
|
*partialFraction = 1.0;
|
|
|
|
return lf->pos - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*partialFraction = 0.0;
|
|
|
|
return lf->pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Point is below 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;
|
|
|
|
return tc->pos + tc->length - 1; /* TODO: this should return the correct thing even if the container is empty */
|
|
|
|
}
|
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
|
|
|
/* Before the first glyph on the line. */
|
|
|
|
if (!i)
|
2002-02-24 07:39:18 +00:00
|
|
|
{
|
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-01-26 19:21:40 +00:00
|
|
|
float cur, prev, next;
|
|
|
|
glyph_run_t *r;
|
|
|
|
unsigned int glyph_pos, char_pos, last_visible;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (i < lf->num_points)
|
|
|
|
next = lp->p.x;
|
|
|
|
else
|
|
|
|
next = NSMinX(lf->rect);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
lp--;
|
|
|
|
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;
|
2003-01-26 19:21:40 +00:00
|
|
|
for (i = lp->pos - glyph_pos; i + glyph_pos < lp->pos + lp->length; )
|
|
|
|
{
|
|
|
|
if (r->glyphs[i].isNotShown || r->glyphs[i].g == NSControlGlyph ||
|
|
|
|
!r->glyphs[i].g)
|
2002-02-24 07:39:18 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
GLYPH_STEP_FORWARD(r, i, glyph_pos, char_pos)
|
|
|
|
continue;
|
2002-02-24 07:39:18 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
last_visible = i + glyph_pos;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
cur = prev + [r->font advancementForGlyph: r->glyphs[i].g].width;
|
|
|
|
if (i + glyph_pos + 1 == lp->pos + lp->length && next > cur)
|
|
|
|
cur = next;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (cur >= point.x)
|
2001-01-22 18:13:43 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
*partialFraction = (point.x - prev) / (cur - prev);
|
|
|
|
if (*partialFraction < 0)
|
|
|
|
*partialFraction = 0;
|
|
|
|
return i + glyph_pos;
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
prev = cur;
|
|
|
|
GLYPH_STEP_FORWARD(r, i, glyph_pos, char_pos)
|
|
|
|
}
|
|
|
|
*partialFraction = 1;
|
|
|
|
return last_visible;
|
2001-01-22 18:13:43 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
container that containts its first glyph, the range is silently clamped.
|
|
|
|
My thought with this is that the requested glyphs might not fit in the
|
|
|
|
text container (if it's the last text container, or there's only one).
|
|
|
|
In that case, it isn't really the caller's fault, and drawing as much as
|
|
|
|
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
|
|
|
|
container */
|
|
|
|
|
|
|
|
-(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;
|
|
|
|
int count;
|
|
|
|
NSColor *color, *last_color;
|
|
|
|
|
|
|
|
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;
|
|
|
|
last_color = nil;
|
2003-01-26 23:02:55 +00:00
|
|
|
first_char_pos = char_pos;
|
2003-01-26 19:21:40 +00:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
rects = [self rectArrayForGlyphRange:
|
|
|
|
NSMakeRange(glyph_pos + i, glyph_run->head.glyph_length - i)
|
|
|
|
withinSelectedGlyphRange: NSMakeRange(NSNotFound, 0)
|
|
|
|
inTextContainer: textContainer
|
|
|
|
rectCount: &count];
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
{
|
|
|
|
color = [_textStorage attribute: NSBackgroundColorAttributeName
|
|
|
|
atIndex: char_pos
|
|
|
|
effectiveRange: NULL];
|
|
|
|
if (color)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
if (i + glyph_pos >= range.location + range.length)
|
|
|
|
break;
|
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;
|
|
|
|
|
|
|
|
if (_selected_range.location >= char_pos ||
|
|
|
|
_selected_range.location + _selected_range.length <= first_char_pos)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The selection (might) intersect our glyph range. */
|
|
|
|
{
|
|
|
|
NSRange r = [self glyphRangeForCharacterRange: _selected_range
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
NSRange sel = r;
|
|
|
|
|
|
|
|
if (r.location < range.location)
|
|
|
|
{
|
|
|
|
r.length -= range.location - r.location;
|
|
|
|
r.location = range.location;
|
|
|
|
}
|
|
|
|
if (r.location + r.length > range.location + range.length)
|
|
|
|
{
|
|
|
|
r.length = range.location + range.length - r.location;
|
|
|
|
}
|
|
|
|
if (r.length <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* TODO: use the text view's selected text attributes */
|
|
|
|
color = [NSColor selectedTextBackgroundColor];
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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-31 21:08:24 +00:00
|
|
|
linefrag_attachment_t *la;
|
|
|
|
int la_i;
|
|
|
|
|
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;
|
|
|
|
NSColor *color, *new_color;
|
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-01-26 19:21:40 +00:00
|
|
|
#define GBUF_SIZE 16 /* TODO: tweak */
|
|
|
|
NSGlyph gbuf[GBUF_SIZE];
|
|
|
|
int gbuf_len;
|
|
|
|
NSPoint gbuf_point;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-31 21:08:24 +00:00
|
|
|
NSView *controlView = nil;
|
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
if (!range.length)
|
|
|
|
return;
|
|
|
|
[self _doLayoutToGlyph: range.location + range.length - 1];
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
|
|
|
|
if (lf->pos + lf->length > range.location)
|
|
|
|
break;
|
|
|
|
if (i == tc->num_linefrags)
|
2001-01-19 23:22:16 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
2001-01-19 23:22:16 +00:00
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-31 21:08:24 +00:00
|
|
|
la = lf->attachments;
|
|
|
|
la_i = 0;
|
|
|
|
|
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);
|
|
|
|
glyph = glyph_run->glyphs + lp->pos - glyph_pos;
|
|
|
|
attributes = [_textStorage attributesAtIndex: char_pos
|
|
|
|
effectiveRange: NULL];
|
|
|
|
color = [attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
if (color)
|
|
|
|
[color set];
|
|
|
|
else
|
2001-01-15 21:48:18 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
DPSsetgray(ctxt, 0.0);
|
|
|
|
DPSsetalpha(ctxt, 1.0);
|
2001-01-15 21:48:18 +00:00
|
|
|
}
|
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
|
|
|
{
|
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);
|
|
|
|
GSShowGlyphs(ctxt, gbuf, gbuf_len);
|
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
lp++;
|
|
|
|
if (j == lf->num_points)
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
lf++;
|
|
|
|
j = 0;
|
|
|
|
lp = lf->points;
|
2003-01-31 21:08:24 +00:00
|
|
|
la = lf->attachments;
|
|
|
|
la_i = 0;
|
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;
|
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];
|
|
|
|
new_color = [attributes valueForKey: NSForegroundColorAttributeName];
|
|
|
|
glyph = glyph_run->glyphs;
|
|
|
|
if (glyph_run->font != f || new_color != color)
|
|
|
|
{
|
|
|
|
if (gbuf_len)
|
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
|
|
|
GSShowGlyphs(ctxt, gbuf, gbuf_len);
|
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
}
|
|
|
|
if (color != new_color)
|
|
|
|
{
|
|
|
|
color = new_color;
|
|
|
|
if (color)
|
|
|
|
[color set];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPSsetgray(ctxt, 0.0);
|
|
|
|
DPSsetalpha(ctxt, 1.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (f != glyph_run->font)
|
|
|
|
{
|
|
|
|
f = glyph_run->font;
|
|
|
|
[f set];
|
|
|
|
}
|
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
/* Silently ignore if we don't have any size information for
|
|
|
|
it. */
|
|
|
|
if (g >= range.location && la)
|
|
|
|
{
|
|
|
|
unsigned int char_index =
|
|
|
|
[self characterRangeForGlyphRange: NSMakeRange(g,1)
|
|
|
|
actualGlyphRange: NULL].location;
|
|
|
|
NSObject<NSTextAttachmentCell> *cell = [[_textStorage attribute: NSAttachmentAttributeName
|
|
|
|
atIndex: char_index
|
|
|
|
effectiveRange: NULL] attachmentCell];
|
|
|
|
NSRect cellFrame;
|
|
|
|
|
|
|
|
if (!controlView)
|
|
|
|
controlView = [tc->textContainer textView];
|
|
|
|
|
|
|
|
while (la->pos != g && la_i < lf->num_attachments)
|
|
|
|
{
|
|
|
|
la++;
|
|
|
|
la_i++;
|
|
|
|
}
|
|
|
|
if (la_i >= lf->num_attachments)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cellFrame.origin = p;
|
|
|
|
cellFrame.size = la->size;
|
|
|
|
cellFrame.origin.y -= cellFrame.size.height;
|
|
|
|
|
|
|
|
/* Drawing the cell might mess up our state. */
|
|
|
|
/* TODO:
|
|
|
|
optimize this. probably cheaper to not save and
|
|
|
|
explicitly reset just the stuff we actually use, or to
|
|
|
|
collect attachments and draw them in bunches of eg. 4
|
|
|
|
|
|
|
|
should they really be drawn in our coordinate system?
|
|
|
|
*/
|
|
|
|
[cell drawWithFrame: cellFrame
|
|
|
|
inView: controlView
|
|
|
|
characterIndex: char_index
|
|
|
|
layoutManager: self];
|
|
|
|
[f set];
|
|
|
|
if (color)
|
|
|
|
[color set];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPSsetgray(ctxt, 0.0);
|
|
|
|
DPSsetalpha(ctxt, 1.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2003-01-26 19:21:40 +00:00
|
|
|
if (g >= range.location)
|
|
|
|
{
|
|
|
|
if (!gbuf_len)
|
|
|
|
{
|
|
|
|
gbuf[0] = glyph->g;
|
|
|
|
gbuf_point = p;
|
|
|
|
gbuf_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (gbuf_len == GBUF_SIZE)
|
|
|
|
{
|
|
|
|
DPSmoveto(ctxt, gbuf_point.x, gbuf_point.y);
|
|
|
|
GSShowGlyphs(ctxt, gbuf, GBUF_SIZE);
|
|
|
|
DPSnewpath(ctxt);
|
|
|
|
gbuf_len = 0;
|
|
|
|
gbuf_point = p;
|
|
|
|
}
|
|
|
|
gbuf[gbuf_len++] = glyph->g;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.x += [f advancementForGlyph: glyph->g].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);
|
|
|
|
GSShowGlyphs(ctxt, gbuf, gbuf_len);
|
|
|
|
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
|
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
|
|
|
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
@implementation NSLayoutManager
|
1999-07-15 17:32:38 +00:00
|
|
|
|
2003-01-26 19:21:40 +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-01-26 19:21:40 +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
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
DESTROY(_typingAttributes);
|
|
|
|
[super dealloc];
|
1999-07-15 17:32:38 +00:00
|
|
|
}
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* TODO */
|
|
|
|
-(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-01-27 15:17:13 +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;
|
|
|
|
NSView *tv;
|
|
|
|
NSView *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
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
tv = (NSView *)[textcontainers[i].textContainer textView];
|
|
|
|
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
|
|
|
|
paragraphStyle: (NSParagraphStyle *)style
|
|
|
|
ruler: (NSRulerView *)ruler
|
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
|
|
|
}
|
|
|
|
|
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-01-26 19:21:40 +00:00
|
|
|
if (!textcontainers[i].started)
|
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
|
|
|
|
{
|
|
|
|
if (layout_char < aRange.location)
|
|
|
|
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
|
|
|
|
|
|
|
-(void) textStorage: (NSTextStorage *)aTextStorage
|
|
|
|
edited: (unsigned int)mask
|
|
|
|
range: (NSRange)range
|
|
|
|
changeInLength: (int)lengthChange
|
|
|
|
invalidatedRange: (NSRange)invalidatedRange
|
1999-08-19 23:18:25 +00:00
|
|
|
{
|
2003-01-26 19:21:40 +00:00
|
|
|
unsigned int g;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
[super textStorage: aTextStorage
|
|
|
|
edited: mask
|
|
|
|
range: range
|
|
|
|
changeInLength: lengthChange
|
|
|
|
invalidatedRange: invalidatedRange];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
2003-01-28 21:57:22 +00:00
|
|
|
if ((mask & NSTextStorageEditedCharacters) && lengthChange)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
if (_selected_range.location >= range.location + range.length - lengthChange)
|
|
|
|
{
|
|
|
|
_selected_range.location += lengthChange;
|
|
|
|
}
|
|
|
|
else if (_selected_range.location + _selected_range.length >= range.location)
|
|
|
|
{
|
2003-01-29 15:05:12 +00:00
|
|
|
if (-lengthChange > _selected_range.length)
|
|
|
|
{
|
|
|
|
_selected_range.length = 0;
|
|
|
|
_selected_range.location = range.location;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_selected_range.length += lengthChange;
|
|
|
|
}
|
2003-01-28 21:57:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-26 19:21:40 +00:00
|
|
|
/* Invalidate display from the first glyph not laid out (which will
|
|
|
|
generally be the first glyph to have been invalidated). */
|
|
|
|
g = layout_glyph;
|
1999-08-19 23:18:25 +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-01-26 19:21:40 +00:00
|
|
|
if (textcontainers[i].complete &&
|
|
|
|
g < textcontainers[i].pos + textcontainers[i].length)
|
|
|
|
continue;
|
|
|
|
|
2003-01-26 20:18:04 +00:00
|
|
|
[[textcontainers[i].textContainer textView] sizeToFit]; /* TODO? */
|
2003-01-26 19:21:40 +00:00
|
|
|
[[textcontainers[i].textContainer textView] setNeedsDisplay: YES];
|
1999-08-19 23:18:25 +00:00
|
|
|
}
|
2003-01-29 15:05:12 +00:00
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
}
|
2000-08-27 22:32:29 +00:00
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
@end
|
2003-01-26 19:21:40 +00:00
|
|
|
|