2001-12-17 16:51:51 +00:00
|
|
|
|
/** <title>NSLayoutManager</title>
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
2001-12-17 16:51:51 +00:00
|
|
|
|
<abstract>The text layout manager class</abstract>
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
|
|
|
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
|
|
|
|
|
2001-12-17 16:51:51 +00:00
|
|
|
|
Author: Jonathan Gapen <jagapen@smithlab.chem.wisc.edu>
|
1999-07-15 17:32:38 +00:00
|
|
|
|
Date: July 1999
|
1999-08-19 23:18:25 +00:00
|
|
|
|
Author: Michael Hanni <mhanni@sprintmail.com>
|
|
|
|
|
Date: August 1999
|
2002-02-20 08:52:39 +00:00
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
#include <AppKit/NSLayoutManager.h>
|
2001-11-24 15:50:54 +00:00
|
|
|
|
#include <AppKit/NSParagraphStyle.h>
|
2000-10-12 23:01:43 +00:00
|
|
|
|
#include "GSSimpleLayoutManager.h"
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
2002-04-11 23:17:42 +00:00
|
|
|
|
#include <AppKit/NSImage.h>
|
2000-12-21 17:29:51 +00:00
|
|
|
|
#include <AppKit/NSWindow.h>
|
2001-01-11 19:09:02 +00:00
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#define USE_GLYPHS 0
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#define ALL_CHECKS 0
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
#define BOTH (NSTextStorageEditedCharacters | NSTextStorageEditedAttributes)
|
2001-01-12 12:32:32 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#if ALL_CHECKS
|
|
|
|
|
static void missmatch(SEL s)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Missmatch in %@", NSStringFromSelector(s));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
/*
|
|
|
|
|
* Glyph attributes known to the layout manager.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
GSGlyphDrawsOutsideLineFragment,
|
2001-01-19 23:22:16 +00:00
|
|
|
|
GSGlyphIsNotShown,
|
|
|
|
|
GSGlyphGeneration,
|
|
|
|
|
GSGlyphInscription,
|
2001-01-11 19:09:02 +00:00
|
|
|
|
} GSGlyphAttributes;
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
typedef union {
|
|
|
|
|
/*
|
|
|
|
|
* A structure to hold information about a glyph in the glyph stream
|
|
|
|
|
* NB. This structure should be no more than 64 bits, so it can fit
|
|
|
|
|
* in as a GSIArray element of the same size as the NSRange structure
|
|
|
|
|
* used to hold gap information.
|
|
|
|
|
*/
|
|
|
|
|
struct {
|
|
|
|
|
unsigned offset:24; // characters in from start of chunk
|
|
|
|
|
unsigned drawsOutsideLineFragment:1; // glyph bigger than fragment?
|
|
|
|
|
unsigned isNotShown:1; // glyph invisible (space, tab etc)
|
|
|
|
|
unsigned inscription:3; // NSGlyphInscription info
|
|
|
|
|
unsigned generation:3; // Other attributes
|
|
|
|
|
NSGlyph glyph; // The glyph itsself
|
|
|
|
|
} g;
|
|
|
|
|
NSRange r; // A range of invalidated glyphs (gap)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
} GSGlyphAttrs;
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
#define gRange(A) ((A).ext.r)
|
|
|
|
|
#define gGlyph(A) ((A).ext.g.glyph)
|
|
|
|
|
#define gOffset(A) ((A).ext.g.offset)
|
|
|
|
|
#define gDrawsOutside(A) ((A).ext.g.drawsOutsideLineFragment)
|
|
|
|
|
#define gIsNotShown(A) ((A).ext.g.isNotShown)
|
|
|
|
|
#define gInscription(A) ((A).ext.g.inscription)
|
|
|
|
|
#define gGeneration(A) ((A).ext.g.generation)
|
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
2000-12-21 17:29:51 +00:00
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
/*
|
|
|
|
|
* We need a fast array that can store -
|
|
|
|
|
* pointers, objects, glyphs (long) and attributes.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
#define GSI_ARRAY_TYPES GSUNION_PTR|GSUNION_OBJ|GSUNION_INT|GSUNION_LONG
|
2002-02-01 11:01:27 +00:00
|
|
|
|
#define GSI_ARRAY_TYPE GSGlyphAttrs
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We handle retain/release explicitly, so we can use GSIArrays to hold
|
|
|
|
|
* non-object values.
|
|
|
|
|
*/
|
|
|
|
|
#define GSI_ARRAY_NO_RELEASE 1
|
|
|
|
|
#define GSI_ARRAY_NO_RETAIN 1
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
#ifdef GSIArray
|
|
|
|
|
#undef GSIArray
|
|
|
|
|
#endif
|
|
|
|
|
#include <base/GSIArray.h>
|
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
/*
|
|
|
|
|
* The glyph attributes within a chunk must be ordered by their offset fields,
|
|
|
|
|
* so we can use a binary search to find the item for a particular offset.
|
|
|
|
|
*/
|
|
|
|
|
static NSComparisonResult
|
|
|
|
|
offsetSort(GSIArrayItem i0, GSIArrayItem i1)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
if (gOffset(i0) < gOffset(i1))
|
2001-01-11 19:09:02 +00:00
|
|
|
|
return NSOrderedAscending;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
else if (gOffset(i0) > gOffset(i1))
|
2001-01-11 19:09:02 +00:00
|
|
|
|
return NSOrderedDescending;
|
|
|
|
|
else
|
|
|
|
|
return NSOrderedSame;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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.
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* 3. The gap array contains ranges of invalidated glyphs in numeric order
|
|
|
|
|
* and where no index exceeds the length of the glyph stream.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
* 4. The glyph stream consists of at least one chunk whose glyph index
|
|
|
|
|
* is zero.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
/*
|
|
|
|
|
* Structure to handle the storage of the glyph stream.
|
|
|
|
|
* This is done as an array of chunks.
|
|
|
|
|
* Each chunk contains an array of glyphs and corresponding attributes.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct {
|
|
|
|
|
unsigned charIndex; // Index of character at start of chunk
|
|
|
|
|
unsigned glyphIndex; // Index of glyph at start of chunk
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArray_t glyphs; // Array of glyphs and their attributes.
|
2001-01-11 19:09:02 +00:00
|
|
|
|
} GSGlyphChunk;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The glyph chunks must be ordered by their charIndex offset fields,
|
|
|
|
|
* so we can use a binary search to find the item for a particular
|
|
|
|
|
* character index.
|
|
|
|
|
*/
|
|
|
|
|
static NSComparisonResult
|
|
|
|
|
charIndexSort(GSIArrayItem i0, GSIArrayItem i1)
|
|
|
|
|
{
|
|
|
|
|
if (((GSGlyphChunk*)(i0.ptr))->charIndex
|
|
|
|
|
< (((GSGlyphChunk*)(i1.ptr))->charIndex))
|
|
|
|
|
return NSOrderedAscending;
|
|
|
|
|
else if (((GSGlyphChunk*)(i0.ptr))->charIndex
|
|
|
|
|
> (((GSGlyphChunk*)(i1.ptr))->charIndex))
|
|
|
|
|
return NSOrderedDescending;
|
|
|
|
|
else
|
|
|
|
|
return NSOrderedSame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The glyph chunks must be ordered by their glyphIndex offset fields,
|
|
|
|
|
* so we can use a binary search to find the item for a particular
|
|
|
|
|
* glyph index.
|
|
|
|
|
*/
|
|
|
|
|
static NSComparisonResult
|
|
|
|
|
glyphIndexSort(GSIArrayItem i0, GSIArrayItem i1)
|
|
|
|
|
{
|
|
|
|
|
if (((GSGlyphChunk*)(i0.ptr))->glyphIndex
|
|
|
|
|
< (((GSGlyphChunk*)(i1.ptr))->glyphIndex))
|
|
|
|
|
return NSOrderedAscending;
|
|
|
|
|
else if (((GSGlyphChunk*)(i0.ptr))->glyphIndex
|
|
|
|
|
> (((GSGlyphChunk*)(i1.ptr))->glyphIndex))
|
|
|
|
|
return NSOrderedDescending;
|
|
|
|
|
else
|
|
|
|
|
return NSOrderedSame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Glyph management functions.
|
|
|
|
|
*/
|
|
|
|
|
static GSGlyphChunk*
|
2001-01-22 18:13:43 +00:00
|
|
|
|
GSCreateGlyphChunk(unsigned glyphIndex, unsigned charIndex)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *chunk;
|
|
|
|
|
|
|
|
|
|
chunk = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSGlyphChunk));
|
|
|
|
|
chunk->charIndex = charIndex;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
chunk->glyphIndex = glyphIndex;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
GSIArrayInitWithZoneAndCapacity(&chunk->glyphs, NSDefaultMallocZone(), 8);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
return chunk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
GSDestroyGlyphChunk(GSGlyphChunk *chunk)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayClear(&chunk->glyphs);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), chunk);
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-12 12:43:22 +00:00
|
|
|
|
static unsigned
|
2001-01-11 19:09:02 +00:00
|
|
|
|
GSChunkForCharIndex(GSIArray chunks, unsigned charIndex)
|
|
|
|
|
{
|
|
|
|
|
unsigned pos;
|
|
|
|
|
GSGlyphChunk tmp;
|
|
|
|
|
|
|
|
|
|
tmp.charIndex = charIndex;
|
|
|
|
|
pos = GSIArrayInsertionPosition(chunks, (GSIArrayItem)(void*)&tmp,
|
|
|
|
|
charIndexSort);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
/*
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* pos is the index of the next chunk *after* the one we want,
|
|
|
|
|
* unless we want something in the very first chunk.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (pos > 0)
|
|
|
|
|
{
|
|
|
|
|
pos--;
|
|
|
|
|
}
|
2001-01-12 12:43:22 +00:00
|
|
|
|
return pos;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-12 12:43:22 +00:00
|
|
|
|
static unsigned
|
2001-01-11 19:09:02 +00:00
|
|
|
|
GSChunkForGlyphIndex(GSIArray chunks, unsigned glyphIndex)
|
|
|
|
|
{
|
|
|
|
|
unsigned pos;
|
|
|
|
|
GSGlyphChunk tmp;
|
|
|
|
|
|
|
|
|
|
tmp.glyphIndex = glyphIndex;
|
|
|
|
|
pos = GSIArrayInsertionPosition(chunks, (GSIArrayItem)(void*)&tmp,
|
|
|
|
|
glyphIndexSort);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
/*
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* pos is the index of the next chunk *after* the one we want,
|
|
|
|
|
* unless we want something in the very first chunk.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
*/
|
2001-01-20 08:58:25 +00:00
|
|
|
|
NSCAssert(pos > 0, @"No glyph chunks present");
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (pos > 0)
|
|
|
|
|
{
|
|
|
|
|
pos--;
|
|
|
|
|
}
|
2001-01-12 12:43:22 +00:00
|
|
|
|
return pos;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Medium level functions for accessing and manipulating glyphs.
|
|
|
|
|
*/
|
|
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
typedef struct {
|
2001-01-20 08:58:25 +00:00
|
|
|
|
@defs(NSLayoutManager)
|
|
|
|
|
} *lmDefs;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
#define glyphChunks ((GSIArray)_glyphData)
|
|
|
|
|
|
|
|
|
|
#define _chunks ((GSIArray)(((lmDefs)lm)->_glyphData))
|
|
|
|
|
#define _chunk ((GSGlyphChunk*)(((lmDefs)lm)->_currentGlyphs))
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#define _gindex (((lmDefs)lm)->_glyphIndex)
|
|
|
|
|
#define _cindex (((lmDefs)lm)->_chunkIndex)
|
|
|
|
|
#define _offset (((lmDefs)lm)->_glyphOffset)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
#define _gaps ((GSIArray)(((lmDefs)lm)->_glyphGaps))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL _Back(NSLayoutManager *lm);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
static unsigned _CharEnd(NSLayoutManager *lm);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static unsigned _CharIndex(NSLayoutManager *lm);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
static unsigned _GlyphEnd(NSLayoutManager *lm);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static unsigned _GlyphIndex(NSLayoutManager *lm);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
static GSIArrayItem *_Info(NSLayoutManager *lm);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static BOOL _JumpToChar(NSLayoutManager *lm, unsigned charIndex);
|
|
|
|
|
static BOOL _JumpToGlyph(NSLayoutManager *lm, unsigned glyphIndex);
|
|
|
|
|
static BOOL _Step(NSLayoutManager *lm);
|
|
|
|
|
|
|
|
|
|
|
2001-01-22 18:57:06 +00:00
|
|
|
|
/*
|
|
|
|
|
* Move 'current' glyph index back one place in glyph stream.
|
|
|
|
|
* return NO on failure (start of stream).
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
static inline BOOL
|
|
|
|
|
_Back(NSLayoutManager *lm)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (_offset > 0)
|
|
|
|
|
{
|
|
|
|
|
_offset--;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex--;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
else if (_cindex > 0)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cindex--;
|
|
|
|
|
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _cindex).ptr;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
_offset = GSIArrayCount(&_chunk->glyphs) - 1;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex--;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-22 18:57:06 +00:00
|
|
|
|
/*
|
|
|
|
|
* Move 'current' glyph index forward one place in glyph stream.
|
|
|
|
|
* return NO on failure (end of stream).
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
static inline BOOL
|
|
|
|
|
_Step(NSLayoutManager *lm)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (_offset < GSIArrayCount(&_chunk->glyphs) - 1)
|
|
|
|
|
{
|
|
|
|
|
_offset++;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex++;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (_cindex < GSIArrayCount(_chunks) - 1)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cindex++;
|
|
|
|
|
_chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, _cindex).ptr;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
_offset = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex++;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
/*
|
|
|
|
|
* Adjust the character indices for all the glyphs from the specified
|
|
|
|
|
* location onwards. Leave the current glyphs set to the 'from' location.
|
|
|
|
|
*/
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static void
|
|
|
|
|
_Adjust(NSLayoutManager *lm, unsigned from, int lengthChange)
|
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (_JumpToGlyph(lm, from) == YES)
|
2001-01-17 22:11:52 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
GSGlyphChunk *chunk = _chunk;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
unsigned index = _cindex;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
unsigned offset = _offset;
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
/*
|
|
|
|
|
* Adjust character offsets for all glyphs in this chunk.
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (offset > 0)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
while (offset < GSIArrayCount(&chunk->glyphs))
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(GSIArrayItems(&chunk->glyphs)[offset]) += lengthChange;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
offset--;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
|
index++;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
/*
|
|
|
|
|
* Now adjust character offsets for remaining chunks.
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
while (index < GSIArrayCount(_chunks))
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, index).ptr;
|
|
|
|
|
index++;
|
|
|
|
|
chunk->charIndex += lengthChange;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
2001-01-17 22:11:52 +00:00
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Return the index of the character immediately beyond the last
|
|
|
|
|
* generated glyph.
|
|
|
|
|
*/
|
|
|
|
|
static inline unsigned
|
|
|
|
|
_CharEnd(NSLayoutManager *lm)
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = GSIArrayCount(_chunks);
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *c;
|
|
|
|
|
unsigned j;
|
|
|
|
|
|
|
|
|
|
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, i).ptr;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
j = GSIArrayCount(&c->glyphs);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (j-- > 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
return c->charIndex + gOffset(GSIArrayItemAtIndex(&c->glyphs, j)) + 1;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return the glyph index immediately beyond the last generated glyph.
|
|
|
|
|
*/
|
|
|
|
|
static inline unsigned
|
|
|
|
|
_GlyphEnd(NSLayoutManager *lm)
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = GSIArrayCount(_chunks);
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *c;
|
|
|
|
|
unsigned j;
|
|
|
|
|
|
|
|
|
|
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, i).ptr;
|
|
|
|
|
j = GSIArrayCount(&c->glyphs);
|
|
|
|
|
if (j > 0)
|
|
|
|
|
{
|
|
|
|
|
return c->glyphIndex + j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* return the character index of the current glyph.
|
|
|
|
|
*/
|
2001-01-15 21:48:18 +00:00
|
|
|
|
static inline unsigned
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_CharIndex(NSLayoutManager *lm)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-20 08:58:25 +00:00
|
|
|
|
return _chunk->charIndex
|
2002-02-24 07:39:18 +00:00
|
|
|
|
+ gOffset(GSIArrayItemAtIndex(&_chunk->glyphs, _offset));
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* return the index of the current glyph.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
static inline unsigned
|
|
|
|
|
_GlyphIndex(NSLayoutManager *lm)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
return _chunk->glyphIndex + _offset;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Return the current glyph and attributes
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
static GSIArrayItem *
|
|
|
|
|
_Info(NSLayoutManager *lm)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
return GSIArrayItems(&_chunk->glyphs) + _offset;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Locate the first glyph corresponding to the specified character index
|
|
|
|
|
* and make it the current glyph.
|
|
|
|
|
*/
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static BOOL
|
|
|
|
|
_JumpToChar(NSLayoutManager *lm, unsigned charIndex)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem tmp;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
GSGlyphChunk *c;
|
|
|
|
|
unsigned i;
|
|
|
|
|
unsigned o;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned co;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
i = GSChunkForCharIndex(_chunks, charIndex);
|
|
|
|
|
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, i).ptr;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(tmp) = charIndex - c->charIndex;
|
|
|
|
|
o = GSIArrayInsertionPosition(&c->glyphs, tmp, offsetSort);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (o == 0)
|
|
|
|
|
{
|
|
|
|
|
return NO; // Insertion position not found.
|
|
|
|
|
}
|
|
|
|
|
o--;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
/*
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* Check the character index of this glyph to see if it matches the
|
|
|
|
|
* character index we were asked for. If it doesn't we have probably
|
|
|
|
|
* failed to find a glyph matching the character.
|
2001-01-20 08:58:25 +00:00
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
co = gOffset(GSIArrayItemAtIndex(&c->glyphs, o));
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (co + c->charIndex != charIndex)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if ([((lmDefs)lm)->_textStorage length] > charIndex)
|
|
|
|
|
{
|
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
|
|
r = [[((lmDefs)lm)->_textStorage string]
|
|
|
|
|
rangeOfComposedCharacterSequenceAtIndex: charIndex];
|
|
|
|
|
if (r.length > 0 && r.location == co + c->charIndex)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The requested character is part of a composed character
|
|
|
|
|
* sequence whose first character maps on to the glyph we found.
|
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_chunk = c;
|
|
|
|
|
_cindex = i;
|
|
|
|
|
_offset = o;
|
|
|
|
|
_gindex = c->glyphIndex + o;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate the *first* glyph for this character index...
|
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
while (o > 0 && gOffset(GSIArrayItemAtIndex(&c->glyphs, o-1)) == co)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
o--;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
}
|
|
|
|
|
_chunk = c;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cindex = i;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_offset = o;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex = c->glyphIndex + o;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
return YES;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Make the specified glyph index the current glyph
|
|
|
|
|
*/
|
2001-01-20 08:58:25 +00:00
|
|
|
|
static BOOL
|
|
|
|
|
_JumpToGlyph(NSLayoutManager *lm, unsigned glyphIndex)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-20 08:58:25 +00:00
|
|
|
|
GSGlyphChunk *c;
|
|
|
|
|
unsigned i;
|
|
|
|
|
unsigned o;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
/*
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* Optimise for glyph index zero ... easy to find.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (glyphIndex == 0)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, 0).ptr;
|
|
|
|
|
if (GSIArrayCount(&c->glyphs) > 0)
|
|
|
|
|
{
|
|
|
|
|
_chunk = c;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cindex = 0;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_offset = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex = 0;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
i = GSChunkForGlyphIndex(_chunks, glyphIndex);
|
|
|
|
|
c = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, i).ptr;
|
|
|
|
|
o = glyphIndex - c->glyphIndex;
|
|
|
|
|
if (o < GSIArrayCount(&c->glyphs))
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_chunk = c;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cindex = i;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_offset = o;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_gindex = glyphIndex;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-22 18:57:06 +00:00
|
|
|
|
#if USE_GLYPHS
|
|
|
|
|
static void
|
|
|
|
|
_Sane(NSLayoutManager *lm)
|
|
|
|
|
{
|
|
|
|
|
unsigned lastGlyph = 0;
|
|
|
|
|
unsigned lastChar = 0;
|
|
|
|
|
unsigned pos;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check gaps.
|
|
|
|
|
*/
|
|
|
|
|
for (pos = 0; pos < GSIArrayCount(_gaps); pos++)
|
|
|
|
|
{
|
|
|
|
|
unsigned val = GSIArrayItemAtIndex(_gaps, pos).ulng;
|
|
|
|
|
|
|
|
|
|
NSCAssert(val > lastGlyph || (val == 0 && pos == 0),
|
|
|
|
|
NSInternalInconsistencyException);
|
|
|
|
|
lastGlyph = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSCAssert(GSIArrayCount(_chunks) > 0, NSInternalInconsistencyException);
|
|
|
|
|
lastGlyph = 0;
|
|
|
|
|
for (pos = 0; pos < GSIArrayCount(_chunks); pos++)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *chunk;
|
|
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, pos).ptr;
|
|
|
|
|
NSCAssert(chunk->glyphIndex == (pos == 0 ? 0 : lastGlyph+1),
|
|
|
|
|
NSInternalInconsistencyException);
|
|
|
|
|
NSCAssert(chunk->charIndex >= lastChar, NSInternalInconsistencyException);
|
|
|
|
|
count = GSIArrayCount(&chunk->glyphs);
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem a;
|
2001-01-22 18:57:06 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
a = GSIArrayItemAtIndex(&chunk->glyphs, i);
|
|
|
|
|
NSCAssert(chunk->charIndex + gOffset(a) >= lastChar,
|
2001-01-22 18:57:06 +00:00
|
|
|
|
NSInternalInconsistencyException);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
lastChar = chunk->charIndex + gOffset(a);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
}
|
|
|
|
|
lastGlyph = chunk->glyphIndex + count - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
static void
|
|
|
|
|
_GLog(NSLayoutManager *lm, SEL _cmd)
|
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#if ALL_CHECKS
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned pos;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check gaps.
|
|
|
|
|
*/
|
|
|
|
|
fprintf(stderr, "%s, %x\ngaps (%u) - ",
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_cmd ? sel_get_name(_cmd) : "", (unsigned)lm, GSIArrayCount(_gaps));
|
2002-02-20 08:52:39 +00:00
|
|
|
|
for (pos = 0; pos < GSIArrayCount(_gaps); pos++)
|
|
|
|
|
{
|
|
|
|
|
unsigned val = GSIArrayItemAtIndex(_gaps, pos).ulng;
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, " %u", val);
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "chunks (%u) -\n", GSIArrayCount(_chunks));
|
|
|
|
|
for (pos = 0; pos < GSIArrayCount(_chunks); pos++)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *chunk;
|
|
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(_chunks, pos).ptr;
|
|
|
|
|
count = GSIArrayCount(&chunk->glyphs);
|
|
|
|
|
fprintf(stderr, " glyphs (%u) - gi %d, ci %d\n",
|
|
|
|
|
count, chunk->glyphIndex, chunk->charIndex);
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem a;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
a = GSIArrayItemAtIndex(&chunk->glyphs, i);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
fprintf(stderr, " %4d %4d %c",
|
|
|
|
|
chunk->glyphIndex + i,
|
2002-02-24 07:39:18 +00:00
|
|
|
|
chunk->charIndex + gOffset(a),
|
2002-02-20 19:39:15 +00:00
|
|
|
|
(char)GSIArrayItemAtIndex(&chunk->glyphs, i).ulng);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#endif
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
2001-01-22 18:57:06 +00:00
|
|
|
|
#else
|
|
|
|
|
static inline void
|
|
|
|
|
_Sane(NSLayoutManager *lm)
|
|
|
|
|
{
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
static inline void
|
|
|
|
|
_GLog(NSLayoutManager *lm, SEL _cmd)
|
|
|
|
|
{
|
|
|
|
|
}
|
2001-01-22 18:57:06 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
|
|
|
|
|
|
2000-10-12 23:01:43 +00:00
|
|
|
|
@interface NSLayoutManager (Private)
|
|
|
|
|
|
|
|
|
|
- (void) _doLayout;
|
|
|
|
|
- (int) _rebuildLayoutForTextContainer: (NSTextContainer*)aContainer
|
|
|
|
|
startingAtGlyphIndex: (int)glyphIndex;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>
|
|
|
|
|
* A layout manager handles layout and glyph management for a text
|
|
|
|
|
* storage. A glyph is a symbol draewn to a display, and while
|
|
|
|
|
* there is usually a one to one correspondence between glyphs
|
|
|
|
|
* and characters in the text storage, that is no always the case.</p>
|
|
|
|
|
* <p>
|
|
|
|
|
* Sometimes a group of characters (a unicode composed character sequence)
|
|
|
|
|
* can represent a single glyph, sometimes a single unicode character is
|
|
|
|
|
* represented by multiple glyphs.</p>
|
|
|
|
|
* <p>
|
|
|
|
|
* eg. The text storage may contain the unichar o-umlaut and
|
|
|
|
|
* the glyph stream could contain the two glyphs "o" and umlaut.
|
|
|
|
|
* In this case, we would have two glyphs, with different glyph indexes,
|
|
|
|
|
* both corresponding to a single character index.</p>
|
|
|
|
|
*/
|
1999-07-15 17:32:38 +00:00
|
|
|
|
@implementation NSLayoutManager
|
|
|
|
|
|
2000-10-12 23:01:43 +00:00
|
|
|
|
+ (id) allocWithZone: (NSZone*)z
|
|
|
|
|
{
|
|
|
|
|
// Return a simple layout manager as this is the only working subclass
|
|
|
|
|
if (self == [NSLayoutManager class])
|
|
|
|
|
{
|
|
|
|
|
return [GSSimpleLayoutManager allocWithZone: z];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return NSAllocateObject (self, 0, z);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/** <init />
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Sets up this instance. We should in future find a glyph generator and a
|
|
|
|
|
* typesetter to use with glyphs management, but for now we just set up
|
|
|
|
|
* the glyph storage.
|
2000-12-20 22:55:30 +00:00
|
|
|
|
*/
|
1999-07-15 17:32:38 +00:00
|
|
|
|
- (id) init
|
|
|
|
|
{
|
2002-02-11 02:42:12 +00:00
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
|
|
if (self != nil)
|
2001-01-20 08:58:25 +00:00
|
|
|
|
{
|
|
|
|
|
GSIArray a;
|
|
|
|
|
|
|
|
|
|
_backgroundLayout = YES;
|
|
|
|
|
_delegate = nil;
|
|
|
|
|
_textContainers = [[NSMutableArray alloc] initWithCapacity: 2];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
/*
|
|
|
|
|
* Initialise glyph storage and ivars to contain 'current' glyph
|
|
|
|
|
* location information.
|
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
a = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(a, NSDefaultMallocZone(), 8);
|
|
|
|
|
_glyphData = a;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
_currentGlyphs = GSCreateGlyphChunk(0, 0);
|
|
|
|
|
GSIArrayInsertItem(glyphChunks, (GSIArrayItem)_currentGlyphs, 0);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_chunkIndex = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_glyphOffset = 0;
|
2002-02-11 02:42:12 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
/*
|
2001-01-22 18:13:43 +00:00
|
|
|
|
* Initialise storage of gaps in the glyph stream.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* Initially there are no gaps in the stream.
|
2001-01-20 08:58:25 +00:00
|
|
|
|
*/
|
|
|
|
|
a = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(a, NSDefaultMallocZone(), 8);
|
|
|
|
|
_glyphGaps = a;
|
|
|
|
|
}
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
2002-02-11 02:42:12 +00:00
|
|
|
|
/* We check that the _glyphData and _glyphGaps are not NULL so that
|
|
|
|
|
* we can dealloc an object which has not been -init (some
|
|
|
|
|
* regression tests need it).
|
|
|
|
|
*/
|
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
/*
|
2002-02-11 02:42:12 +00:00
|
|
|
|
* Release all glyph chunk information.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
*/
|
2002-02-11 02:42:12 +00:00
|
|
|
|
if (_glyphData != NULL)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-11 02:42:12 +00:00
|
|
|
|
i = GSIArrayCount(glyphChunks);
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *chunk;
|
|
|
|
|
|
|
|
|
|
chunk = (GSGlyphChunk*)(GSIArrayItemAtIndex(glyphChunks, i).ptr);
|
|
|
|
|
GSDestroyGlyphChunk(chunk);
|
|
|
|
|
}
|
|
|
|
|
GSIArrayEmpty(glyphChunks);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), _glyphData);
|
2001-01-22 18:13:43 +00:00
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
2002-02-11 02:42:12 +00:00
|
|
|
|
if (_glyphGaps != NULL)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayEmpty((GSIArray)_glyphGaps);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), _glyphGaps);
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-11 19:09:02 +00:00
|
|
|
|
RELEASE (_textContainers);
|
2000-10-12 23:01:43 +00:00
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Sets the text storage for the layout manager.
|
|
|
|
|
* Use -replaceTextStorage: instead as a rule. - this method is really
|
|
|
|
|
* more for internal use by the text system.
|
|
|
|
|
* Invalidates the entire layout (should it??)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setTextStorage: (NSTextStorage*)aTextStorage
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned int length;
|
|
|
|
|
NSRange aRange;
|
2000-09-30 23:12:42 +00:00
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Mark the entire existing text storage as invalid.
|
|
|
|
|
*/
|
|
|
|
|
length = [_textStorage length];
|
|
|
|
|
aRange = NSMakeRange(0, length);
|
|
|
|
|
[self textStorage: _textStorage
|
|
|
|
|
edited: BOTH
|
|
|
|
|
range: aRange
|
|
|
|
|
changeInLength: -length
|
|
|
|
|
invalidatedRange: aRange];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make a note of the new text storage object, but don't retain it.
|
|
|
|
|
* The text storage is owning us - it retains us.
|
|
|
|
|
*/
|
2000-12-16 20:17:54 +00:00
|
|
|
|
_textStorage = aTextStorage;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
|
|
|
|
|
length = [aTextStorage length];
|
|
|
|
|
aRange = NSMakeRange (0, length);
|
2000-09-30 23:12:42 +00:00
|
|
|
|
// force complete re - layout
|
|
|
|
|
[self textStorage: aTextStorage
|
2002-02-20 08:52:39 +00:00
|
|
|
|
edited: BOTH
|
|
|
|
|
range: aRange
|
|
|
|
|
changeInLength: length
|
|
|
|
|
invalidatedRange: aRange];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the text storage for this layout manager.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSTextStorage*) textStorage
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _textStorage;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Replaces the test storage with a new one.<br />
|
|
|
|
|
* Takes care (since layout managers are owned by text storages)
|
|
|
|
|
* not to get self deallocated.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) replaceTextStorage: (NSTextStorage*)newTextStorage
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-12 12:32:32 +00:00
|
|
|
|
NSArray *layoutManagers = [_textStorage layoutManagers];
|
|
|
|
|
NSEnumerator *enumerator = [layoutManagers objectEnumerator];
|
|
|
|
|
NSLayoutManager *object;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* Remove layout managers from old NSTextStorage object and add them to the
|
|
|
|
|
new one. NSTextStorage's addLayoutManager invokes NSLayoutManager's
|
|
|
|
|
setTextStorage method automatically, and that includes self. */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
2000-12-16 20:17:54 +00:00
|
|
|
|
while ((object = (NSLayoutManager*)[enumerator nextObject]) != nil)
|
2001-01-12 12:32:32 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
RETAIN(object);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
[_textStorage removeLayoutManager: object];
|
|
|
|
|
[newTextStorage addLayoutManager: object];
|
2002-02-24 07:39:18 +00:00
|
|
|
|
RELEASE(object);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return the text containers
|
2000-12-21 17:29:51 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSArray*) textContainers
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _textContainers;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Adds a container to the layout manager.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (void) addTextContainer: (NSTextContainer*)container
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if ([_textContainers indexOfObjectIdenticalTo: container] == NSNotFound)
|
2001-01-07 00:57:23 +00:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
[_textContainers addObject: container];
|
|
|
|
|
[container setLayoutManager: self];
|
2002-02-24 07:39:18 +00:00
|
|
|
|
// FIXME: Invalidate layout beyond previous last container
|
2001-01-07 00:57:23 +00:00
|
|
|
|
_textContainersCount++;
|
|
|
|
|
/* NB: We do not retain this here ! It's already retained in the
|
|
|
|
|
array. */
|
|
|
|
|
_firstTextView = [(NSTextContainer *)[_textContainers objectAtIndex: 0]
|
|
|
|
|
textView];
|
|
|
|
|
for (i = 0; i < _textContainersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *tv = [[_textContainers objectAtIndex: i] textView];
|
|
|
|
|
|
|
|
|
|
[tv _updateMultipleTextViews];
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Inserts a new text container at index.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) insertTextContainer: (NSTextContainer*)aTextContainer
|
|
|
|
|
atIndex: (unsigned)index
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-12-21 17:29:51 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
[_textContainers insertObject: aTextContainer atIndex: index];
|
|
|
|
|
_textContainersCount++;
|
|
|
|
|
_firstTextView = [(NSTextContainer *)[_textContainers objectAtIndex: 0]
|
|
|
|
|
textView];
|
|
|
|
|
for (i = 0; i < _textContainersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *tv = [[_textContainers objectAtIndex: i] textView];
|
|
|
|
|
|
|
|
|
|
[tv _updateMultipleTextViews];
|
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
// FIXME: Invalidate layout from thsi container onwards
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Removes the text container at index.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) removeTextContainerAtIndex: (unsigned)index
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-12-21 17:29:51 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
// FIXME invalidate from thsi point onwards.
|
1999-07-15 17:32:38 +00:00
|
|
|
|
[_textContainers removeObjectAtIndex: index];
|
2000-12-21 17:29:51 +00:00
|
|
|
|
_textContainersCount--;
|
2002-02-11 02:01:29 +00:00
|
|
|
|
if (_textContainersCount > 0)
|
|
|
|
|
{
|
|
|
|
|
_firstTextView = [(NSTextContainer *)[_textContainers objectAtIndex: 0]
|
|
|
|
|
textView];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_firstTextView = nil;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:29:51 +00:00
|
|
|
|
for (i = 0; i < _textContainersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *tv = [[_textContainers objectAtIndex: i] textView];
|
|
|
|
|
|
|
|
|
|
[tv _updateMultipleTextViews];
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* This determines the glyph range corresponding to aRange and
|
|
|
|
|
* marks the glyphs as invalid. It adjusts the character locations
|
|
|
|
|
* of all glyphs beyond this by lengthChange. It returns the
|
|
|
|
|
* actual character range corresponding to the invalidated glyphs
|
|
|
|
|
* if actualRange is non-nul.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) invalidateGlyphsForCharacterRange: (NSRange)aRange
|
|
|
|
|
changeInLength: (int)lengthChange
|
|
|
|
|
actualCharacterRange: (NSRange*)actualRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem item;
|
2001-01-17 22:11:52 +00:00
|
|
|
|
NSRange cRange;
|
|
|
|
|
NSRange gRange;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
unsigned count;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
|
|
|
|
if (actualRange != 0)
|
|
|
|
|
{
|
|
|
|
|
*actualRange = cRange;
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
if (aRange.length == 0)
|
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
return; // Empty ... nothing to do.
|
|
|
|
|
}
|
|
|
|
|
if (aRange.location >= _CharEnd(self))
|
|
|
|
|
{
|
|
|
|
|
return; // No glyphs generated for that character index.
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-17 22:11:52 +00:00
|
|
|
|
gRange = [self glyphRangeForCharacterRange: aRange
|
|
|
|
|
actualCharacterRange: &cRange];
|
|
|
|
|
if (actualRange != 0)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-17 22:11:52 +00:00
|
|
|
|
*actualRange = cRange;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (gRange.length == 0)
|
|
|
|
|
{
|
|
|
|
|
return; // Nothing to do.
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-17 22:11:52 +00:00
|
|
|
|
/*
|
|
|
|
|
* Now adjust character locations for glyphs if necessary.
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
_Adjust(self, gRange.location, lengthChange);
|
|
|
|
|
|
|
|
|
|
/*
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Now adjust invalidated gaps in the glyph stream.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
count = GSIArrayCount((GSIArray)_glyphGaps);
|
|
|
|
|
if (count == 0)
|
|
|
|
|
{
|
|
|
|
|
gRange(item) = gRange;
|
|
|
|
|
GSIArrayInsertItem((GSIArray)_glyphGaps, item, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
unsigned pos;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
for (pos = 0; pos < count; pos++)
|
|
|
|
|
{
|
|
|
|
|
NSRange val;
|
|
|
|
|
NSRange tmp;
|
|
|
|
|
|
|
|
|
|
val = gRange(GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If there is no overlap, we must either insert the new gap
|
|
|
|
|
* before the found one, or continue to look at the next gap.
|
|
|
|
|
*/
|
|
|
|
|
tmp = NSIntersectionRange(gRange, val);
|
|
|
|
|
if (tmp.length == 0)
|
|
|
|
|
{
|
|
|
|
|
if (gRange.location < val.location)
|
|
|
|
|
{
|
|
|
|
|
gRange(item) = gRange;
|
|
|
|
|
GSIArrayInsertItem((GSIArray)_glyphGaps, item, pos);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the new gap is entirely within an existing one, we
|
|
|
|
|
* don't need to do anything.
|
|
|
|
|
*/
|
|
|
|
|
if (val.location <= gRange.location
|
|
|
|
|
&& NSMaxRange(val) >= NSMaxRange(gRange))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Update the existing gap to be a union with our new gap.
|
|
|
|
|
*/
|
|
|
|
|
gRange = NSUnionRange(gRange, val);
|
|
|
|
|
gRange(item) = gRange;
|
|
|
|
|
GSIArraySetItemAtIndex((GSIArray)_glyphGaps, item, pos);
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
while (pos + 1 < count)
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
val = gRange(GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos + 1));
|
2002-02-20 08:52:39 +00:00
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
/*
|
|
|
|
|
* If there is no overlap with the next gap, we have
|
|
|
|
|
* nothing more to do.
|
|
|
|
|
*/
|
|
|
|
|
tmp = NSIntersectionRange(gRange, val);
|
|
|
|
|
if (tmp.length == 0)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
break;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the next gap extends beyond our gap, we can remove
|
|
|
|
|
* the current one and replace the next one with a union.
|
|
|
|
|
*/
|
|
|
|
|
if (val.location <= gRange.location
|
|
|
|
|
&& NSMaxRange(val) >= NSMaxRange(gRange))
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayRemoveItemsFromIndex((GSIArray)_glyphGaps, pos);
|
|
|
|
|
count--;
|
|
|
|
|
gRange(item) = NSUnionRange(gRange, val);
|
|
|
|
|
GSIArraySetItemAtIndex((GSIArray)_glyphGaps, item, pos);
|
2002-02-20 08:52:39 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The next gap is contained within our gap, so we can
|
|
|
|
|
* simply remove it. Then loop to examine the one beyond
|
|
|
|
|
*/
|
|
|
|
|
GSIArrayRemoveItemsFromIndex((GSIArray)_glyphGaps, pos + 1);
|
|
|
|
|
count--;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-01-17 22:11:52 +00:00
|
|
|
|
|
|
|
|
|
// FIXME - should invalidate the character range ... but what does that mean?
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
_Sane(self);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* This invalidates glyph positions for all glyphs corresponding
|
|
|
|
|
* to the specified character range.<br />
|
|
|
|
|
* If flag is YES then the layout information needs to be redone from
|
|
|
|
|
* scratch, but if it's NO, the layout manager may try to optimise
|
|
|
|
|
* layout from the old information.<br />
|
|
|
|
|
* If actualRange is non-nul, returns the actual range invalidated.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) invalidateLayoutForCharacterRange: (NSRange)aRange
|
|
|
|
|
isSoft: (BOOL)flag
|
|
|
|
|
actualCharacterRange: (NSRange*)actualRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
[self _doLayout];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Causes redisplay of aRange, but does not lose the alyout information.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) invalidateDisplayForCharacterRange: (NSRange)aRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/* FIXME */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Causes redisplay of aRange, but does not lose the alyout information.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) invalidateDisplayForGlyphRange: (NSRange)aRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/* FIXME */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Invalidates the layout of all glyphs in aContainer and all containers
|
|
|
|
|
* following it.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) textContainerChangedGeometry: (NSTextContainer*)aContainer
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
// find the first character in that text container
|
2000-11-03 00:21:29 +00:00
|
|
|
|
NSRange aRange = [self glyphRangeForTextContainer: aContainer];
|
|
|
|
|
unsigned first = aRange.location;
|
2000-08-27 22:32:29 +00:00
|
|
|
|
|
|
|
|
|
// invalidate the layout from here on
|
|
|
|
|
[self invalidateLayoutForCharacterRange:
|
2000-12-16 20:17:54 +00:00
|
|
|
|
NSMakeRange(first, [_textStorage length] - first)
|
2000-08-27 22:32:29 +00:00
|
|
|
|
isSoft: NO
|
|
|
|
|
actualCharacterRange: NULL];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Notifies the layout manager that one of its text containers has
|
|
|
|
|
* changed its view and an update of the display is needed.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) textContainerChangedTextView: (NSTextContainer*)aContainer
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-11 16:37:21 +00:00
|
|
|
|
unsigned index;
|
|
|
|
|
|
|
|
|
|
index = [_textContainers indexOfObjectIdenticalTo: aContainer];
|
|
|
|
|
|
|
|
|
|
if (index != NSNotFound)
|
2001-01-07 00:57:23 +00:00
|
|
|
|
{
|
2002-02-11 16:37:21 +00:00
|
|
|
|
if (index == 0)
|
2001-01-07 00:57:23 +00:00
|
|
|
|
{
|
2002-02-11 16:37:21 +00:00
|
|
|
|
_firstTextView = [aContainer textView];
|
|
|
|
|
|
|
|
|
|
/* It only makes sense to update the other text views if we
|
|
|
|
|
have more than one text container */
|
|
|
|
|
if (_textContainersCount > 1)
|
2001-01-07 00:57:23 +00:00
|
|
|
|
{
|
|
|
|
|
/* It's the first text view. Need to update everything. */
|
|
|
|
|
int i;
|
2002-02-11 16:37:21 +00:00
|
|
|
|
|
2001-01-07 00:57:23 +00:00
|
|
|
|
for (i = 0; i < _textContainersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *tv;
|
2002-02-11 16:37:21 +00:00
|
|
|
|
|
2001-01-07 00:57:23 +00:00
|
|
|
|
tv = [[_textContainers objectAtIndex: i] textView];
|
|
|
|
|
[tv _updateMultipleTextViews];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* This method is used to handle an editing change to aTextStorage.
|
|
|
|
|
* The mask value indicates whether characters or attribuytes or both
|
|
|
|
|
* have changed.<br />
|
|
|
|
|
* If characters have not changed, the lengthChange argument is ignored.<br/>
|
|
|
|
|
* The newCharRange is the effected range currently in the storage, while
|
|
|
|
|
* invalidatedRange is the original range effected.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) textStorage: (NSTextStorage*)aTextStorage
|
|
|
|
|
edited: (unsigned)mask
|
2002-02-20 08:52:39 +00:00
|
|
|
|
range: (NSRange)newCharRange
|
2000-02-19 00:40:47 +00:00
|
|
|
|
changeInLength: (int)lengthChange
|
|
|
|
|
invalidatedRange: (NSRange)invalidatedRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
/*
|
1999-07-25 04:58:39 +00:00
|
|
|
|
NSLog(@"NSLayoutManager was just notified that a change in the text
|
|
|
|
|
storage occured.");
|
1999-11-22 21:48:03 +00:00
|
|
|
|
NSLog(@"range: (%d, %d) changeInLength: %d invalidatedRange (%d, %d)",
|
2002-02-20 08:52:39 +00:00
|
|
|
|
newCharRange.location, newCharRange.length, lengthChange, invalidatedRange.location,
|
1999-11-22 21:48:03 +00:00
|
|
|
|
invalidatedRange.length);
|
2000-08-27 22:32:29 +00:00
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
int delta = 0;
|
|
|
|
|
unsigned int last;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2000-08-27 22:32:29 +00:00
|
|
|
|
if (mask & NSTextStorageEditedCharacters)
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
delta = lengthChange;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
else if (mask == 0)
|
|
|
|
|
{
|
|
|
|
|
return; // No changes to make.
|
|
|
|
|
}
|
|
|
|
|
last = NSMaxRange (invalidatedRange);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
// hard invalidation occures here.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
[self invalidateGlyphsForCharacterRange: newCharRange
|
|
|
|
|
changeInLength: delta
|
|
|
|
|
actualCharacterRange: NULL];
|
2000-08-27 22:32:29 +00:00
|
|
|
|
[self invalidateLayoutForCharacterRange: invalidatedRange
|
2002-02-20 08:52:39 +00:00
|
|
|
|
isSoft: NO
|
|
|
|
|
actualCharacterRange: NULL];
|
2000-08-27 22:32:29 +00:00
|
|
|
|
|
|
|
|
|
// the following range is soft invalidated
|
2002-02-20 08:52:39 +00:00
|
|
|
|
newCharRange = NSMakeRange (last, [_textStorage length] - last);
|
|
|
|
|
[self invalidateLayoutForCharacterRange: newCharRange
|
|
|
|
|
isSoft: YES
|
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
|
_GLog(self,_cmd);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Sets flag to say if text should get laid out in
|
|
|
|
|
* the background when the run lopp is idle.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setBackgroundLayoutEnabled: (BOOL)flag
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_backgroundLayout = flag;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns flag to say if text should get laid out in
|
|
|
|
|
* the background when the run lopp is idle.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) backgroundLayoutEnabled
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _backgroundLayout;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Used by the internal glyph generation system to insert aGlyph into
|
|
|
|
|
* the glyph stream athe the specified glyphIndex and charIndex.<br />
|
|
|
|
|
* Invariants ...<br />
|
|
|
|
|
* a) Glyph chunks are ordered sequentially from zero by character index.<br />
|
|
|
|
|
* b) Glyph chunks are ordered sequentially from zero by glyph index.<br />
|
|
|
|
|
* c) Adjacent glyphs may share a character index.<br />
|
2001-01-15 21:48:18 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) insertGlyph: (NSGlyph)aGlyph
|
|
|
|
|
atGlyphIndex: (unsigned)glyphIndex
|
|
|
|
|
characterIndex: (unsigned)charIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-12 12:32:32 +00:00
|
|
|
|
unsigned chunkCount = GSIArrayCount(glyphChunks);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem info = { 0 };
|
2001-01-11 19:09:02 +00:00
|
|
|
|
GSGlyphChunk *chunk;
|
2001-01-22 18:13:43 +00:00
|
|
|
|
unsigned pos;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (glyphIndex == 0 && chunkCount == 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Special case - if there are no chunks, this is the
|
|
|
|
|
* very first glyph and can simply be added to a new chunk.
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
chunk = GSCreateGlyphChunk(glyphIndex, charIndex);
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gGlyph(info) = aGlyph;
|
|
|
|
|
GSIArrayAddItem(&chunk->glyphs, info);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
GSIArrayAddItem(glyphChunks, (GSIArrayItem)(void*)chunk);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned gCount;
|
|
|
|
|
unsigned gOffset;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
unsigned chunkIndex;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate the chunk that we should insert into - the last one with
|
|
|
|
|
* a glyphIndex less than or equal to the index we were given.
|
|
|
|
|
*/
|
2001-01-15 21:48:18 +00:00
|
|
|
|
chunkIndex = GSChunkForGlyphIndex(glyphChunks, glyphIndex);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkIndex).ptr;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check for the case where we have been given an index that's
|
|
|
|
|
* beyond the end of the last chunk.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
gCount = GSIArrayCount(&chunk->glyphs);
|
|
|
|
|
gOffset = glyphIndex - chunk->glyphIndex;
|
|
|
|
|
if (gOffset > gCount)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"glyph index out of range"];
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (gOffset == 0) // Before first glyph in chunk
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
|
|
|
|
if (chunk->charIndex < charIndex)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index greater than that of next glyph"];
|
|
|
|
|
}
|
|
|
|
|
if (chunkIndex > 0)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *previous;
|
|
|
|
|
unsigned c;
|
|
|
|
|
|
2001-01-12 12:32:32 +00:00
|
|
|
|
previous = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks,
|
2001-01-11 19:09:02 +00:00
|
|
|
|
chunkIndex-1).ptr;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
c = GSIArrayCount(&previous->glyphs);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
c = previous->charIndex
|
2002-02-24 07:39:18 +00:00
|
|
|
|
+ gOffset(GSIArrayItemAtIndex(&previous->glyphs, c));
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (c > charIndex)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index less than that of previous glyph"];
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
else if (c == charIndex)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Inserting with the same character index as the last glyph
|
|
|
|
|
* in the previous chunk - so we should append to that chunk
|
|
|
|
|
* rather than prepending to this one.
|
|
|
|
|
*/
|
|
|
|
|
chunkIndex--;
|
|
|
|
|
chunk = previous;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
gCount = GSIArrayCount(&chunk->glyphs);
|
|
|
|
|
gOffset = glyphIndex - chunk->glyphIndex;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
else if (gOffset == gCount) // After last glyph in chunk
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned c = chunk->charIndex;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (gOffset > 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
c += gOffset(GSIArrayItemAtIndex(&chunk->glyphs, gOffset-1));
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (charIndex < c)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index less than that of previous glyph"];
|
|
|
|
|
}
|
|
|
|
|
if (chunkIndex < chunkCount - 1)
|
|
|
|
|
{
|
|
|
|
|
GSGlyphChunk *next;
|
|
|
|
|
|
2001-01-12 12:32:32 +00:00
|
|
|
|
next = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks,
|
2001-01-11 19:09:02 +00:00
|
|
|
|
chunkIndex+1).ptr;
|
|
|
|
|
if (next->charIndex < charIndex)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index greater than that of next glyph"];
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
else if (next->charIndex == charIndex)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Inserting with the same character index as the first glyph
|
|
|
|
|
* in the next chunk - so we should insert in that chunk
|
|
|
|
|
* rather than appending to this one.
|
|
|
|
|
*/
|
|
|
|
|
chunkIndex++;
|
|
|
|
|
chunk = next;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
gCount = GSIArrayCount(&chunk->glyphs);
|
|
|
|
|
gOffset = glyphIndex - chunk->glyphIndex;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // In middle of chunk somewhere.
|
|
|
|
|
{
|
|
|
|
|
unsigned n;
|
|
|
|
|
unsigned p;
|
|
|
|
|
|
|
|
|
|
p = chunk->charIndex
|
2002-02-24 07:39:18 +00:00
|
|
|
|
+ gOffset(GSIArrayItemAtIndex(&chunk->glyphs, gOffset-1));
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (p > charIndex)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index less than that of previous glyph"];
|
|
|
|
|
}
|
|
|
|
|
n = chunk->charIndex
|
2002-02-24 07:39:18 +00:00
|
|
|
|
+ gOffset(GSIArrayItemAtIndex(&chunk->glyphs, gOffset));
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (n < charIndex)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"insertGlyph:glyphIndex:characterIndex: "
|
|
|
|
|
@"character index greater than that of next glyph"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2001-01-12 12:32:32 +00:00
|
|
|
|
* Shall we add to the chunk or is it big enough already?
|
2001-01-11 19:09:02 +00:00
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (gCount > 4 && gCount == GSIArrayCapacity(&chunk->glyphs))
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
GSGlyphChunk *newChunk = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
unsigned from;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
unsigned pos;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
unsigned splitAt = gCount/2;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
unsigned splitChar;
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
splitChar = gOffset(GSIArrayItemAtIndex(&chunk->glyphs, splitAt));
|
2001-01-15 21:48:18 +00:00
|
|
|
|
while (splitAt > 0 && splitChar
|
2002-02-24 07:39:18 +00:00
|
|
|
|
== gOffset(GSIArrayItemAtIndex(&chunk->glyphs, splitAt-1)))
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
splitAt--;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Arbitrary check that we could make a sane splitup of the
|
|
|
|
|
* chunk. Conceivably we could have every glyph in the
|
|
|
|
|
* chunk set to the same character - which would force us to
|
|
|
|
|
* break our invariant that all glyphs for a particular
|
|
|
|
|
* character lie in the same chunk.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (splitAt <= gCount/4)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"unable to split glyph chunk"];
|
|
|
|
|
}
|
2001-01-12 12:32:32 +00:00
|
|
|
|
/*
|
2001-01-15 21:48:18 +00:00
|
|
|
|
* Ok - split the chunk into two (roughly) equal parts.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
*/
|
2001-01-15 21:48:18 +00:00
|
|
|
|
splitChar
|
2002-02-24 07:39:18 +00:00
|
|
|
|
= gOffset(GSIArrayItemAtIndex(&chunk->glyphs, splitAt));
|
2001-01-22 18:13:43 +00:00
|
|
|
|
newChunk = GSCreateGlyphChunk(chunk->glyphIndex + splitAt,
|
|
|
|
|
chunk->charIndex + splitChar);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
GSIArrayInsertItem(glyphChunks, (GSIArrayItem)(void*)newChunk,
|
|
|
|
|
chunkIndex+1);
|
|
|
|
|
pos = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
from = splitAt;
|
|
|
|
|
while (from < GSIArrayCount(&chunk->glyphs))
|
2001-01-12 12:32:32 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem info;
|
2001-01-12 12:32:32 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2001-01-15 21:48:18 +00:00
|
|
|
|
* Remove attributes from old chunk and add to new.
|
|
|
|
|
* Adjust offset for character index of new chunk.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
info = GSIArrayItemAtIndex(&chunk->glyphs, from);
|
|
|
|
|
gOffset(info) -= splitChar;
|
|
|
|
|
GSIArrayInsertItem(&newChunk->glyphs, info, pos);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
from++;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
pos++;
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
GSIArrayRemoveItemsFromIndex(&chunk->glyphs, splitAt);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
/*
|
|
|
|
|
* And set up so we point at the correct half of the split chunk.
|
|
|
|
|
*/
|
|
|
|
|
if (glyphIndex >= newChunk->glyphIndex)
|
2001-01-12 12:32:32 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
chunkIndex++;
|
|
|
|
|
chunk = newChunk;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
gOffset = glyphIndex - chunk->glyphIndex;
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
gCount = GSIArrayCount(&chunk->glyphs);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-12 12:32:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* Special handling for insertion at the start of a chunk - we
|
|
|
|
|
* need to update the index values for the chunk, and (possibly)
|
|
|
|
|
* the character offsets of every glyph in the chunk.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (gOffset == 0)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2001-01-12 12:32:32 +00:00
|
|
|
|
chunk->glyphIndex = glyphIndex;
|
|
|
|
|
if (chunk->charIndex != charIndex)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
int diff = charIndex - chunk->charIndex;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
2001-01-12 12:32:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* Changing character index of entire chunk.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
for (pos = 0; pos < gCount; pos++)
|
2001-01-12 12:32:32 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(GSIArrayItems(&chunk->glyphs)[pos]) += diff;
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
chunk->charIndex = charIndex;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2001-01-12 12:32:32 +00:00
|
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
/*
|
|
|
|
|
* At last we insert the glyph and its attributes into the chunk.
|
|
|
|
|
*/
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(info) = charIndex - chunk->charIndex;
|
|
|
|
|
gGlyph(info) = aGlyph;
|
|
|
|
|
GSIArrayInsertItem(&chunk->glyphs, info, gOffset);
|
2001-01-11 19:09:02 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now adjust the glyph index for all following chunks so we will
|
|
|
|
|
* still know the index of the first glyph in each chunk.
|
|
|
|
|
*/
|
|
|
|
|
for (pos = chunkIndex+1; pos < chunkCount; pos++)
|
|
|
|
|
{
|
2001-01-12 12:32:32 +00:00
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, pos).ptr;
|
2001-01-11 19:09:02 +00:00
|
|
|
|
chunk->glyphIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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++;
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
_Sane(self);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* Returns the glyph at the specified glyphIndex.<br />
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Causes any gaps (areas where glyphs have been invalidated) before this
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* glyphIndex to be re-filled.<br />
|
|
|
|
|
* Raises an exception if the glyphIndex is out of range.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (NSGlyph) glyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-11 19:09:02 +00:00
|
|
|
|
BOOL flag;
|
|
|
|
|
NSGlyph glyph;
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
glyph = [self glyphAtIndex: glyphIndex isValidIndex: &flag];
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if (flag == NO)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph index out of range"];
|
|
|
|
|
}
|
|
|
|
|
return glyph;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-24 07:39:18 +00:00
|
|
|
|
/**
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* Returns the glyph at the specified glyphIndex.<br />
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Causes any gaps (areas where glyphs have been invalidated) before this
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* glyphIndex to be re-filled.<br />
|
|
|
|
|
* Sets the flag to indicate whether the glyphIndex was found ... if it wasn't
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* the returned glyph is meaningless.
|
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (NSGlyph) glyphAtIndex: (unsigned)glyphIndex
|
|
|
|
|
isValidIndex: (BOOL*)isValidIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#if USE_GLYPHS
|
2002-02-20 19:39:15 +00:00
|
|
|
|
NSGlyph glyph;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
NSString *string = nil;
|
|
|
|
|
unsigned textLength = [_textStorage length];
|
|
|
|
|
|
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (GSIArrayCount((GSIArray)_glyphGaps) > 0
|
2002-10-13 13:50:06 +00:00
|
|
|
|
&& (GSIArrayItemAtIndex((GSIArray)_glyphGaps, 0).ulng) <= glyphIndex)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
unsigned long gap;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
|
|
|
|
string = [_textStorage string];
|
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
while (GSIArrayCount((GSIArray)_glyphGaps) > 0
|
2002-10-13 13:50:06 +00:00
|
|
|
|
&& (gap = GSIArrayItemAtIndex((GSIArray)_glyphGaps, 0).ulng)
|
|
|
|
|
<= glyphIndex)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
unsigned endChar;
|
|
|
|
|
unsigned startChar;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-22 18:13:43 +00:00
|
|
|
|
if (gap == 0)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
startChar = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Locate the glyph that preceeds the gap, and start with the
|
2002-02-20 19:39:15 +00:00
|
|
|
|
* a character one beyond the one that generated that glyph.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
* This guarantees that we won't try to re-generate the
|
|
|
|
|
* preceeding glyph.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* FIXME ... probably too simplistic an algorithm if we have
|
|
|
|
|
* decomposed unicode characters to deal with 0 we should
|
|
|
|
|
* probably skip forward to the next character sequence.
|
2001-01-22 18:13:43 +00:00
|
|
|
|
*/
|
|
|
|
|
_JumpToGlyph(self, gap - 1);
|
|
|
|
|
startChar = _CharIndex(self) + 1;
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (gap == _GlyphEnd(self))
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
while (startChar < endChar)
|
|
|
|
|
{
|
|
|
|
|
unichar c = [string characterAtIndex: startChar];
|
|
|
|
|
|
|
|
|
|
[self insertGlyph: (NSGlyph)c
|
|
|
|
|
atGlyphIndex: gap++
|
|
|
|
|
characterIndex: startChar++];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* We have generated glyphs upto or beyond the gap, so we
|
2002-02-20 19:39:15 +00:00
|
|
|
|
* can remove this gap and any others we have gone past.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
while (GSIArrayCount((GSIArray)_glyphGaps) > 0
|
|
|
|
|
&& GSIArrayItemAtIndex((GSIArray)_glyphGaps, 0).ulng < gap)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex((GSIArray)_glyphGaps, 0);
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (glyphIndex >= _GlyphEnd(self) && _CharEnd(self) < textLength)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned endChar = textLength;
|
|
|
|
|
unsigned startChar = _CharEnd(self);
|
|
|
|
|
unsigned glyphIndex = _GlyphEnd(self);
|
|
|
|
|
|
|
|
|
|
if (string == nil)
|
|
|
|
|
{
|
|
|
|
|
string = [_textStorage string];
|
|
|
|
|
}
|
|
|
|
|
/* FIXME ... should generate glyphs properly here */
|
2002-10-13 13:50:06 +00:00
|
|
|
|
while (startChar < endChar && glyphIndex <= glyphIndex)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
|
|
|
|
unichar c = [string characterAtIndex: startChar];
|
|
|
|
|
|
|
|
|
|
[self insertGlyph: (NSGlyph)c
|
|
|
|
|
atGlyphIndex: glyphIndex++
|
|
|
|
|
characterIndex: startChar++];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
|
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
_Sane(self);
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == YES)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2002-10-13 13:50:06 +00:00
|
|
|
|
*isValidIndex = YES;
|
2002-02-24 07:39:18 +00:00
|
|
|
|
glyph = gGlyph(*_Info(self));
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-10-13 13:50:06 +00:00
|
|
|
|
*isValidIndex = NO;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
glyph = NSNullGlyph;
|
|
|
|
|
}
|
|
|
|
|
#if ALL_CHECKS
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (glyphIndex >= [_textStorage length])
|
2002-02-20 19:39:15 +00:00
|
|
|
|
{
|
|
|
|
|
if (glyph != NSNullGlyph)
|
|
|
|
|
{
|
|
|
|
|
missmatch(_cmd);
|
2002-10-13 13:50:06 +00:00
|
|
|
|
*isValidIndex = NO;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
glyph = NSNullGlyph;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (glyph != (NSGlyph)[[_textStorage string] characterAtIndex:
|
|
|
|
|
glyphIndex])
|
2002-02-20 19:39:15 +00:00
|
|
|
|
{
|
|
|
|
|
missmatch(_cmd);
|
2002-10-13 13:50:06 +00:00
|
|
|
|
*isValidIndex = YES;
|
|
|
|
|
glyph = (NSGlyph)[[_textStorage string] characterAtIndex: glyphIndex];
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#endif
|
|
|
|
|
return glyph;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#else
|
2002-10-13 13:50:06 +00:00
|
|
|
|
return (NSGlyph)[[_textStorage string] characterAtIndex: glyphIndex];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#endif
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* Replaces the glyph at glyphIndex with newGlyph without changing
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* character index or other attributes.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (void) replaceGlyphAtIndex: (unsigned)glyphIndex
|
2000-02-19 00:40:47 +00:00
|
|
|
|
withGlyph: (NSGlyph)newGlyph
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == NO)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph index out of range"];
|
2001-01-11 19:09:02 +00:00
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gGlyph(*_Info(self)) = newGlyph;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* This returns a nul terminated array of glyphs ... so glyphArray
|
|
|
|
|
* should contain space for glyphRange.length+1 glyphs.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (unsigned) getGlyphs: (NSGlyph*)glyphArray
|
|
|
|
|
range: (NSRange)glyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-12 12:32:32 +00:00
|
|
|
|
unsigned packed = 0;
|
|
|
|
|
unsigned toFetch = glyphRange.length;
|
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
if (toFetch > 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Force generation of glyphs to fill range.
|
|
|
|
|
*/
|
|
|
|
|
[self glyphAtIndex: NSMaxRange(glyphRange)-1];
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_JumpToGlyph(self, glyphRange.location);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now return glyphs, excluding those 'not shown'
|
|
|
|
|
*/
|
|
|
|
|
while (toFetch-- > 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem info = *_Info(self);
|
|
|
|
|
|
|
|
|
|
if (gIsNotShown(info) == 0)
|
2001-01-12 12:32:32 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
glyphArray[packed++] = gGlyph(info);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_Step(self); // Move to next glyph.
|
2001-01-12 12:32:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
glyphArray[packed] = 0;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-12 12:32:32 +00:00
|
|
|
|
return packed;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Removes all the glyphs in aRange from the glyph stream, causing all
|
|
|
|
|
* subsequent glyphs to have their index decreased by aRange.length
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) deleteGlyphsInRange: (NSRange)aRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
unsigned chunkStart;
|
|
|
|
|
unsigned chunkEnd;
|
|
|
|
|
unsigned offset;
|
|
|
|
|
unsigned from;
|
|
|
|
|
unsigned pos;
|
|
|
|
|
GSGlyphChunk *chunk;
|
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
if (aRange.length == 0)
|
|
|
|
|
{
|
2001-01-22 18:13:43 +00:00
|
|
|
|
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.
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chunkStart = GSChunkForGlyphIndex(glyphChunks, aRange.location);
|
|
|
|
|
chunkEnd = GSChunkForGlyphIndex(glyphChunks, NSMaxRange(aRange)-1);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove all chunks wholy contained in the range.
|
|
|
|
|
*/
|
|
|
|
|
while (chunkEnd - chunkStart > 1)
|
|
|
|
|
{
|
|
|
|
|
chunkEnd--;
|
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkEnd).ptr;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(glyphChunks, chunkEnd);
|
|
|
|
|
GSDestroyGlyphChunk(chunk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2002-02-20 08:52:39 +00:00
|
|
|
|
* Get start chunk and remove any glyphs in specified range.
|
2001-01-15 21:48:18 +00:00
|
|
|
|
*/
|
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkStart).ptr;
|
|
|
|
|
if (chunkStart == chunkEnd)
|
|
|
|
|
{
|
|
|
|
|
pos = chunk->glyphIndex;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
offset = aRange.location - chunk->glyphIndex;
|
|
|
|
|
if (offset == 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Start chunk is fully enclosed in range - remove it.
|
|
|
|
|
*/
|
|
|
|
|
pos = chunk->glyphIndex;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(glyphChunks, chunkStart);
|
|
|
|
|
GSDestroyGlyphChunk(chunk);
|
|
|
|
|
chunkEnd--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pos = chunk->glyphIndex + offset;
|
|
|
|
|
GSIArrayRemoveItemsFromIndex(&chunk->glyphs, offset);
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkEnd).ptr;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
offset = NSMaxRange(aRange) - chunk->glyphIndex;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
if (chunk->glyphIndex < aRange.location)
|
|
|
|
|
{
|
|
|
|
|
from = aRange.location - chunk->glyphIndex;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
from = 0;
|
|
|
|
|
}
|
|
|
|
|
chunk->glyphIndex = pos;
|
|
|
|
|
while (offset-- > from)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(&chunk->glyphs, from);
|
|
|
|
|
}
|
|
|
|
|
while (++chunkEnd < GSIArrayCount(glyphChunks))
|
|
|
|
|
{
|
|
|
|
|
chunk = (GSGlyphChunk*)GSIArrayItemAtIndex(glyphChunks, chunkEnd).ptr;
|
|
|
|
|
chunk->glyphIndex -= aRange.length;
|
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
pos++; // Not modified by deletion
|
2001-01-22 18:13:43 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
else if (val <= NSMaxRange(aRange))
|
2001-01-22 18:13:43 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Gap is within (or immediately after) the deleted area ...
|
|
|
|
|
* we set it to the end of the deleted area, or remove it if
|
|
|
|
|
* there is already a gap at that location.
|
|
|
|
|
*/
|
|
|
|
|
if (pos > 0
|
|
|
|
|
&& GSIArrayItemAtIndex((GSIArray)_glyphGaps, pos-1).ulng
|
|
|
|
|
== aRange.location)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex((GSIArray)_glyphGaps, pos);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSIArraySetItemAtIndex((GSIArray)_glyphGaps,
|
|
|
|
|
(GSIArrayItem)aRange.location, pos);
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
2001-01-22 18:13:43 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Gap is beyond deleted area ... simply adjust downwards.
|
|
|
|
|
*/
|
2001-01-22 18:13:43 +00:00
|
|
|
|
val -= aRange.length;
|
2002-02-20 08:52:39 +00:00
|
|
|
|
GSIArraySetItemAtIndex((GSIArray)_glyphGaps,
|
|
|
|
|
(GSIArrayItem)val, pos);
|
2001-01-22 18:13:43 +00:00
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
_Sane(self);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns the number of glyphs in the glyph stream ... causing generation
|
|
|
|
|
* of new glyphs to fill gaps and to extend the stream until all characters
|
|
|
|
|
* in the text storage have had glyphs generated.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (unsigned) numberOfGlyphs
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
unsigned result;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#if USE_GLYPHS
|
2002-02-20 19:39:15 +00:00
|
|
|
|
BOOL valid;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
/*
|
|
|
|
|
* Force generation of all glyphs.
|
|
|
|
|
*/
|
|
|
|
|
[self glyphAtIndex: 0x7fffffff isValidIndex: &valid];
|
|
|
|
|
result = _GlyphEnd(self);
|
|
|
|
|
#if ALL_CHECKS
|
|
|
|
|
if (result != [_textStorage length])
|
|
|
|
|
{
|
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
result = [_textStorage length];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#endif
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#else
|
2002-02-20 19:39:15 +00:00
|
|
|
|
result = [_textStorage length];
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#endif
|
2002-02-20 19:39:15 +00:00
|
|
|
|
return result;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Sets the glyph at glyphIndex to correspond to the character at charIndex.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setCharacterIndex: (unsigned)charIndex
|
|
|
|
|
forGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
int diff;
|
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[self glyphAtIndex: glyphIndex];
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_JumpToGlyph(self, glyphIndex);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
diff = charIndex - _CharIndex(self);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
if (diff == 0)
|
|
|
|
|
{
|
|
|
|
|
return; // Already set - nothing to do.
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_Back(self) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (charIndex != 0)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"set non-zero index for initial glyph"];
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_CharIndex(self) > charIndex)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"set index lower than preceeding glyph"];
|
|
|
|
|
}
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_Step(self);
|
|
|
|
|
if (_Step(self) == YES && charIndex > _CharIndex(self))
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"set index higher than following glyph"];
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_Back(self);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
/*
|
|
|
|
|
* If this is the start of a chunk, we adjust the character position
|
|
|
|
|
* for the chunk as a whole, then fix each glyph in turn. Otherwise
|
|
|
|
|
* we simply adjust the glyph concerned.
|
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (_glyphOffset == 0)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-20 08:58:25 +00:00
|
|
|
|
GSGlyphChunk *chunk = (GSGlyphChunk*)_currentGlyphs;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2001-01-20 08:58:25 +00:00
|
|
|
|
diff = charIndex - _CharIndex(self);
|
|
|
|
|
chunk->charIndex += diff;
|
|
|
|
|
while (_Step(self) == YES && (GSGlyphChunk*)_currentGlyphs == chunk)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(*_Info(self)) += diff;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gOffset(*_Info(self)) += diff;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-22 18:57:06 +00:00
|
|
|
|
_Sane(self);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns the character index for the specified glyphIndex.<br />
|
|
|
|
|
* If there are invalidated ranges (gaps) in the glyph stream before
|
|
|
|
|
* glyphIndex, this will cause glyph generation to fill them.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (unsigned) characterIndexForGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
unsigned result;
|
|
|
|
|
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#if USE_GLYPHS
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2001-01-26 17:01:43 +00:00
|
|
|
|
BOOL exists;
|
|
|
|
|
|
|
|
|
|
[self glyphAtIndex: glyphIndex isValidIndex: &exists];
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (exists == YES)
|
|
|
|
|
{
|
|
|
|
|
_JumpToGlyph(self, glyphIndex);
|
|
|
|
|
result = _CharIndex(self);
|
|
|
|
|
}
|
|
|
|
|
else
|
2001-01-26 17:01:43 +00:00
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* As a special case, the glyph index just beyond the end of
|
|
|
|
|
* the glyph stream is known to map to the character index just
|
|
|
|
|
* beyond the end of the text.
|
|
|
|
|
*/
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (glyphIndex == _GlyphEnd(self))
|
2001-01-26 17:01:43 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
result = [_textStorage length];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph index out of range"];
|
2001-01-26 17:01:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result = _CharIndex(self);
|
|
|
|
|
}
|
|
|
|
|
#if ALL_CHECKS
|
|
|
|
|
if (result != glyphIndex)
|
|
|
|
|
{
|
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
result = glyphIndex;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
_GLog(self,_cmd);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#else
|
2002-02-20 19:39:15 +00:00
|
|
|
|
result = glyphIndex;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#endif
|
2002-02-20 19:39:15 +00:00
|
|
|
|
return result;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns the range of characters that generated the glyphs in glyphRange.
|
|
|
|
|
* Sets actualGlyphRange (if non-nul) to the range of glyphs generated by
|
|
|
|
|
* those characters.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRange) characterRangeForGlyphRange: (NSRange)glyphRange
|
|
|
|
|
actualGlyphRange: (NSRange*)actualGlyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-15 21:48:18 +00:00
|
|
|
|
NSRange cRange;
|
|
|
|
|
NSRange gRange = glyphRange;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#if USE_GLYPHS
|
2001-01-26 17:01:43 +00:00
|
|
|
|
unsigned cEnd;
|
|
|
|
|
BOOL exists;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-26 17:01:43 +00:00
|
|
|
|
/*
|
|
|
|
|
* Force generation of glyphs to fill gaps.
|
|
|
|
|
*/
|
|
|
|
|
[self glyphAtIndex: NSMaxRange(glyphRange)
|
|
|
|
|
isValidIndex: &exists];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate character index of location immediately beyond last glyph in range.
|
|
|
|
|
*/
|
|
|
|
|
if (exists == NO)
|
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
if (NSMaxRange(glyphRange) > _GlyphEnd(self))
|
2001-01-26 17:01:43 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph range too large"];
|
|
|
|
|
}
|
|
|
|
|
cEnd = [_textStorage length];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_JumpToGlyph(self, NSMaxRange(glyphRange));
|
|
|
|
|
cEnd = _CharIndex(self);
|
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate the first glyph and step backwards to the earliest glyph with
|
|
|
|
|
* the same character index.
|
|
|
|
|
*/
|
2001-01-20 08:58:25 +00:00
|
|
|
|
_JumpToGlyph(self, glyphRange.location);
|
|
|
|
|
cRange.location = _CharIndex(self);
|
2001-01-26 17:01:43 +00:00
|
|
|
|
cRange.length = cEnd - cRange.location;
|
2001-01-20 08:58:25 +00:00
|
|
|
|
while (_Back(self) == YES && _CharIndex(self) == cRange.location)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
gRange.location--;
|
|
|
|
|
gRange.length++;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#if ALL_CHECKS
|
|
|
|
|
if (NSEqualRanges(cRange, glyphRange) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
cRange = glyphRange;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (NSEqualRanges(gRange, glyphRange) == NO)
|
|
|
|
|
{
|
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
gRange = glyphRange;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#else
|
2000-11-03 00:21:29 +00:00
|
|
|
|
// Currently gyphIndex is the same as character index
|
2002-02-20 19:39:15 +00:00
|
|
|
|
gRange = glyphRange;
|
|
|
|
|
cRange = glyphRange;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#endif
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (actualGlyphRange != 0)
|
|
|
|
|
{
|
|
|
|
|
*actualGlyphRange = gRange;
|
|
|
|
|
}
|
|
|
|
|
return cRange;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns the range of glyphs that are generated from the characters in
|
|
|
|
|
* charRange.
|
|
|
|
|
* Sets actualCharRange (if non-nul) to the full range of characters which
|
|
|
|
|
* generated those glyphs.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRange) glyphRangeForCharacterRange: (NSRange)charRange
|
|
|
|
|
actualCharacterRange: (NSRange*)actualCharRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
NSRange gRange;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#if USE_GLYPHS
|
2001-01-17 22:11:52 +00:00
|
|
|
|
unsigned pos;
|
|
|
|
|
NSRange cRange = charRange;
|
|
|
|
|
unsigned numGlyphs;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
BOOL valid;
|
2001-01-17 22:11:52 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-02-20 19:39:15 +00:00
|
|
|
|
* If the range we have been given begins or ends with a composed
|
|
|
|
|
* character sequence, we must extend it to encompass the entire
|
|
|
|
|
* sequence. We store the actual range in cRange.
|
2001-01-17 22:11:52 +00:00
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (charRange.length > 0)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
NSString *s = [_textStorage string];
|
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
|
|
r = [s rangeOfComposedCharacterSequenceAtIndex: cRange.location];
|
|
|
|
|
if (r.length > 0)
|
|
|
|
|
{
|
|
|
|
|
cRange.length += (cRange.location - r.location);
|
|
|
|
|
cRange.location = r.location;
|
|
|
|
|
}
|
|
|
|
|
if (NSMaxRange(charRange) > NSMaxRange(r))
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
pos = NSMaxRange(charRange) - 1;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
r = [s rangeOfComposedCharacterSequenceAtIndex: pos];
|
|
|
|
|
if (r.length > 0)
|
|
|
|
|
{
|
|
|
|
|
cRange.length += r.length - 1;
|
|
|
|
|
}
|
2002-02-20 08:52:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
|
|
|
|
|
_GLog(self,_cmd);
|
|
|
|
|
// Force generation of glyphs.
|
|
|
|
|
[self glyphAtIndex: NSMaxRange(cRange) - 1 isValidIndex: &valid];
|
2001-01-17 22:11:52 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-02-20 19:39:15 +00:00
|
|
|
|
* Locate the first glyph corresponding to the start character.
|
|
|
|
|
* If it doesn't exist, we either have a zero length range at the end.
|
|
|
|
|
* or we must return a not found marker.
|
2001-01-17 22:11:52 +00:00
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (_JumpToChar(self, charRange.location) == NO)
|
2001-01-17 22:11:52 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (charRange.location == _CharEnd(self))
|
|
|
|
|
{
|
|
|
|
|
cRange = NSMakeRange(charRange.location, 0);
|
|
|
|
|
gRange = NSMakeRange(numGlyphs, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cRange = NSMakeRange(NSNotFound, 0);
|
|
|
|
|
gRange = NSMakeRange(NSNotFound, 0);
|
|
|
|
|
}
|
2001-01-17 22:11:52 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
else
|
2001-01-17 22:11:52 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
gRange.location = _GlyphIndex(self);
|
|
|
|
|
|
2001-01-17 22:11:52 +00:00
|
|
|
|
/*
|
2002-02-20 19:39:15 +00:00
|
|
|
|
* Adjust start character if necessary. The glyph may have a lower
|
|
|
|
|
* index if the start char is part of a composed character sequence.
|
2001-01-17 22:11:52 +00:00
|
|
|
|
*/
|
2002-02-20 19:39:15 +00:00
|
|
|
|
pos = _CharIndex(self);
|
|
|
|
|
if (pos < cRange.location)
|
|
|
|
|
{
|
|
|
|
|
cRange.length += (cRange.location - pos);
|
|
|
|
|
cRange.location = pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (charRange.length == 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* For a zero length range, we don't need to locate an end character.
|
|
|
|
|
*/
|
|
|
|
|
cRange.length = 0; // May have been lengthened above.
|
|
|
|
|
gRange.length = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (NSMaxRange(charRange) == [_textStorage length])
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Special case - range extends to end of text storage.
|
|
|
|
|
*/
|
|
|
|
|
gRange.length = numGlyphs - gRange.location;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Locate the glyph immediately beyond the range,
|
|
|
|
|
* and calculate the length of the range from that.
|
|
|
|
|
*/
|
|
|
|
|
_JumpToChar(self, NSMaxRange(charRange));
|
|
|
|
|
pos = _GlyphIndex(self);
|
|
|
|
|
gRange.length = pos - gRange.location;
|
|
|
|
|
pos = _CharIndex(self);
|
|
|
|
|
cRange.length = pos - cRange.location;
|
|
|
|
|
}
|
2001-01-17 22:11:52 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#if ALL_CHECKS
|
|
|
|
|
if (NSEqualRanges(gRange, charRange) == NO)
|
2001-01-17 22:11:52 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
gRange = charRange;
|
2001-01-17 22:11:52 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
if (NSEqualRanges(cRange, charRange) == NO)
|
2001-01-17 22:11:52 +00:00
|
|
|
|
{
|
2002-02-20 19:39:15 +00:00
|
|
|
|
missmatch(_cmd);
|
|
|
|
|
cRange = charRange;
|
2001-01-17 22:11:52 +00:00
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
#endif
|
2001-01-17 22:11:52 +00:00
|
|
|
|
if (actualCharRange != 0)
|
|
|
|
|
{
|
|
|
|
|
*actualCharRange = cRange;
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#else
|
2000-11-03 00:21:29 +00:00
|
|
|
|
// Currently gyphIndex is the same as character index
|
|
|
|
|
if (actualCharRange != NULL)
|
2002-02-20 08:52:39 +00:00
|
|
|
|
{
|
|
|
|
|
*actualCharRange = charRange;
|
|
|
|
|
}
|
2002-02-20 19:39:15 +00:00
|
|
|
|
gRange = charRange;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
#endif
|
2002-02-20 19:39:15 +00:00
|
|
|
|
return gRange;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* This method modifies the attributes of an existing glyph at glyphIndex.
|
|
|
|
|
* It only deals with the existing attribute types ... if you subclass and
|
|
|
|
|
* add new attributed, you must replace this method with one which can
|
|
|
|
|
* store your new attributes.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (void) setIntAttribute: (int)attributeTag
|
2000-02-19 00:40:47 +00:00
|
|
|
|
value: (int)anInt
|
|
|
|
|
forGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem info;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph index out of range"];
|
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
info = *_Info(self);
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (attributeTag == GSGlyphDrawsOutsideLineFragment)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (anInt == 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gDrawsOutside(info) = 0;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gDrawsOutside(info) = 1;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphIsNotShown)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (anInt == 0)
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gIsNotShown(info) = 0;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gIsNotShown(info) = 1;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphGeneration)
|
2001-01-19 23:22:16 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gGeneration(info) = anInt;
|
2001-01-19 23:22:16 +00:00
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphInscription)
|
2001-01-19 23:22:16 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
gInscription(info) = anInt;
|
2001-01-19 23:22:16 +00:00
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
*_Info(self) = info;
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-10-13 13:50:06 +00:00
|
|
|
|
* Returns the value for the attributeTag at the glyphIndex.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (int) intAttribute: (int)attributeTag
|
2000-02-19 00:40:47 +00:00
|
|
|
|
forGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
GSIArrayItem info;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
2002-02-20 19:39:15 +00:00
|
|
|
|
_GLog(self,_cmd);
|
2001-01-20 08:58:25 +00:00
|
|
|
|
if (_JumpToGlyph(self, glyphIndex) == NO)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSRangeException
|
|
|
|
|
format: @"glyph index out of range"];
|
|
|
|
|
}
|
2002-02-24 07:39:18 +00:00
|
|
|
|
info = *_Info(self);
|
2001-01-17 22:11:52 +00:00
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
if (attributeTag == GSGlyphDrawsOutsideLineFragment)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
if (gDrawsOutside(info) == 0)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphIsNotShown)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
if (gIsNotShown(info) == 0)
|
2001-01-15 21:48:18 +00:00
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2001-01-19 23:22:16 +00:00
|
|
|
|
return 1;
|
2001-01-15 21:48:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphGeneration)
|
2001-01-19 23:22:16 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
return gGeneration(info);
|
2001-01-19 23:22:16 +00:00
|
|
|
|
}
|
2002-10-13 13:50:06 +00:00
|
|
|
|
else if (attributeTag == GSGlyphInscription)
|
2001-01-19 23:22:16 +00:00
|
|
|
|
{
|
2002-02-24 07:39:18 +00:00
|
|
|
|
return gInscription(info);
|
2001-01-19 23:22:16 +00:00
|
|
|
|
}
|
2001-01-15 21:48:18 +00:00
|
|
|
|
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setTextContainer: (NSTextContainer*)aTextContainer
|
|
|
|
|
forGlyphRange: (NSRange)glyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (NSRange) glyphRangeForTextContainer: (NSTextContainer*)container
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/**
|
2002-02-24 07:39:18 +00:00
|
|
|
|
* Returns the text container in which the glyph at glyphIndex is laid.
|
|
|
|
|
* If effectiveRange is non-nul, returns the range of all glyphs in the
|
|
|
|
|
* container.
|
2002-02-20 08:52:39 +00:00
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSTextContainer*) textContainerForGlyphAtIndex: (unsigned)glyphIndex
|
|
|
|
|
effectiveRange: (NSRange*)effectiveRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-20 08:52:39 +00:00
|
|
|
|
/* FIXME ... needs to be properly implemented */
|
|
|
|
|
if (effectiveRange != 0)
|
|
|
|
|
{
|
|
|
|
|
*effectiveRange = NSMakeRange(0, [self numberOfGlyphs]);
|
|
|
|
|
}
|
|
|
|
|
if ([_textContainers count] == 0)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return [_textContainers objectAtIndex: 0];
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setLineFragmentRect: (NSRect)fragmentRect
|
|
|
|
|
forGlyphRange: (NSRange)glyphRange
|
|
|
|
|
usedRect: (NSRect)usedRect
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect) lineFragmentRectForGlyphAtIndex: (unsigned)glyphIndex
|
2002-10-13 13:50:06 +00:00
|
|
|
|
effectiveRange: (NSRange*)effectiveGlyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSZeroRect;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect) lineFragmentUsedRectForGlyphAtIndex: (unsigned)glyphIndex
|
2002-10-13 13:50:06 +00:00
|
|
|
|
effectiveRange: (NSRange*)effectiveGlyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSZeroRect;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
- (void) setExtraLineFragmentRect: (NSRect)fragmentRect
|
2000-02-19 00:40:47 +00:00
|
|
|
|
usedRect: (NSRect)usedRect
|
2002-10-13 13:50:06 +00:00
|
|
|
|
textContainer: (NSTextContainer*)container
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-10-13 13:50:06 +00:00
|
|
|
|
_extraLineFragmentRect = fragmentRect;
|
2002-02-27 23:35:48 +00:00
|
|
|
|
_extraLineFragmentUsedRect = usedRect;
|
2002-10-13 13:50:06 +00:00
|
|
|
|
_extraLineFragmentContainer = container;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect) extraLineFragmentRect
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-27 23:35:48 +00:00
|
|
|
|
return _extraLineFragmentRect;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect) extraLineFragmentUsedRect
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-27 23:35:48 +00:00
|
|
|
|
return _extraLineFragmentUsedRect;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSTextContainer*) extraLineFragmentTextContainer
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-27 23:35:48 +00:00
|
|
|
|
return _extraLineFragmentContainer;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-10-12 23:01:43 +00:00
|
|
|
|
- (NSRect)usedRectForTextContainer:(NSTextContainer *)container
|
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
2000-10-12 23:01:43 +00:00
|
|
|
|
return NSZeroRect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setAttachmentSize:(NSSize)attachmentSize
|
|
|
|
|
forGlyphRange:(NSRange)glyphRange
|
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
2000-10-12 23:01:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setDrawsOutsideLineFragment: (BOOL)flag
|
|
|
|
|
forGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-11 19:09:02 +00:00
|
|
|
|
[self setIntAttribute: GSGlyphDrawsOutsideLineFragment
|
2002-02-06 16:20:43 +00:00
|
|
|
|
value: 1
|
2001-01-11 19:09:02 +00:00
|
|
|
|
forGlyphAtIndex: glyphIndex];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) drawsOutsideLineFragmentForGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if ([self intAttribute: GSGlyphDrawsOutsideLineFragment
|
2002-02-06 16:20:43 +00:00
|
|
|
|
forGlyphAtIndex: glyphIndex] == 1)
|
2001-01-11 19:09:02 +00:00
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
|
|
|
|
- (void) setLocation: (NSPoint)location
|
2000-02-19 00:40:47 +00:00
|
|
|
|
forStartOfGlyphRange: (NSRange)glyphRange
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSPoint) locationForGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSZeroPoint;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRange) rangeOfNominallySpacedGlyphsContainingIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect*) rectArrayForCharacterRange: (NSRange)charRange
|
2002-10-13 13:50:06 +00:00
|
|
|
|
withinSelectedCharacterRange: (NSRange)selCharRange
|
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
2000-02-19 00:40:47 +00:00
|
|
|
|
rectCount: (unsigned*)rectCount
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
2000-10-12 23:01:43 +00:00
|
|
|
|
return NULL;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect*) rectArrayForGlyphRange: (NSRange)glyphRange
|
2002-10-13 13:50:06 +00:00
|
|
|
|
withinSelectedGlyphRange: (NSRange)selGlyphRange
|
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
2000-02-19 00:40:47 +00:00
|
|
|
|
rectCount: (unsigned*)rectCount
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
return _cachedRectArray;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRect) boundingRectForGlyphRange: (NSRange)glyphRange
|
|
|
|
|
inTextContainer: (NSTextContainer*)aTextContainer
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSZeroRect;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
|
|
|
|
- (NSRange) glyphRangeForBoundingRect: (NSRect)bounds
|
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSMakeRange(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSRange) glyphRangeForBoundingRectWithoutAdditionalLayout: (NSRect)bounds
|
2002-10-13 13:50:06 +00:00
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NSMakeRange(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-13 13:50:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* Not implemented.
|
|
|
|
|
*/
|
|
|
|
|
- (unsigned) glyphIndexForPoint: (NSPoint)point
|
|
|
|
|
inTextContainer: (NSTextContainer*)container
|
2000-02-19 00:40:47 +00:00
|
|
|
|
fractionOfDistanceThroughGlyph: (float*)partialFraction
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-07 00:57:23 +00:00
|
|
|
|
- (unsigned) glyphIndexForPoint: (NSPoint)aPoint
|
|
|
|
|
inTextContainer: (NSTextContainer *)aTextContainer
|
2000-10-12 23:01:43 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
2000-10-12 23:01:43 +00:00
|
|
|
|
return [self glyphIndexForPoint: aPoint
|
|
|
|
|
inTextContainer: aTextContainer
|
|
|
|
|
fractionOfDistanceThroughGlyph: NULL];
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setNotShownAttribute: (BOOL)flag
|
|
|
|
|
forGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-11 19:09:02 +00:00
|
|
|
|
[self setIntAttribute: GSGlyphIsNotShown
|
2002-02-06 16:20:43 +00:00
|
|
|
|
value: 1
|
2001-01-11 19:09:02 +00:00
|
|
|
|
forGlyphAtIndex: glyphIndex];
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) notShownAttributeForGlyphAtIndex: (unsigned)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-01-11 19:09:02 +00:00
|
|
|
|
if ([self intAttribute: GSGlyphIsNotShown forGlyphAtIndex: glyphIndex] == 1)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setShowsInvisibleCharacters: (BOOL)flag
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_showsInvisibleChars = flag;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) showsInvisibleCharacters
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _showsInvisibleChars;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setShowsControlCharacters: (BOOL)flag
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_showsControlChars = flag;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) showsControlCharacters
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _showsControlChars;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setHyphenationFactor: (float)factor
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_hyphenationFactor = factor;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (float) hyphenationFactor
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _hyphenationFactor;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) getFirstUnlaidCharacterIndex: (unsigned*)charIndex
|
|
|
|
|
glyphIndex: (unsigned*)glyphIndex
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
if (charIndex)
|
2002-02-06 16:20:43 +00:00
|
|
|
|
{
|
|
|
|
|
*charIndex = [self firstUnlaidCharacterIndex];
|
|
|
|
|
}
|
|
|
|
|
|
2000-08-27 22:32:29 +00:00
|
|
|
|
if (glyphIndex)
|
2002-02-06 16:20:43 +00:00
|
|
|
|
{
|
|
|
|
|
*glyphIndex = [self firstUnlaidGlyphIndex];
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (unsigned int) firstUnlaidCharacterIndex
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
return _firstUnlaidCharIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (unsigned int) firstUnlaidGlyphIndex
|
|
|
|
|
{
|
|
|
|
|
return _firstUnlaidGlyphIndex;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setUsesScreenFonts: (BOOL)flag
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_usesScreenFonts = flag;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) usesScreenFonts
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _usesScreenFonts;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSFont*) substituteFontForFont: (NSFont*)originalFont
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-08-27 22:32:29 +00:00
|
|
|
|
NSFont *replaceFont;
|
|
|
|
|
|
2002-02-06 16:20:43 +00:00
|
|
|
|
if (! _usesScreenFonts)
|
|
|
|
|
{
|
|
|
|
|
return originalFont;
|
|
|
|
|
}
|
2000-08-27 22:32:29 +00:00
|
|
|
|
|
2000-09-30 23:12:42 +00:00
|
|
|
|
// FIXME: Should check if any NSTextView is scaled or rotated
|
2000-08-27 22:32:29 +00:00
|
|
|
|
replaceFont = [originalFont screenFont];
|
|
|
|
|
|
|
|
|
|
if (replaceFont != nil)
|
2002-02-06 16:20:43 +00:00
|
|
|
|
{
|
|
|
|
|
return replaceFont;
|
|
|
|
|
}
|
2000-08-27 22:32:29 +00:00
|
|
|
|
else
|
2002-02-06 16:20:43 +00:00
|
|
|
|
{
|
|
|
|
|
return originalFont;
|
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSView*) rulerAccessoryViewForTextView: (NSTextView*)aTextView
|
|
|
|
|
paragraphStyle: (NSParagraphStyle*)paragraphStyle
|
|
|
|
|
ruler: (NSRulerView*)aRulerView
|
1999-07-15 17:32:38 +00:00
|
|
|
|
enabled: (BOOL)flag
|
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSArray*) rulerMarkersForTextView: (NSTextView*)aTextView
|
|
|
|
|
paragraphStyle: (NSParagraphStyle*)paragraphStyle
|
|
|
|
|
ruler: (NSRulerView*)aRulerView
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2001-11-24 15:50:54 +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];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return markers;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:29:51 +00:00
|
|
|
|
/*
|
|
|
|
|
* Managing the responder chain
|
|
|
|
|
*/
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (BOOL) layoutManagerOwnsFirstResponderInWindow: (NSWindow*)aWindow
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2000-12-21 17:29:51 +00:00
|
|
|
|
id firstResponder = [aWindow firstResponder];
|
|
|
|
|
|
|
|
|
|
if (_textContainersCount == 1)
|
|
|
|
|
{
|
|
|
|
|
if (_firstTextView == firstResponder)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < _textContainersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
id tv = [[_textContainers objectAtIndex: i] textView];
|
|
|
|
|
|
|
|
|
|
if (tv == firstResponder)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-15 17:32:38 +00:00
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSTextView*) firstTextView
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-11 02:01:29 +00:00
|
|
|
|
return _firstTextView;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSTextView*) textViewForBeginningOfSelection
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-11 02:01:29 +00:00
|
|
|
|
return nil;
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) drawBackgroundForGlyphRange: (NSRange)glyphRange
|
|
|
|
|
atPoint: (NSPoint)containerOrigin
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 19:21:29 +00:00
|
|
|
|
NSTextContainer *aTextContainer;
|
2002-09-11 20:26:27 +00:00
|
|
|
|
NSRect rect;
|
|
|
|
|
|
2002-02-06 19:21:29 +00:00
|
|
|
|
aTextContainer = [self textContainerForGlyphAtIndex: glyphRange.location
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
|
|
|
|
|
[[[aTextContainer textView] backgroundColor] set];
|
|
|
|
|
|
2002-09-11 20:26:27 +00:00
|
|
|
|
rect = [self boundingRectForGlyphRange: glyphRange
|
|
|
|
|
inTextContainer: aTextContainer];
|
|
|
|
|
rect.origin.x += containerOrigin.x;
|
|
|
|
|
rect.origin.x += containerOrigin.y;
|
|
|
|
|
NSRectFill (rect);
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) drawGlyphsForGlyphRange: (NSRange)glyphRange
|
|
|
|
|
atPoint: (NSPoint)containerOrigin
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) drawUnderlineForGlyphRange: (NSRange)glyphRange
|
|
|
|
|
underlineType: (int)underlineType
|
|
|
|
|
baselineOffset: (float)baselineOffset
|
|
|
|
|
lineFragmentRect: (NSRect)lineRect
|
|
|
|
|
lineFragmentGlyphRange: (NSRange)lineGlyphRange
|
|
|
|
|
containerOrigin: (NSPoint)containerOrigin
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) underlineGlyphRange: (NSRange)glyphRange
|
|
|
|
|
underlineType: (int)underlineType
|
|
|
|
|
lineFragmentRect: (NSRect)lineRect
|
|
|
|
|
lineFragmentGlyphRange: (NSRange)lineGlyphRange
|
|
|
|
|
containerOrigin: (NSPoint)containerOrigin
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
2002-02-06 16:20:43 +00:00
|
|
|
|
/* TODO */
|
1999-07-15 17:32:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) setDelegate: (id)aDelegate
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
_delegate = aDelegate;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (id) delegate
|
1999-07-15 17:32:38 +00:00
|
|
|
|
{
|
|
|
|
|
return _delegate;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-14 11:29:48 +00:00
|
|
|
|
- (unsigned) _charIndexForInsertionPointMovingFromY: (float)position
|
|
|
|
|
bestX: (float)wanted
|
|
|
|
|
up: (BOOL)upFlag
|
|
|
|
|
textContainer: (NSTextContainer *)tc
|
|
|
|
|
{
|
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1999-07-15 17:32:38 +00:00
|
|
|
|
@end /* NSLayoutManager */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
/* The methods laid out here are not correct, however the code they
|
2000-12-16 20:17:54 +00:00
|
|
|
|
contain for the most part is. Therefore, my country and a handsome
|
|
|
|
|
gift of Ghiradelli chocolate to he who puts all the pieces together :) */
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-11-03 00:21:29 +00:00
|
|
|
|
/*
|
2000-12-16 20:17:54 +00:00
|
|
|
|
* A little utility function to determine the range of characters in a
|
|
|
|
|
* scanner that are present in a specified character set. */
|
2000-11-03 00:21:29 +00:00
|
|
|
|
static inline NSRange
|
2000-12-16 20:17:54 +00:00
|
|
|
|
scanRange (NSScanner *scanner, NSCharacterSet* aSet)
|
2000-11-03 00:21:29 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned start = [scanner scanLocation];
|
|
|
|
|
unsigned end = start;
|
|
|
|
|
|
2000-12-16 20:17:54 +00:00
|
|
|
|
if ([scanner scanCharactersFromSet: aSet intoString: 0] == YES)
|
2000-11-03 00:21:29 +00:00
|
|
|
|
{
|
|
|
|
|
end = [scanner scanLocation];
|
|
|
|
|
}
|
2000-12-16 20:17:54 +00:00
|
|
|
|
return NSMakeRange (start, end - start);
|
2000-11-03 00:21:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
@implementation NSLayoutManager (Private)
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (int) _rebuildLayoutForTextContainer: (NSTextContainer*)aContainer
|
|
|
|
|
startingAtGlyphIndex: (int)glyphIndex
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
|
|
|
|
NSSize cSize = [aContainer containerSize];
|
|
|
|
|
float i = 0.0;
|
1999-09-07 08:59:35 +00:00
|
|
|
|
NSMutableArray *lineStarts = [NSMutableArray new];
|
|
|
|
|
NSMutableArray *lineEnds = [NSMutableArray new];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
int indexToAdd;
|
2000-02-19 00:40:47 +00:00
|
|
|
|
NSScanner *lineScanner;
|
|
|
|
|
NSScanner *paragraphScanner;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
BOOL lastLineForContainerReached = NO;
|
|
|
|
|
int previousScanLocation;
|
1999-09-07 08:59:35 +00:00
|
|
|
|
int previousParagraphLocation;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
int endScanLocation;
|
|
|
|
|
int startIndex;
|
|
|
|
|
NSRect firstProposedRect;
|
|
|
|
|
NSRect secondProposedRect;
|
2000-02-19 00:40:47 +00:00
|
|
|
|
NSCharacterSet *selectionParagraphGranularitySet = [NSCharacterSet characterSetWithCharactersInString: @"\n"];
|
|
|
|
|
NSCharacterSet *selectionWordGranularitySet = [NSCharacterSet characterSetWithCharactersInString: @" "];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
NSCharacterSet *invSelectionWordGranularitySet = [selectionWordGranularitySet invertedSet];
|
|
|
|
|
NSCharacterSet *invSelectionParagraphGranularitySet = [selectionParagraphGranularitySet invertedSet];
|
|
|
|
|
NSRange paragraphRange;
|
|
|
|
|
NSRange leadingSpacesRange;
|
|
|
|
|
NSRange currentStringRange;
|
|
|
|
|
NSRange trailingSpacesRange;
|
|
|
|
|
NSRange leadingNlRange;
|
1999-09-07 08:59:35 +00:00
|
|
|
|
NSRange trailingNlRange;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
NSSize lSize;
|
|
|
|
|
float lineWidth = 0.0;
|
|
|
|
|
float ourLines = 0.0;
|
1999-09-07 08:59:35 +00:00
|
|
|
|
int beginLineIndex = 0;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
paragraphScanner = [NSScanner scannerWithString: [_textStorage string]];
|
|
|
|
|
[paragraphScanner setCharactersToBeSkipped: nil];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[paragraphScanner setScanLocation: startIndex];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
NSLog(@"length of textStorage: %d", [[_textStorage string] length]);
|
|
|
|
|
|
|
|
|
|
// NSLog(@"buffer: %@", [_textStorage string]);
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
1999-08-19 23:18:25 +00:00
|
|
|
|
while (![paragraphScanner isAtEnd])
|
|
|
|
|
{
|
1999-09-07 08:59:35 +00:00
|
|
|
|
previousParagraphLocation = [paragraphScanner scanLocation];
|
|
|
|
|
beginLineIndex = previousParagraphLocation;
|
|
|
|
|
lineWidth = 0.0;
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
leadingNlRange
|
|
|
|
|
= scanRange(paragraphScanner, selectionParagraphGranularitySet);
|
|
|
|
|
paragraphRange
|
|
|
|
|
= scanRange(paragraphScanner, invSelectionParagraphGranularitySet);
|
|
|
|
|
trailingNlRange
|
|
|
|
|
= scanRange(paragraphScanner, selectionParagraphGranularitySet);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
// NSLog(@"leadingNlRange: (%d, %d)", leadingNlRange.location, leadingNlRange.length);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
// if (leadingNlRange.length)
|
|
|
|
|
// paragraphRange = NSUnionRange (leadingNlRange,paragraphRange);
|
|
|
|
|
// if (trailingNlRange.length)
|
|
|
|
|
// paragraphRange = NSUnionRange (trailingNlRange,paragraphRange);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
NSLog(@"paragraphRange: (%d, %d)", paragraphRange.location, paragraphRange.length);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
lineScanner = [NSScanner scannerWithString:
|
|
|
|
|
[[_textStorage string] substringWithRange: paragraphRange]];
|
|
|
|
|
[lineScanner setCharactersToBeSkipped: nil];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
while (![lineScanner isAtEnd])
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
|
|
|
|
previousScanLocation = [lineScanner scanLocation];
|
1999-09-07 08:59:35 +00:00
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
// snack next word
|
2000-02-19 00:40:47 +00:00
|
|
|
|
leadingSpacesRange
|
|
|
|
|
= scanRange(lineScanner, selectionWordGranularitySet);
|
|
|
|
|
currentStringRange
|
|
|
|
|
= scanRange(lineScanner, invSelectionWordGranularitySet);
|
|
|
|
|
trailingSpacesRange
|
|
|
|
|
= scanRange(lineScanner, selectionWordGranularitySet);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
if (leadingSpacesRange.length)
|
2000-02-19 00:40:47 +00:00
|
|
|
|
currentStringRange = NSUnionRange(leadingSpacesRange,currentStringRange);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
if (trailingSpacesRange.length)
|
2000-02-19 00:40:47 +00:00
|
|
|
|
currentStringRange = NSUnionRange(trailingSpacesRange,currentStringRange);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
lSize = [_textStorage sizeRange: currentStringRange];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
// lSize = [_textStorage sizeRange:
|
1999-09-07 08:59:35 +00:00
|
|
|
|
//NSMakeRange(currentStringRange.location+paragraphRange.location+startIndex,
|
|
|
|
|
//currentStringRange.length)];
|
|
|
|
|
|
|
|
|
|
if ((lineWidth + lSize.width) < cSize.width)
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
|
|
|
|
if ([lineScanner isAtEnd])
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"we are at end before finishing a line: %d.\n", [lineScanner scanLocation]);
|
1999-09-07 08:59:35 +00:00
|
|
|
|
NSLog(@"scanLocation = %d, previousParagraphLocation = %d, beginLineIndex = %d",
|
2001-10-18 15:33:23 +00:00
|
|
|
|
[lineScanner scanLocation],
|
|
|
|
|
previousParagraphLocation,
|
|
|
|
|
beginLineIndex);
|
|
|
|
|
[lineStarts addObject: [NSNumber
|
|
|
|
|
numberWithInt: beginLineIndex]];
|
|
|
|
|
[lineEnds addObject: [NSNumber
|
|
|
|
|
numberWithInt: (int)[lineScanner scanLocation] + previousParagraphLocation - (beginLineIndex)]];
|
|
|
|
|
lineWidth = 0.0;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
2001-10-18 15:33:23 +00:00
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
lineWidth += lSize.width;
|
1999-09-07 08:59:35 +00:00
|
|
|
|
//NSLog(@"lineWidth: %f", lineWidth);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (ourLines > cSize.height)
|
|
|
|
|
{
|
|
|
|
|
lastLineForContainerReached = YES;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-10-18 15:33:23 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[lineScanner setScanLocation: previousScanLocation];
|
2000-10-12 23:01:43 +00:00
|
|
|
|
indexToAdd = previousScanLocation + previousParagraphLocation
|
2001-10-18 15:33:23 +00:00
|
|
|
|
- (beginLineIndex);
|
|
|
|
|
|
|
|
|
|
NSLog(@"previousScanLocation = %d, previousParagraphLocation = %d, beginLineIndex = %d indexToAdd = %d",
|
|
|
|
|
previousScanLocation,
|
|
|
|
|
previousParagraphLocation,
|
|
|
|
|
beginLineIndex,
|
|
|
|
|
indexToAdd);
|
|
|
|
|
|
1999-11-22 21:48:03 +00:00
|
|
|
|
ourLines += 20.0; // 14
|
1999-09-07 08:59:35 +00:00
|
|
|
|
lineWidth = 0.0;
|
2001-10-18 15:33:23 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
[lineStarts addObject: [NSNumber
|
2001-10-18 15:33:23 +00:00
|
|
|
|
numberWithInt: beginLineIndex]];
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[lineEnds addObject: [NSNumber numberWithInt: indexToAdd]];
|
1999-09-07 08:59:35 +00:00
|
|
|
|
beginLineIndex = previousScanLocation + previousParagraphLocation;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastLineForContainerReached)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
endScanLocation = [paragraphScanner scanLocation];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
NSLog(@"endScanLocation: %d", endScanLocation);
|
|
|
|
|
|
|
|
|
|
// set this container for that glyphrange
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[self setTextContainer: aContainer
|
2002-02-20 08:52:39 +00:00
|
|
|
|
forGlyphRange: NSMakeRange(startIndex, endScanLocation - startIndex)];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
NSLog(@"ok, move on to step 2.");
|
|
|
|
|
|
|
|
|
|
// step 2. break the lines up and assign rects to them.
|
|
|
|
|
|
2002-02-20 08:52:39 +00:00
|
|
|
|
for (i = 0; i < [lineStarts count]; i++)
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
1999-08-22 11:03:10 +00:00
|
|
|
|
NSRect aRect, bRect;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
float padding = [aContainer lineFragmentPadding];
|
|
|
|
|
NSRange ourRange;
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
// NSLog(@"\t\t===> %d", [[lines objectAtIndex: i] intValue]);
|
1999-09-07 08:59:35 +00:00
|
|
|
|
|
|
|
|
|
ourRange = NSMakeRange ([[lineStarts objectAtIndex: i] intValue],
|
|
|
|
|
[[lineEnds objectAtIndex: i] intValue]);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
/*
|
1999-08-19 23:18:25 +00:00
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
ourRange = NSMakeRange (startIndex,
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[[lines objectAtIndex: i] intValue] - startIndex);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-02-19 00:40:47 +00:00
|
|
|
|
ourRange = NSMakeRange ([[lines objectAtIndex: i-1] intValue],
|
|
|
|
|
[[lines objectAtIndex: i] intValue] - [[lines objectAtIndex: i-1]
|
1999-08-19 23:18:25 +00:00
|
|
|
|
intValue]);
|
|
|
|
|
}
|
1999-09-07 08:59:35 +00:00
|
|
|
|
*/
|
|
|
|
|
NSLog(@"line: %@|", [[_textStorage string]
|
2000-02-19 00:40:47 +00:00
|
|
|
|
substringWithRange: ourRange]);
|
1999-09-07 08:59:35 +00:00
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
firstProposedRect = NSMakeRect (0, i * 14, cSize.width, 14);
|
|
|
|
|
|
|
|
|
|
// ask our textContainer to fix our lineFragment.
|
|
|
|
|
|
1999-08-22 11:03:10 +00:00
|
|
|
|
secondProposedRect = [aContainer
|
|
|
|
|
lineFragmentRectForProposedRect: firstProposedRect
|
|
|
|
|
sweepDirection: NSLineSweepLeft
|
|
|
|
|
movementDirection: NSLineMoveLeft
|
1999-08-19 23:18:25 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[self setLocation: NSMakePoint(secondProposedRect.origin.x + padding,
|
1999-08-19 23:18:25 +00:00
|
|
|
|
secondProposedRect.origin.y + padding)
|
|
|
|
|
forStartOfGlyphRange: ourRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bloody hack.
|
|
|
|
|
// if (moreText)
|
2000-02-19 00:40:47 +00:00
|
|
|
|
// [delegate layoutManager: self
|
|
|
|
|
// didCompleteLayoutForTextContainer: [textContainers objectAtIndex: i]
|
|
|
|
|
// atEnd: NO];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
// else
|
2000-02-19 00:40:47 +00:00
|
|
|
|
// [delegate layoutManager: self
|
|
|
|
|
// didCompleteLayoutForTextContainer: [textContainers objectAtIndex: i]
|
|
|
|
|
// atEnd: YES];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-09-07 08:59:35 +00:00
|
|
|
|
[lineStarts release];
|
|
|
|
|
[lineEnds release];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
return endScanLocation;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) _doLayout
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
2000-02-19 00:40:47 +00:00
|
|
|
|
NSEnumerator *enumerator;
|
|
|
|
|
NSTextContainer *container;
|
|
|
|
|
int gIndex = 0;
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
|
|
|
|
NSLog(@"doLayout called.\n");
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
enumerator = [_textContainers objectEnumerator];
|
|
|
|
|
while ((container = [enumerator nextObject]) != nil)
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
2000-02-19 00:40:47 +00:00
|
|
|
|
gIndex = [self _rebuildLayoutForTextContainer: container
|
|
|
|
|
startingAtGlyphIndex: gIndex];
|
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
|