mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-24 06:28:54 +00:00
Layout manager glyph implementation ... initial hack complete.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@8749 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
db359baa21
commit
10a8c41a38
3 changed files with 352 additions and 173 deletions
|
@ -1,3 +1,11 @@
|
|||
2001-01-22 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSLayoutManager.m: loads of glyph changes - added some
|
||||
implementation notes. Implemented gaps in the glyphs stream.
|
||||
I think all glyph methods are implemented now (probably).
|
||||
Really needs someone elses eye casting over it, to try to
|
||||
catch any obvious bugs.
|
||||
|
||||
2001-01-20 Fred Kiefer <FredKiefer@gmx.de>
|
||||
|
||||
* Source/NSBezierPath.m
|
||||
|
|
|
@ -109,11 +109,12 @@ typedef enum {
|
|||
NSTypesetter *_typesetter;
|
||||
|
||||
void *_glyphData; // Private glyph storage.
|
||||
void *_currentGlyphs;
|
||||
void *_currentGlyphs; // Current chunk pointer.
|
||||
unsigned _currentGlyph; // Current glyph index.
|
||||
void *_glyphGaps; // Gaps in character mapping.
|
||||
unsigned _chunkIndex;
|
||||
unsigned _glyphIndex;
|
||||
unsigned _endCharIndex; // After last char with generated glyph.
|
||||
unsigned _chunkIndex; // Current chunk in glyph stream.
|
||||
unsigned _glyphIndex; // Current glyph in chunk.
|
||||
unsigned _numberOfGlyphs; // After last generated glyph.
|
||||
|
||||
NSGlyphGenerator *_glyphGenerator;
|
||||
NSStorage *_containerUsedRects;
|
||||
|
|
|
@ -190,6 +190,58 @@ offsetSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
return NSOrderedSame;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Glyph management implementation notes (January 2001)
|
||||
* Author - Richard Frith-Macdonald <rfm@gnu.org>
|
||||
*
|
||||
* An NSLayoutManager object maintains a 'glyph stream' which contains the
|
||||
* actual symbols to be displayed in text. This glyph stream is conceptually
|
||||
* an array of glyphs and certain attributes.
|
||||
*
|
||||
* Each glyph has an associated index of the corresponding character in the
|
||||
* text storage object. Since more than one character may map to a single
|
||||
* glyphs, the character index for a glyph is the index of the first
|
||||
* character corresponing to the glyph. Since more than one glyph may map
|
||||
* on to the same character, adjacent glyphs in the glyph stream may have
|
||||
* the same character index.
|
||||
*
|
||||
* Other attributes of a glyph include flags to say whether the glyph is to
|
||||
* be drawn or not, and how it should be layed out with respect to the
|
||||
* preceeding glyph in the stream (allowing for overstrike etc).
|
||||
*
|
||||
* Since the state of the text storage object may change, glyphs may be
|
||||
* deleted from the stream from time to time, leaving a situation where
|
||||
* not all characters in the text storage have corresponding glyphs in
|
||||
* the glyph stream. This state is called a 'gap'. We maintain an array
|
||||
* of the locations of the gaps in the glyph stream. When we attempt to
|
||||
* access a glyph, we must generate new glyphs from the text storage to
|
||||
* fill any gaps in the glyphs stream and insert them into the stream.
|
||||
*
|
||||
*
|
||||
* The glyph stream is actually implemented as an array of 'chunks' -
|
||||
* where a chunk contains an array of glyphs, and array of the glyph
|
||||
* attributes, and the glyph and character indices of the first glyph
|
||||
* in the chunk. The remaining character/glyph indices are calculated
|
||||
* as offsets from the first glyph in the chunk.
|
||||
*
|
||||
* This implementation is used for speed of manipulation of very large
|
||||
* documents - modifications to a single chunk may have their effects
|
||||
* to some degree localised to that chunk.
|
||||
*
|
||||
* Invariants ...
|
||||
* 1. The glyph stream is a continuous array of glyphs ranging from 0 up.
|
||||
* 2. The character index of a glyph in the glyph stream is greater than
|
||||
* or equal to that of the glyph that preceeds it.
|
||||
* 3. The gap array contains glyph indices in numeric order and where no
|
||||
* index exceeds the length of the glyph stream.
|
||||
* 4. The glyph stream consists of at least one chunk whose glyph index
|
||||
* is zero.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Structure to handle the storage of the glyph stream.
|
||||
* This is done as an array of chunks.
|
||||
|
@ -242,13 +294,13 @@ glyphIndexSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
* Glyph management functions.
|
||||
*/
|
||||
static GSGlyphChunk*
|
||||
GSCreateGlyphChunk(unsigned charIndex)
|
||||
GSCreateGlyphChunk(unsigned glyphIndex, unsigned charIndex)
|
||||
{
|
||||
GSGlyphChunk *chunk;
|
||||
|
||||
chunk = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSGlyphChunk));
|
||||
chunk->charIndex = charIndex;
|
||||
chunk->glyphIndex = 0;
|
||||
chunk->glyphIndex = glyphIndex;
|
||||
GSIArrayInitWithZoneAndCapacity(&chunk->glyphs, NSDefaultMallocZone(), 8);
|
||||
GSIArrayInitWithZoneAndCapacity(&chunk->attrs, NSDefaultMallocZone(), 8);
|
||||
return chunk;
|
||||
|
@ -262,43 +314,6 @@ GSDestroyGlyphChunk(GSGlyphChunk *chunk)
|
|||
NSZoneFree(NSDefaultMallocZone(), chunk);
|
||||
}
|
||||
|
||||
static void
|
||||
GSDestroyChunks(GSIArray *where)
|
||||
{
|
||||
if (*where != 0)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/*
|
||||
* Release all glyph chunk information
|
||||
*/
|
||||
i = GSIArrayCount(*where);
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSGlyphChunk *chunk;
|
||||
|
||||
chunk = (GSGlyphChunk*)(GSIArrayItemAtIndex(*where, i).ptr);
|
||||
GSDestroyGlyphChunk(chunk);
|
||||
}
|
||||
GSIArrayEmpty(*where);
|
||||
NSZoneFree(NSDefaultMallocZone(), *where);
|
||||
*where = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static GSGlyphChunk*
|
||||
GSCreateChunks(GSIArray *where)
|
||||
{
|
||||
GSGlyphChunk *chunk;
|
||||
|
||||
GSDestroyChunks(where);
|
||||
*where = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
||||
GSIArrayInitWithZoneAndCapacity(*where, NSDefaultMallocZone(), 8);
|
||||
chunk = GSCreateGlyphChunk(0);
|
||||
GSIArrayInsertItem(*where, (GSIArrayItem)(void*)chunk, 0);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
GSCharIndexForGlyphInChunk(GSGlyphChunk *chunk, unsigned index)
|
||||
{
|
||||
|
@ -354,12 +369,12 @@ typedef struct {
|
|||
|
||||
#define _chunks ((GSIArray)(((lmDefs)lm)->_glyphData))
|
||||
#define _chunk ((GSGlyphChunk*)(((lmDefs)lm)->_currentGlyphs))
|
||||
#define _index ((lmDefs)lm)->_chunkIndex
|
||||
#define _offset ((lmDefs)lm)->_glyphIndex
|
||||
#define _current (((lmDefs)lm)->_currentGlyph)
|
||||
#define _index (((lmDefs)lm)->_chunkIndex)
|
||||
#define _offset (((lmDefs)lm)->_glyphIndex)
|
||||
#define _gaps ((GSIArray)(((lmDefs)lm)->_glyphGaps))
|
||||
|
||||
|
||||
static void _Adjust(NSLayoutManager *lm, unsigned from, int by);
|
||||
static GSGlyphAttrs _Attrs(NSLayoutManager *lm);
|
||||
static BOOL _Back(NSLayoutManager *lm);
|
||||
static unsigned _CharIndex(NSLayoutManager *lm);
|
||||
|
@ -372,54 +387,13 @@ static void _SetGlyph(NSLayoutManager *lm, NSGlyph g);
|
|||
static BOOL _Step(NSLayoutManager *lm);
|
||||
|
||||
|
||||
static inline GSGlyphAttrs
|
||||
_Attrs(NSLayoutManager *lm)
|
||||
{
|
||||
return GSIArrayItemAtIndex(&_chunk->attrs, _offset).ext;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_SetAttrs(NSLayoutManager *lm, GSGlyphAttrs a)
|
||||
{
|
||||
GSIArraySetItemAtIndex(&_chunk->attrs, (GSIArrayItem)a, _offset);
|
||||
}
|
||||
|
||||
static void
|
||||
_Adjust(NSLayoutManager *lm, unsigned from, int lengthChange)
|
||||
{
|
||||
if (from < [((lmDefs)lm)->_textStorage length])
|
||||
{
|
||||
_JumpToGlyph(lm, from);
|
||||
/*
|
||||
* Adjust character offsets for all glyphs in this chunk.
|
||||
*/
|
||||
if (_offset > 0)
|
||||
{
|
||||
while (_offset < GSIArrayCount(&_chunk->glyphs))
|
||||
{
|
||||
GSGlyphAttrs attrs = _Attrs(lm);
|
||||
|
||||
attrs.offset += lengthChange;
|
||||
_SetAttrs(lm, attrs);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now adjust character offsets for remaining chunks.
|
||||
*/
|
||||
while (++_index < GSIArrayCount(_chunks))
|
||||
{
|
||||
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _index).ptr;
|
||||
_chunk->charIndex += lengthChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
_Back(NSLayoutManager *lm)
|
||||
{
|
||||
if (_offset > 0)
|
||||
{
|
||||
_offset--;
|
||||
_current--;
|
||||
return YES;
|
||||
}
|
||||
else if (_index > 0)
|
||||
|
@ -427,6 +401,7 @@ _Back(NSLayoutManager *lm)
|
|||
_index--;
|
||||
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _index).ptr;
|
||||
_offset = GSIArrayCount(&_chunk->glyphs) - 1;
|
||||
_current--;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
|
@ -435,6 +410,79 @@ _Back(NSLayoutManager *lm)
|
|||
}
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
_Step(NSLayoutManager *lm)
|
||||
{
|
||||
if (_offset < GSIArrayCount(&_chunk->glyphs) - 1)
|
||||
{
|
||||
_offset++;
|
||||
_current++;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_index < GSIArrayCount(_chunks) - 1)
|
||||
{
|
||||
_index++;
|
||||
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _index).ptr;
|
||||
_offset = 0;
|
||||
_current++;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the character indices for all the glyphs from the specified
|
||||
* location onwards. Leave the current glyphs set to the 'from' location.
|
||||
*/
|
||||
static void
|
||||
_Adjust(NSLayoutManager *lm, unsigned from, int lengthChange)
|
||||
{
|
||||
if (_JumpToGlyph(lm, from) == YES)
|
||||
{
|
||||
GSGlyphChunk *chunk = _chunk;
|
||||
unsigned index = _index;
|
||||
unsigned offset = _offset;
|
||||
|
||||
/*
|
||||
* Adjust character offsets for all glyphs in this chunk.
|
||||
*/
|
||||
if (offset > 0)
|
||||
{
|
||||
while (offset < GSIArrayCount(&chunk->glyphs))
|
||||
{
|
||||
GSGlyphAttrs a;
|
||||
|
||||
a = GSIArrayItemAtIndex(&chunk->attrs, offset).ext;
|
||||
a.offset += lengthChange;
|
||||
GSIArraySetItemAtIndex(&chunk->attrs, (GSIArrayItem)a, offset);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now adjust character offsets for remaining chunks.
|
||||
*/
|
||||
while (index < GSIArrayCount(_chunks))
|
||||
{
|
||||
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, index).ptr;
|
||||
index++;
|
||||
chunk->charIndex += lengthChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline GSGlyphAttrs
|
||||
_Attrs(NSLayoutManager *lm)
|
||||
{
|
||||
return GSIArrayItemAtIndex(&_chunk->attrs, _offset).ext;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
_CharIndex(NSLayoutManager *lm)
|
||||
{
|
||||
|
@ -485,6 +533,7 @@ _JumpToChar(NSLayoutManager *lm, unsigned charIndex)
|
|||
_chunk = c;
|
||||
_index = i;
|
||||
_offset = o;
|
||||
_current = c->glyphIndex + o;
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
@ -495,6 +544,24 @@ _JumpToGlyph(NSLayoutManager *lm, unsigned glyphIndex)
|
|||
unsigned i;
|
||||
unsigned o;
|
||||
|
||||
/*
|
||||
* Attempt a little optimisation based on the current glyph index -
|
||||
* avoid binary search through glyph chunks if a simpler operation
|
||||
* will suffice.
|
||||
*/
|
||||
if (glyphIndex == _current)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
else if (glyphIndex == _current + 1)
|
||||
{
|
||||
return _Step(lm);
|
||||
}
|
||||
else if (glyphIndex == _current - 1)
|
||||
{
|
||||
return _Back(lm);
|
||||
}
|
||||
|
||||
i = GSChunkForGlyphIndex(_chunks, glyphIndex);
|
||||
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, i).ptr;
|
||||
o = glyphIndex - c->glyphIndex;
|
||||
|
@ -503,6 +570,7 @@ _JumpToGlyph(NSLayoutManager *lm, unsigned glyphIndex)
|
|||
_chunk = c;
|
||||
_index = i;
|
||||
_offset = o;
|
||||
_current = c->glyphIndex + o;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
|
@ -511,36 +579,18 @@ _JumpToGlyph(NSLayoutManager *lm, unsigned glyphIndex)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_SetAttrs(NSLayoutManager *lm, GSGlyphAttrs a)
|
||||
{
|
||||
GSIArraySetItemAtIndex(&_chunk->attrs, (GSIArrayItem)a, _offset);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_SetGlyph(NSLayoutManager *lm, NSGlyph g)
|
||||
{
|
||||
GSIArraySetItemAtIndex(&_chunk->glyphs, (GSIArrayItem)g, _offset);
|
||||
}
|
||||
|
||||
static inline BOOL
|
||||
_Step(NSLayoutManager *lm)
|
||||
{
|
||||
if (_offset < GSIArrayCount(&_chunk->glyphs) - 1)
|
||||
{
|
||||
_offset++;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_index < GSIArrayCount(_chunks) - 1)
|
||||
{
|
||||
_index++;
|
||||
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _index).ptr;
|
||||
_offset = 0;
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@interface GSRunStorage : NSObject
|
||||
|
@ -749,15 +799,20 @@ _Step(NSLayoutManager *lm)
|
|||
* Initialise glyph storage and ivars to contain 'current' glyph
|
||||
* location information.
|
||||
*/
|
||||
_currentGlyphs = GSCreateChunks((GSIArray*)&_glyphData);
|
||||
_glyphData = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
||||
GSIArrayInitWithZoneAndCapacity(glyphChunks, NSDefaultMallocZone(), 8);
|
||||
_currentGlyphs = GSCreateGlyphChunk(0, 0);
|
||||
GSIArrayInsertItem(glyphChunks, (GSIArrayItem)_currentGlyphs, 0);
|
||||
_chunkIndex = 0;
|
||||
_glyphIndex = 0;
|
||||
|
||||
/*
|
||||
* Initialise storage of holes in the glyph stream.
|
||||
* Initialise storage of gaps in the glyph stream.
|
||||
* The initial glyph stream is one big gap starting at index 0!
|
||||
*/
|
||||
a = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
||||
GSIArrayInitWithZoneAndCapacity(a, NSDefaultMallocZone(), 8);
|
||||
GSIArrayInsertItem(a, (GSIArrayItem)(unsigned long)0, 0);
|
||||
_glyphGaps = a;
|
||||
}
|
||||
|
||||
|
@ -766,7 +821,22 @@ _Step(NSLayoutManager *lm)
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
GSDestroyChunks((GSIArray*)&_glyphData);
|
||||
unsigned i;
|
||||
|
||||
/*
|
||||
* Release all glyph chunk information
|
||||
*/
|
||||
i = GSIArrayCount(glyphChunks);
|
||||
while (i-- > 0)
|
||||
{
|
||||
GSGlyphChunk *chunk;
|
||||
|
||||
chunk = (GSGlyphChunk*)(GSIArrayItemAtIndex(glyphChunks, i).ptr);
|
||||
GSDestroyGlyphChunk(chunk);
|
||||
}
|
||||
GSIArrayEmpty(glyphChunks);
|
||||
NSZoneFree(NSDefaultMallocZone(), _glyphData);
|
||||
_glyphData = 0;
|
||||
|
||||
GSIArrayEmpty((GSIArray)_glyphGaps);
|
||||
NSZoneFree(NSDefaultMallocZone(), _glyphGaps);
|
||||
|
@ -937,7 +1007,33 @@ _Step(NSLayoutManager *lm)
|
|||
/*
|
||||
* Now adjust character locations for glyphs if necessary.
|
||||
*/
|
||||
_Adjust(self, NSMaxRange(cRange), lengthChange);
|
||||
_Adjust(self, gRange.location, lengthChange);
|
||||
|
||||
/*
|
||||
* Unless the 'lengthChange' accounts for the entire character range
|
||||
* that we deleted glyphs for, we must note the presence of a gap.
|
||||
*/
|
||||
if (cRange.length + lengthChange > 0)
|
||||
{
|
||||
unsigned pos;
|
||||
|
||||
for (pos = 0; pos < GSIArrayCount((GSIArray)_glyphGaps); pos++)
|
||||
{
|
||||
unsigned val;
|
||||
|
||||
val = (unsigned)GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos).ulng;
|
||||
if (val == gRange.location)
|
||||
{
|
||||
break; // Gap already marked here.
|
||||
}
|
||||
if (val > gRange.location)
|
||||
{
|
||||
GSIArrayInsertItem((GSIArray)_glyphGaps,
|
||||
(GSIArrayItem)gRange.location, pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - should invalidate the character range ... but what does that mean?
|
||||
}
|
||||
|
@ -1093,8 +1189,7 @@ invalidatedRange.length);
|
|||
* Invariants ...
|
||||
* a) Glyph chunks are ordered sequentially from zero by character index.
|
||||
* b) Glyph chunks are ordered sequentially from zero by glyph index.
|
||||
* c) Adjacent glyphs in the same chunk may share a character index.
|
||||
* d) _endCharIndex is the index one after the last character in the glyphs.
|
||||
* c) Adjacent glyphs may share a character index.
|
||||
*/
|
||||
- (void) insertGlyph: (NSGlyph)aGlyph
|
||||
atGlyphIndex: (unsigned)glyphIndex
|
||||
|
@ -1103,6 +1198,7 @@ invalidatedRange.length);
|
|||
unsigned chunkCount = GSIArrayCount(glyphChunks);
|
||||
GSGlyphAttrs attrs = { 0 };
|
||||
GSGlyphChunk *chunk;
|
||||
unsigned pos;
|
||||
|
||||
if (glyphIndex == 0 && chunkCount == 0)
|
||||
{
|
||||
|
@ -1110,7 +1206,7 @@ invalidatedRange.length);
|
|||
* Special case - if there are no chunks, this is the
|
||||
* very first glyph and can simply be added to a new chunk.
|
||||
*/
|
||||
chunk = GSCreateGlyphChunk(charIndex);
|
||||
chunk = GSCreateGlyphChunk(glyphIndex, charIndex);
|
||||
GSIArrayAddItem(&chunk->glyphs, (GSIArrayItem)aGlyph);
|
||||
GSIArrayAddItem(&chunk->attrs, (GSIArrayItem)attrs);
|
||||
GSIArrayAddItem(glyphChunks, (GSIArrayItem)(void*)chunk);
|
||||
|
@ -1120,7 +1216,6 @@ invalidatedRange.length);
|
|||
unsigned glyphCount;
|
||||
unsigned glyphOffset;
|
||||
unsigned chunkIndex;
|
||||
unsigned pos;
|
||||
|
||||
/*
|
||||
* Locate the chunk that we should insert into - the last one with
|
||||
|
@ -1274,8 +1369,8 @@ invalidatedRange.length);
|
|||
*/
|
||||
splitChar
|
||||
= (GSIArrayItemAtIndex(&chunk->attrs, splitAt).ext).offset;
|
||||
newChunk = GSCreateGlyphChunk(chunk->charIndex + splitChar);
|
||||
newChunk->glyphIndex = chunk->glyphIndex + splitAt;
|
||||
newChunk = GSCreateGlyphChunk(chunk->glyphIndex + splitAt,
|
||||
chunk->charIndex + splitChar);
|
||||
GSIArrayInsertItem(glyphChunks, (GSIArrayItem)(void*)newChunk,
|
||||
chunkIndex+1);
|
||||
pos = 0;
|
||||
|
@ -1359,6 +1454,25 @@ invalidatedRange.length);
|
|||
chunk->glyphIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
_numberOfGlyphs++;
|
||||
|
||||
/*
|
||||
* Now adjust gaps to handle glyph insertion.
|
||||
*/
|
||||
pos = 0;
|
||||
while (pos < GSIArrayCount((GSIArray)_glyphGaps))
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
val = GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos).ulng;
|
||||
if (val >= glyphIndex)
|
||||
{
|
||||
GSIArraySetItemAtIndex((GSIArray)_glyphGaps, (GSIArrayItem)(val+1),
|
||||
pos);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any holes in the glyph stream this will cause glyph
|
||||
|
@ -1385,40 +1499,60 @@ invalidatedRange.length);
|
|||
isValidIndex: (BOOL*)flag
|
||||
{
|
||||
#if USE_GLYPHS
|
||||
/*
|
||||
* If the chunk located doesn't contain the index we want,
|
||||
* we must need to generate more glyphs from the text.
|
||||
*/
|
||||
if (_JumpToGlyph(self, index) == NO)
|
||||
if (GSIArrayCount((GSIArray)_glyphGaps) > 0
|
||||
&& (GSIArrayItemAtIndex((GSIArray)_glyphGaps, 0).ulng) <= index)
|
||||
{
|
||||
GSGlyphChunk *chunk = (GSGlyphChunk*)_currentGlyphs;
|
||||
unsigned pos = _chunkIndex;
|
||||
unsigned numChars;
|
||||
unsigned numGlyphs;
|
||||
NSString *string;
|
||||
unsigned long gap;
|
||||
unsigned textLength;
|
||||
|
||||
numChars = [_textStorage length];
|
||||
numGlyphs = chunk->glyphIndex + GSIArrayCount(&chunk->glyphs);
|
||||
string = [_textStorage string];
|
||||
textLength = [string length];
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* Here we put some simple-minded code to generate glyphs from
|
||||
* characters assuming that a glyph is the same as a character.
|
||||
*/
|
||||
while (_endCharIndex < numChars
|
||||
&& chunk->glyphIndex + GSIArrayCount(&chunk->glyphs) <= index)
|
||||
while (GSIArrayCount((GSIArray)_glyphGaps) > 0
|
||||
&& (gap = GSIArrayItemAtIndex((GSIArray)_glyphGaps, 0).ulng) <= index)
|
||||
{
|
||||
unichar c = [string characterAtIndex: _endCharIndex];
|
||||
unsigned endChar;
|
||||
unsigned startChar;
|
||||
|
||||
[self insertGlyph: (NSGlyph)c
|
||||
atGlyphIndex: numGlyphs++
|
||||
characterIndex: _endCharIndex++];
|
||||
if (pos != GSIArrayCount(glyphChunks) - 1)
|
||||
if (gap == 0)
|
||||
{
|
||||
pos++;
|
||||
chunk
|
||||
= (GSGlyphChunk*)(GSIArrayItemAtIndex(glyphChunks, pos).ptr);
|
||||
startChar = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Locate the glyph that preceeds the gap, and start with the
|
||||
* a character one beyond the one that generated the glyph.
|
||||
* This guarantees that we won't try to re-generate the
|
||||
* preceeding glyph.
|
||||
*/
|
||||
_JumpToGlyph(self, gap - 1);
|
||||
startChar = _CharIndex(self) + 1;
|
||||
}
|
||||
if (gap == _numberOfGlyphs)
|
||||
{
|
||||
endChar = textLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
_JumpToGlyph(self, gap);
|
||||
endChar = _CharIndex(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* Here we put some simple-minded code to generate glyphs from
|
||||
* characters assuming that a glyph is the same as a character.
|
||||
*/
|
||||
GSIArrayRemoveItemAtIndex((GSIArray)_glyphGaps, 0); // Remove gap.
|
||||
while (startChar < endChar)
|
||||
{
|
||||
unichar c = [string characterAtIndex: startChar];
|
||||
|
||||
[self insertGlyph: (NSGlyph)c
|
||||
atGlyphIndex: gap++
|
||||
characterIndex: startChar++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1504,12 +1638,19 @@ invalidatedRange.length);
|
|||
|
||||
if (aRange.length == 0)
|
||||
{
|
||||
return;
|
||||
return; // Nothing to delete.
|
||||
}
|
||||
pos = GSIArrayCount(glyphChunks) - 1;
|
||||
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, pos).ptr;
|
||||
pos = chunk->glyphIndex + GSIArrayCount(&chunk->glyphs);
|
||||
if (aRange.location >= pos)
|
||||
{
|
||||
return; // Range is beyond glyphs.
|
||||
}
|
||||
if (NSMaxRange(aRange) > pos)
|
||||
{
|
||||
aRange.length = pos - aRange.location; // Truncate range to glyphs.
|
||||
}
|
||||
/*
|
||||
* Force range to be complete.
|
||||
*/
|
||||
[self glyphAtIndex: NSMaxRange(aRange)-1];
|
||||
|
||||
chunkStart = GSChunkForGlyphIndex(glyphChunks, aRange.location);
|
||||
chunkEnd = GSChunkForGlyphIndex(glyphChunks, NSMaxRange(aRange)-1);
|
||||
|
@ -1575,6 +1716,33 @@ invalidatedRange.length);
|
|||
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkEnd).ptr;
|
||||
chunk->glyphIndex -= aRange.length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any gaps that were in the deleted range and adjust the
|
||||
* indices of any remaining gaps to allow for the deletion.
|
||||
*/
|
||||
pos = 0;
|
||||
while (pos < GSIArrayCount((GSIArray)_glyphGaps))
|
||||
{
|
||||
unsigned val = GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos).ulng;
|
||||
|
||||
if (val < aRange.location)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
else if (val < aRange.length)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex((GSIArray)_glyphGaps, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
val -= aRange.length;
|
||||
GSIArraySetItemAtIndex((GSIArray)_glyphGaps, (GSIArrayItem)val, pos);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
_numberOfGlyphs -= aRange.length;
|
||||
}
|
||||
|
||||
// If there are any holes in the glyph stream, this will cause all
|
||||
|
@ -1582,10 +1750,7 @@ invalidatedRange.length);
|
|||
- (unsigned) numberOfGlyphs
|
||||
{
|
||||
#if USE_GLYPHS
|
||||
GSGlyphChunk *chunk;
|
||||
unsigned pos;
|
||||
|
||||
if (_endCharIndex < [_textStorage length])
|
||||
if (GSIArrayCount((GSIArray)_glyphGaps) > 0)
|
||||
{
|
||||
BOOL valid;
|
||||
|
||||
|
@ -1594,9 +1759,7 @@ invalidatedRange.length);
|
|||
*/
|
||||
[self glyphAtIndex: 0x7fffffff isValidIndex: &valid];
|
||||
}
|
||||
pos = GSChunkForGlyphIndex(glyphChunks, 0x7fffffff);
|
||||
chunk = (GSGlyphChunk*)(GSIArrayItemAtIndex(glyphChunks, pos).ptr);
|
||||
return chunk->glyphIndex + GSIArrayCount(&chunk->glyphs);
|
||||
return _numberOfGlyphs;
|
||||
#else
|
||||
return [_textStorage length];
|
||||
#endif
|
||||
|
@ -1729,28 +1892,35 @@ invalidatedRange.length);
|
|||
}
|
||||
else
|
||||
{
|
||||
BOOL exists;
|
||||
|
||||
/*
|
||||
* Make sure that the last glyph in the range exists.
|
||||
*/
|
||||
[self glyphAtIndex: NSMaxRange(glyphRange)-1];
|
||||
|
||||
/*
|
||||
* Locate the glyph immediately beyond the range.
|
||||
*/
|
||||
if (_JumpToGlyph(self, NSMaxRange(glyphRange)) == NO)
|
||||
[self glyphAtIndex: NSMaxRange(glyphRange)
|
||||
isValidIndex: &exists];
|
||||
if (exists == YES)
|
||||
{
|
||||
pos = _endCharIndex - 1;
|
||||
_JumpToGlyph(self, NSMaxRange(glyphRange));
|
||||
gRange.length = _GlyphIndex(self) - gRange.location;
|
||||
pos = _CharIndex(self);
|
||||
}
|
||||
else if (_numberOfGlyphs == NSMaxRange(glyphRange)-1)
|
||||
{
|
||||
_JumpToGlyph(self, NSMaxRange(glyphRange)-1);
|
||||
gRange.length = _numberOfGlyphs - gRange.location;
|
||||
pos = [_textStorage length];
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = _CharIndex(self);
|
||||
gRange.length = _GlyphIndex(self) - gRange.location;
|
||||
while (_Back(self) == YES && _CharIndex(self) == pos)
|
||||
{
|
||||
gRange.length--;
|
||||
}
|
||||
[NSException raise: NSRangeException
|
||||
format: @"glyph index out of range"];
|
||||
}
|
||||
while (_Back(self) == YES && _CharIndex(self) == pos)
|
||||
{
|
||||
gRange.length--;
|
||||
}
|
||||
|
||||
cRange.length = pos - cRange.location;
|
||||
}
|
||||
if (actualGlyphRange != 0)
|
||||
|
|
Loading…
Reference in a new issue