A load of changes.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@4724 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
michael 1999-08-19 23:18:25 +00:00
parent 6d69b992d2
commit 6bceaa0b50
12 changed files with 1035 additions and 49 deletions

View file

@ -7,6 +7,8 @@
Author: Jonathan Gapen <jagapen@smithlab.chem.wisc.edu>
Date: July 1999
Author: Michael Hanni <mhanni@sprintmail.com>
Date: August 1999
This file is part of the GNUstep GUI Library.
@ -27,14 +29,271 @@
*/
#include <AppKit/NSLayoutManager.h>
// _GSRunSearchKey is an internal class which serves as the foundation for
// all our searching. This may not be an elegant way to go about this, so
// if someone wants to optimize this out, please do.
@interface _GSRunSearchKey : NSObject
{
@public
NSRange glyphRange;
}
@end
@implementation _GSRunSearchKey
- (id)init
{
return [super init];
}
- (void) dealloc
{
[super dealloc];
}
@end
@interface GSGlyphLocation : _GSRunSearchKey
{
@public
NSPoint point;
}
@end
@implementation GSGlyphLocation
- (id)init
{
return [super init];
}
- (void) dealloc
{
[super dealloc];
}
@end
@interface GSLineLayoutInfo : _GSRunSearchKey
{
@public
NSRect lineFragmentRect;
NSRect usedRect;
}
@end
@implementation GSLineLayoutInfo
- (id)init
{
return [super init];
}
- (void) dealloc
{
[super dealloc];
}
@end
@interface GSTextContainerLayoutInfo : _GSRunSearchKey
{
@public
NSTextContainer *textContainer;
NSString *testString;
}
@end
@implementation GSTextContainerLayoutInfo
- (id)init
{
return [super init];
}
- (void) dealloc
{
[super dealloc];
}
@end
#define GSI_ARRAY_TYPES GSUNION_OBJ
#ifdef GSIArray
#undef GSIArray
#endif
#include <base/GSIArray.h>
static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
{
if (((_GSRunSearchKey *)(i0.obj))->glyphRange.location
< ((_GSRunSearchKey *)(i1.obj))->glyphRange.location)
return NSOrderedAscending;
else if (((_GSRunSearchKey *)(i0.obj))->glyphRange.location
>= NSMaxRange(((_GSRunSearchKey *)(i1.obj))->glyphRange))
return NSOrderedDescending;
else
return NSOrderedSame;
}
@interface GSRunStorage : NSObject
{
unsigned int _count;
void *_runs;
}
- (void)insertObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(unsigned)theIndex;
- (id)objectAtIndex:(unsigned)theIndex;
- (unsigned)indexOfObject:(id)anObject;
- (unsigned)indexOfObjectContainingLocation:(unsigned)aLocation;
- (id)objectContainingLocation:(unsigned)aLocation;
- (int)count;
@end
@implementation GSRunStorage
- (id)init
{
NSZone *z;
[super init];
z = [self zone];
_runs = NSZoneMalloc(z, sizeof(GSIArray_t));
GSIArrayInitWithZoneAndCapacity((GSIArray)_runs, z, 8);
return self;
}
- (void)insertObject:(id)anObject
{
_GSRunSearchKey *aKey = [_GSRunSearchKey new];
_GSRunSearchKey *aObject = (_GSRunSearchKey *)anObject;
int position;
aKey->glyphRange.location = aObject->glyphRange.location;
position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort);
NSLog(@"key: %d aObject: %d position: %d", aKey->glyphRange.location,
aObject->glyphRange.location, position);
if (position > 0)
{
_GSRunSearchKey *anKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj;
if (anKey->glyphRange.location == aObject->glyphRange.location)
{
// GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort);
NSLog(@"=========> duplicated item.");
GSIArraySetItemAtIndex(_runs, (GSIArrayItem)anObject, position-1);
}
else
{
NSLog(@"=========> not duplicated item.");
GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, position);
}
}
else if (position == 0)
{
NSLog(@"=========> first item (zero index).");
GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, position);
// GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort);
// [self insertObject:anObject atIndex:position];
}
else
NSLog(@"dead. VERY DEAD DEAD DEAD DEAD.");
NSLog(@"==> %d item(s)", GSIArrayCount(_runs));
}
- (void)insertObject:(id)anObject
atIndex:(unsigned)theIndex
{
NSLog(@"insertObject:atIndex: called. %d item(s)", GSIArrayCount(_runs));
GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort);
// GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, theIndex);
NSLog(@"insertObject:atIndex: ended. %d item(s)", GSIArrayCount(_runs));
}
- (void)removeObjectAtIndex:(int)theIndex
{
GSIArrayRemoveItemAtIndex(_runs, theIndex);
}
- (id)objectAtIndex:(unsigned)theIndex
{
return GSIArrayItemAtIndex(_runs, (unsigned)theIndex).obj;
}
- (unsigned)indexOfObject:(id)anObject
{
return NSNotFound;
}
- (unsigned)indexOfObjectContainingLocation:(unsigned)aLocation
{
_GSRunSearchKey *aKey = [_GSRunSearchKey new];
int position;
aKey->glyphRange.location = aLocation;
position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort);
if (position >= 0 && position - 1 >= 0)
{
aKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj;
if (NSLocationInRange(aLocation, aKey->glyphRange))
{
return (position - 1);
}
}
return -1;
}
- (id)objectContainingLocation:(unsigned)aLocation
{
_GSRunSearchKey *aKey = [_GSRunSearchKey new];
int position;
aKey->glyphRange.location = aLocation;
position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort);
if (position >= 0 && position - 1 >= 0)
{
aKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj;
if (NSLocationInRange(aLocation, aKey->glyphRange))
{
return aKey;
}
}
return nil;
}
- (id)lastObject
{
return GSIArrayItemAtIndex(_runs, GSIArrayCount(_runs) - 1).obj;
}
- (int)count
{
return GSIArrayCount(_runs);
}
@end
@implementation NSLayoutManager
- (id) init
{
[super init];
_backgroundLayout = YES;
_delegate = nil;
_textContainers = [[NSMutableArray alloc] initWithCapacity: 2];
containerRuns = [GSRunStorage new];
fragmentRuns = [GSRunStorage new];
locationRuns = [GSRunStorage new];
return self;
}
@ -135,6 +394,23 @@
{
NSLog(@"NSLayoutManager was just notified that a change in the text
storage occured.");
/*
if (mask == NSTextStorageEditedCharacters)
{
aLayoutHole = [[NSLayoutHole alloc]
initWithCharacterRange:invalidatedRange isSoft:NO];
}
else if (mask == NSTextStorageEditedAttributes)
{
}
else if (mask == NSTextStorageEditedCharacters | NSTextStorageEditedAttributes)
{
}
*/
// invalidation should occure here.
[self _doLayout];
}
//
@ -220,6 +496,9 @@ storage occured.");
//
// Setting glyph attributes
//
// Each NSGlyph has an attribute field, yes?
- (void)setIntAttribute: (int)attribute
value: (int)anInt
forGlyphAtIndex: (unsigned)glyphIndex
@ -238,26 +517,55 @@ storage occured.");
- (void)setTextContainer: (NSTextContainer *)aTextContainer
forGlyphRange: (NSRange)glyphRange
{
GSTextContainerLayoutInfo *theLine = [GSTextContainerLayoutInfo new];
theLine->glyphRange = glyphRange;
ASSIGN(theLine->textContainer, aTextContainer);
[containerRuns insertObject:theLine];
}
- (NSRange)glyphRangeForTextContainer: (NSTextContainer *)aTextContainer
{
NSSize tcSize = [aTextContainer containerSize];
int i;
NSLog(@"glyphRangeForTextContainer: called. There are %d
textContainer(s) in containerRuns.", [containerRuns count]);
return NSMakeRange(0, 0);
for (i=0;i<[containerRuns count];i++)
{
GSTextContainerLayoutInfo *aNewLine = [containerRuns objectAtIndex:i];
NSLog(@"glyphRangeForTextContainer: (%d, %d)",
aNewLine->glyphRange.location,
aNewLine->glyphRange.length);
if ([aNewLine->textContainer isEqual:aTextContainer])
{
NSLog(@"glyphRangeForWantedTextContainer: (%d, %d)",
aNewLine->glyphRange.location,
aNewLine->glyphRange.length);
return aNewLine->glyphRange;
}
}
return NSMakeRange(NSNotFound, 0);
}
- (NSTextContainer *)textContainerForGlyphAtIndex: (unsigned)glyphIndex
effectiveRange: (NSRange *)effectiveRange
{
return nil;
}
GSTextContainerLayoutInfo *theLine = [containerRuns objectContainingLocation:glyphIndex];
- (NSRect)usedRectForTextContainer: (NSTextContainer *)aTextContainer
{
return NSZeroRect;
if(theLine)
{
(NSRange *)effectiveRange = &theLine->glyphRange;
return theLine->textContainer;
}
(NSRange *)effectiveRange = NULL;
return nil;
}
//
@ -267,17 +575,42 @@ storage occured.");
forGlyphRange: (NSRange)glyphRange
usedRect: (NSRect)usedRect
{
GSLineLayoutInfo *aNewLine = [GSLineLayoutInfo new];
aNewLine->glyphRange = glyphRange;
aNewLine->lineFragmentRect = fragmentRect;
aNewLine->usedRect = usedRect;
[fragmentRuns insertObject:aNewLine];
}
- (NSRect)lineFragmentRectForGlyphAtIndex: (unsigned)glyphIndex
effectiveRange: (NSRange *)lineFragmentRange
{
GSLineLayoutInfo *theLine = [fragmentRuns objectContainingLocation:glyphIndex];
if (theLine)
{
(NSRange *)lineFragmentRange = &theLine->glyphRange;
return theLine->lineFragmentRect;
}
(NSRange *)lineFragmentRange = NULL;
return NSZeroRect;
}
- (NSRect)lineFragmentUsedRectForGlyphAtIndex: (unsigned)glyphIndex
effectiveRange: (NSRange *)lineFragmentRange
{
GSLineLayoutInfo *theLine = [fragmentRuns objectContainingLocation:glyphIndex];
if (theLine)
{
(NSRange *)lineFragmentRange = &theLine->glyphRange;
return theLine->usedRect;
}
(NSRange *)lineFragmentRange = NULL;
return NSZeroRect;
}
@ -315,9 +648,16 @@ storage occured.");
//
// Layout of glyphs
//
- (void)setLocation: (NSPoint)aPoint
forStartOfGlyphRange: (NSRange)glyphRange
{
GSGlyphLocation *aNewLine = [GSGlyphLocation new];
aNewLine->glyphRange = glyphRange;
aNewLine->point = aPoint;
[locationRuns insertObject:aNewLine];
}
- (NSPoint)locationForGlyphAtIndex: (unsigned)glyphIndex
@ -327,7 +667,14 @@ storage occured.");
- (NSRange)rangeOfNominallySpacedGlyphsContainingIndex: (unsigned)glyphIndex
{
return NSMakeRange(0, 0);
GSLineLayoutInfo *theLine = [locationRuns objectContainingLocation:glyphIndex];
if (theLine)
{
return theLine->glyphRange;
}
return NSMakeRange(NSNotFound, 0);
}
- (NSRect *)rectArrayForCharacterRange: (NSRange)charRange
@ -335,7 +682,43 @@ storage occured.");
inTextContainer: (NSTextContainer *)aTextContainer
rectCount: (unsigned *)rectCount
{
return NULL;
/*
GSLineLayoutInfo *theLine = [GSLineLayoutInfo new];
int position, lastPosition;
int i, j = 0;
theLine->glyphRange.location = charRange.location;
position = GSIArrayInsertionPosition(lineFragments, (GSIArrayItem)theLine, aSort);
if (position < 0)
{
return NULL;
}
theLine->glyphRange.location = charRange.location + charRange.length;
lastPosition = GSIArrayInsertionPosition(lineFragments, (GSIArrayItem)theLine, aSort);
if (lastPosition > 0)
{
_cachedRectArray = NSZoneRealloc([self zone], _cachedRectArray,
(lastPosition - position) * sizeof(NSRect));
_cachedRectArrayCapacity = lastPosition - position;
for (i = position - 1; i < lastPosition - 1; i++)
{
GSLineLayoutInfo *aLine = GSIArrayItemAtIndex(lineFragments, i).obj;
_cachedRectArray[j] = aLine->lineFragmentRect;
j++;
}
}
(*rectCount) = (position - 1 + lastPosition - 1);
return _cachedRectArray;
*/
}
- (NSRect *)rectArrayForGlyphRange: (NSRange)glyphRange
@ -343,12 +726,41 @@ storage occured.");
inTextContainer: (NSTextContainer *)aTextContainer
rectCount: (unsigned *)rectCount
{
return NULL;
return _cachedRectArray;
}
- (NSRect)boundingRectForGlyphRange: (NSRange)glyphRange
inTextContainer: (NSTextContainer *)aTextContainer
{
/* Returns a single bounding rectangle enclosing all glyphs and other
marks drawn in aTextContainer for glyphRange, including glyphs that draw
outside their line fragment rectangles and text attributes such as
underlining. This method is useful for determining the area that needs to
be redrawn when a range of glyphs changes. */
/*
unsigned rectCount;
NSRect *rects = [self rectArrayForCharacterRange: [self glyphRangeForTextContainer:aTextContainer]
withinSelectedCharacterRange: NSMakeRange(0,0)
inTextContainer: aTextContainer
rectCount: &rectCount];
// NSPoint aOrigin = [aTextContainer originPoint];
NSRect rRect = NSZeroRect;
int i;
for (i=0;i<rectCount;i++)
{
NSRect aRect = rects[i];
if (aRect.origin.y == rRect.size.height)
rRect.size.height += aRect.size.width;
if (rRect.size.width == aRect.origin.x)
rRect.size.width += aRect.size.width;
}
return rRect;
*/
return NSZeroRect;
}
@ -425,6 +837,11 @@ fractionOfDistanceThroughGlyph: (float *)partialFraction
{
}
- (unsigned int)firstUnlaidCharacterIndex
{
return 0;
}
//
// Using screen fonts
//
@ -490,6 +907,54 @@ fractionOfDistanceThroughGlyph: (float *)partialFraction
- (void)drawGlyphsForGlyphRange: (NSRange)glyphRange
atPoint: (NSPoint)containerOrigin
{
int firstPosition, lastPosition, i;
for (i=0;i<[fragmentRuns count];i++)
{
GSLineLayoutInfo *info = [fragmentRuns objectAtIndex:i];
/*
NSLog(@"i: %d glyphRange: (%d, %d) lineFragmentRect: (%f, %f) (%f, %f)",
i,
info->glyphRange.location,
info->glyphRange.length,
info->lineFragmentRect.origin.x,
info->lineFragmentRect.origin.y,
info->lineFragmentRect.size.width,
info->lineFragmentRect.size.height);
*/
}
firstPosition = [fragmentRuns indexOfObjectContainingLocation:glyphRange.location];
lastPosition = [fragmentRuns
indexOfObjectContainingLocation:(glyphRange.location+glyphRange.length-2)];
NSLog(@"glyphRange: (%d, %d) position1: %d position2: %d",
glyphRange.location, glyphRange.length, firstPosition, lastPosition);
if (firstPosition >= 0)
{
if (lastPosition == -1)
{
lastPosition = [fragmentRuns count]; // FIXME
NSLog(@"fixed lastPosition: %d", lastPosition);
}
for (i = firstPosition; i < lastPosition; i++)
{
GSLineLayoutInfo *aLine = [fragmentRuns objectAtIndex:i];
/*
NSLog(@"drawRange: (%d, %d) inRect (%f, %f) (%f, %f)",
aLine->glyphRange.location,
aLine->glyphRange.length,
aLine->lineFragmentRect.origin.x,
aLine->lineFragmentRect.origin.y,
aLine->lineFragmentRect.size.width,
aLine->lineFragmentRect.size.height);
*/
[_textStorage drawRange:aLine->glyphRange inRect:aLine->lineFragmentRect];
}
}
}
- (void)drawUnderlineForGlyphRange: (NSRange)glyphRange
@ -523,3 +988,238 @@ fractionOfDistanceThroughGlyph: (float *)partialFraction
}
@end /* NSLayoutManager */
/* Thew methods laid out here are not correct, however the code they
contain for the most part is. Therefore, my country and a handsome gift of
Ghiradelli chocolate to he who puts all the pieces together :) */
@interface _GNUTextScanner:NSObject
{ NSString *string;
NSCharacterSet *set,*iSet;
unsigned stringLength;
NSRange activeRange;
}
+(_GNUTextScanner*) scannerWithString:(NSString*) aStr set:(NSCharacterSet*) aSet invertedSet:(NSCharacterSet*) anInvSet;
-(void) setString:(NSString*) aString set:(NSCharacterSet*) aSet invertedSet:(NSCharacterSet*) anInvSet;
-(NSRange) _scanCharactersInverted:(BOOL) inverted;
-(NSRange) scanSetCharacters;
-(NSRange) scanNonSetCharacters;
-(BOOL) isAtEnd;
-(unsigned) scanLocation;
-(void) setScanLocation:(unsigned) aLoc;
@end
@implementation NSLayoutManager (Private)
- (int)_rebuildLayoutForTextContainer:(NSTextContainer *)aContainer
startingAtGlyphIndex:(int)glyphIndex
{
NSSize cSize = [aContainer containerSize];
float i = 0.0;
NSMutableArray *lines = [NSMutableArray new];
int indexToAdd;
_GNUTextScanner *lineScanner;
_GNUTextScanner *paragraphScanner;
BOOL lastLineForContainerReached = NO;
NSString *aString;
int previousScanLocation;
int endScanLocation;
int startIndex;
NSRect firstProposedRect;
NSRect secondProposedRect;
NSFont *default_font = [NSFont systemFontOfSize: 12.0];
int widthOfString;
NSSize rSize;
NSCharacterSet *selectionParagraphGranularitySet = [NSCharacterSet characterSetWithCharactersInString:@"\n"];
NSCharacterSet *selectionWordGranularitySet = [NSCharacterSet characterSetWithCharactersInString:@" "];
NSCharacterSet *invSelectionWordGranularitySet = [selectionWordGranularitySet invertedSet];
NSCharacterSet *invSelectionParagraphGranularitySet = [selectionParagraphGranularitySet invertedSet];
NSRange paragraphRange;
NSRange leadingSpacesRange;
NSRange currentStringRange;
NSRange trailingSpacesRange;
NSRange leadingNlRange;
NSSize lSize;
float lineWidth = 0.0;
float ourLines = 0.0;
NSLog(@"rebuilding Layout at index: %d.\n", glyphIndex);
// 1.) figure out how many glyphs we can fit in our container by
// breaking up glyphs from the first unlaid out glyph and breaking it
// into lines.
//
// 2.)
// a.) set the range for the container
// b.) for each line in step 1 we need to set a lineFragmentRect and
// an origin point.
// Here we go at part 1.
startIndex = glyphIndex;
// lineScanner = [NSScanner scannerWithString:[_textStorage string]];
paragraphScanner = [_GNUTextScanner scannerWithString:[_textStorage string]
set:selectionParagraphGranularitySet invertedSet:invSelectionParagraphGranularitySet];
[paragraphScanner setScanLocation:startIndex];
NSLog(@"length of textStorage: %d", [[_textStorage string] length]);
// NSLog(@"buffer: %@", [_textStorage string]);
// This scanner eats one word at a time, we should have it imbeded in
// another scanner that snacks on paragraphs (i.e. lines that end with
// \n). Look in NSText.
while (![paragraphScanner isAtEnd])
{
// leadingNlRange=[paragraphScanner scanSetCharacters];
paragraphRange = [paragraphScanner scanNonSetCharacters];
leadingNlRange=[paragraphScanner scanSetCharacters];
if (leadingNlRange.length)
currentStringRange = NSUnionRange (leadingNlRange,paragraphRange);
NSLog(@"paragraphRange: (%d, %d)", paragraphRange.location, paragraphRange.length);
NSLog(@"======> begin paragraph");
lineScanner = [_GNUTextScanner scannerWithString:[[_textStorage string] substringWithRange:paragraphRange]
set:selectionWordGranularitySet invertedSet:invSelectionWordGranularitySet];
while(![lineScanner isAtEnd])
{
previousScanLocation = [lineScanner scanLocation];
// snack next word
leadingSpacesRange = [lineScanner scanSetCharacters]; // leading spaces: only first time
currentStringRange = [lineScanner scanNonSetCharacters];
trailingSpacesRange= [lineScanner scanSetCharacters];
if (leadingSpacesRange.length)
currentStringRange = NSUnionRange (leadingSpacesRange,currentStringRange);
if (trailingSpacesRange.length)
currentStringRange = NSUnionRange (trailingSpacesRange,currentStringRange);
lSize = [_textStorage sizeRange:currentStringRange];
if (lineWidth + lSize.width < cSize.width)
{
if ([lineScanner isAtEnd])
{
NSLog(@"we are at end before finishing a line: %d.\n", [lineScanner scanLocation]);
[lines addObject:[NSNumber numberWithInt:(int)[lineScanner scanLocation] + paragraphRange.location]];
}
lineWidth += lSize.width;
NSLog(@"lineWidth: %f", lineWidth);
}
else
{
if (ourLines > cSize.height)
{
lastLineForContainerReached = YES;
break;
}
[lineScanner setScanLocation:previousScanLocation];
indexToAdd = previousScanLocation + paragraphRange.location;
ourLines += 14.0;
lineWidth = 0;
NSLog(@"indexToAdd: %d\tourLines: %f", indexToAdd, ourLines);
[lines addObject:[NSNumber numberWithInt:indexToAdd]];
}
}
if (lastLineForContainerReached)
break;
}
endScanLocation = [lineScanner scanLocation] + paragraphRange.location;
NSLog(@"endScanLocation: %d", endScanLocation);
// set this container for that glyphrange
[self setTextContainer:aContainer
forGlyphRange:NSMakeRange(startIndex, endScanLocation - startIndex)];
NSLog(@"ok, move on to step 2.");
// step 2. break the lines up and assign rects to them.
for (i=0;i<[lines count];i++)
{
NSRect aRect, *bRect;
float padding = [aContainer lineFragmentPadding];
NSRange ourRange;
NSLog(@"\t\t===> %d", [[lines objectAtIndex:i] intValue]);
if (i == 0)
{
ourRange = NSMakeRange (startIndex,
[[lines objectAtIndex:i] intValue] - startIndex);
}
else
{
ourRange = NSMakeRange ([[lines objectAtIndex:i-1] intValue],
[[lines objectAtIndex:i] intValue] - [[lines objectAtIndex:i-1]
intValue]);
}
firstProposedRect = NSMakeRect (0, i * 14, cSize.width, 14);
// ask our textContainer to fix our lineFragment.
secondProposedRect = [aContainer lineFragmentRectForProposedRect:firstProposedRect
sweepDirection: NULL
movementDirection: NULL
remainingRect: &bRect];
// set the line fragmentRect for this range.
[self setLineFragmentRect: secondProposedRect
forGlyphRange: ourRange
usedRect: aRect];
// set the location for this string to be 'show'ed.
[self setLocation:NSMakePoint(secondProposedRect.origin.x + padding,
secondProposedRect.origin.y + padding)
forStartOfGlyphRange: ourRange];
}
// bloody hack.
// if (moreText)
// [delegate layoutManager:self
// didCompleteLayoutForTextContainer:[textContainers objectAtIndex:i]
// atEnd:NO];
// else
// [delegate layoutManager:self
// didCompleteLayoutForTextContainer:[textContainers objectAtIndex:i]
// atEnd:YES];
[lines release];
return endScanLocation;
}
- (void)_doLayout
{
int i;
BOOL moreText;
int gIndex = 0;
NSLog(@"doLayout called.\n");
for (i=0;i<[_textContainers count];i++)
{
gIndex = [self _rebuildLayoutForTextContainer:[_textContainers objectAtIndex:i]
startingAtGlyphIndex:gIndex];
}
}
@end