mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 08:41:03 +00:00
Attributed string fixes
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@6238 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
78b3506b6f
commit
0fab02ec02
3 changed files with 256 additions and 114 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
Thu Mar 09 18:07:00 2000 Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||||
|
|
||||||
|
* Source/NSAttributedString.m: Varius bugfixes.
|
||||||
|
* Source/NSGAttributedString.m: ditto.
|
||||||
|
|
||||||
2000-03-07 Adam Fedor <fedor@gnu.org>
|
2000-03-07 Adam Fedor <fedor@gnu.org>
|
||||||
|
|
||||||
* configure.in: Remove obsolete header checks for Time class.
|
* configure.in: Remove obsolete header checks for Time class.
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
/* Warning - [-initWithString:attributes:] is the designated initialiser,
|
/* Warning - [-initWithString:attributes:] is the designated initialiser,
|
||||||
* but it doesn't provide any way to perform the function of the
|
* but it doesn't provide any way to perform the function of the
|
||||||
* [-initWithAttributedString:] initialiser.
|
* [-initWithAttributedString:] initialiser.
|
||||||
* In order to work youd this, the string argument of the
|
* In order to work round this, the string argument of the
|
||||||
* designated initialiser has been overloaded such that it
|
* designated initialiser has been overloaded such that it
|
||||||
* is expected to accept an NSAttributedString here instead of
|
* is expected to accept an NSAttributedString here instead of
|
||||||
* a string. If you create an NSAttributedString subclass, you
|
* a string. If you create an NSAttributedString subclass, you
|
||||||
|
@ -205,34 +205,41 @@ static Class NSMutableAttributedString_concrete_class;
|
||||||
|
|
||||||
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
|
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException format:
|
[NSException raise: NSRangeException
|
||||||
@"RangeError in method -attributesAtIndex: longestEffectiveRange: inRange: in class NSAttributedString"];
|
format: @"RangeError in method -attributesAtIndex:longestEffectiveRange:inRange: in class NSAttributedString"];
|
||||||
}
|
}
|
||||||
attrDictionary = [self attributesAtIndex: index effectiveRange: aRange];
|
attrDictionary = [self attributesAtIndex: index effectiveRange: aRange];
|
||||||
if (!aRange)
|
if (aRange == 0)
|
||||||
return attrDictionary;
|
return attrDictionary;
|
||||||
|
|
||||||
while (aRange->location > rangeLimit.location)
|
while (aRange->location > rangeLimit.location)
|
||||||
{
|
{
|
||||||
//Check extend range backwards
|
//Check extend range backwards
|
||||||
tmpDictionary =
|
tmpDictionary = [self attributesAtIndex: aRange->location-1
|
||||||
[self attributesAtIndex: aRange->location-1
|
effectiveRange: &tmpRange];
|
||||||
effectiveRange: &tmpRange];
|
|
||||||
if ([tmpDictionary isEqualToDictionary: attrDictionary])
|
if ([tmpDictionary isEqualToDictionary: attrDictionary])
|
||||||
aRange->location = tmpRange.location;
|
{
|
||||||
|
aRange->length = NSMaxRange(*aRange) - tmpRange.location;
|
||||||
|
aRange->location = tmpRange.location;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
|
while (NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
|
||||||
{
|
{
|
||||||
//Check extend range forwards
|
//Check extend range forwards
|
||||||
tmpDictionary =
|
tmpDictionary = [self attributesAtIndex: NSMaxRange(*aRange)
|
||||||
[self attributesAtIndex: NSMaxRange(*aRange)
|
effectiveRange: &tmpRange];
|
||||||
effectiveRange: &tmpRange];
|
|
||||||
if ([tmpDictionary isEqualToDictionary: attrDictionary])
|
if ([tmpDictionary isEqualToDictionary: attrDictionary])
|
||||||
aRange->length = NSMaxRange(tmpRange) - aRange->location;
|
{
|
||||||
|
aRange->length = NSMaxRange(tmpRange) - aRange->location;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
|
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
|
||||||
return attrDictionary;
|
return attrDictionary;
|
||||||
|
@ -246,65 +253,78 @@ static Class NSMutableAttributedString_concrete_class;
|
||||||
id attrValue;
|
id attrValue;
|
||||||
|
|
||||||
tmpDictionary = [self attributesAtIndex: index effectiveRange: aRange];
|
tmpDictionary = [self attributesAtIndex: index effectiveRange: aRange];
|
||||||
//Raises exception if index is out of range, so that I don't have to test this...
|
|
||||||
|
|
||||||
if (!attributeName)
|
if (attributeName == nil)
|
||||||
{
|
{
|
||||||
if (aRange)
|
if (aRange != 0)
|
||||||
*aRange = NSMakeRange(0,[self length]);
|
{
|
||||||
//If attributeName is nil, then the attribute will not exist in the
|
*aRange = NSMakeRange(0,[self length]);
|
||||||
//entire text - therefore aRange of the entire text must be correct
|
/*
|
||||||
|
* If attributeName is nil, then the attribute will not exist in the
|
||||||
|
* entire text - therefore aRange of the entire text must be correct
|
||||||
|
*/
|
||||||
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
attrValue = [tmpDictionary objectForKey: attributeName];
|
attrValue = [tmpDictionary objectForKey: attributeName];
|
||||||
return attrValue;
|
return attrValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) attribute: (NSString*)attributeName atIndex: (unsigned int)index
|
- (id) attribute: (NSString*)attributeName
|
||||||
longestEffectiveRange: (NSRange*)aRange inRange: (NSRange)rangeLimit
|
atIndex: (unsigned int)index
|
||||||
|
longestEffectiveRange: (NSRange*)aRange
|
||||||
|
inRange: (NSRange)rangeLimit
|
||||||
{
|
{
|
||||||
NSDictionary *tmpDictionary;
|
NSDictionary *tmpDictionary;
|
||||||
id attrValue,tmpAttrValue;
|
id attrValue;
|
||||||
NSRange tmpRange;
|
id tmpAttrValue;
|
||||||
|
NSRange tmpRange;
|
||||||
|
|
||||||
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
|
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException format:
|
[NSException raise: NSRangeException
|
||||||
@"RangeError in method -attribute: atIndex: longestEffectiveRange: inRange: in class NSAttributedString"];
|
format: @"RangeError in method -attribute:atIndex:longestEffectiveRange:inRange: in class NSAttributedString"];
|
||||||
}
|
}
|
||||||
|
|
||||||
attrValue = [self attribute: attributeName atIndex: index effectiveRange: aRange];
|
attrValue = [self attribute: attributeName
|
||||||
//Raises exception if index is out of range, so that I don't have to test this...
|
atIndex: index
|
||||||
|
effectiveRange: aRange];
|
||||||
|
|
||||||
if (!attributeName)
|
if (attributeName == nil)
|
||||||
return nil;//attribute: atIndex: effectiveRange: handles this case...
|
return nil;
|
||||||
if (!aRange)
|
if (aRange == 0)
|
||||||
return attrValue;
|
return attrValue;
|
||||||
|
|
||||||
while (aRange->location > rangeLimit.location)
|
while (aRange->location > rangeLimit.location)
|
||||||
{
|
{
|
||||||
//Check extend range backwards
|
//Check extend range backwards
|
||||||
tmpDictionary =
|
tmpDictionary = [self attributesAtIndex: aRange->location-1
|
||||||
[self attributesAtIndex: aRange->location-1
|
effectiveRange: &tmpRange];
|
||||||
effectiveRange: &tmpRange];
|
|
||||||
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
|
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
|
||||||
if (tmpAttrValue == attrValue)
|
if (tmpAttrValue == attrValue)
|
||||||
aRange->location = tmpRange.location;
|
{
|
||||||
|
aRange->length = NSMaxRange(*aRange) - tmpRange.location;
|
||||||
|
aRange->location = tmpRange.location;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
|
while (NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
|
||||||
{
|
{
|
||||||
//Check extend range forwards
|
//Check extend range forwards
|
||||||
tmpDictionary =
|
tmpDictionary = [self attributesAtIndex: NSMaxRange(*aRange)
|
||||||
[self attributesAtIndex: NSMaxRange(*aRange)
|
effectiveRange: &tmpRange];
|
||||||
effectiveRange: &tmpRange];
|
|
||||||
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
|
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
|
||||||
if (tmpAttrValue == attrValue)
|
if (tmpAttrValue == attrValue)
|
||||||
aRange->length = NSMaxRange(tmpRange) - aRange->location;
|
{
|
||||||
|
aRange->length = NSMaxRange(tmpRange) - aRange->location;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
|
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
|
||||||
return attrValue;
|
return attrValue;
|
||||||
|
@ -328,9 +348,9 @@ static Class NSMutableAttributedString_concrete_class;
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
ownDictionary = [self attributesAtIndex: 0
|
ownDictionary = [self attributesAtIndex: 0
|
||||||
effectiveRange: &ownEffectiveRange];
|
effectiveRange: &ownEffectiveRange];
|
||||||
otherDictionary = [otherString attributesAtIndex: 0
|
otherDictionary = [otherString attributesAtIndex: 0
|
||||||
effectiveRange: &otherEffectiveRange];
|
effectiveRange: &otherEffectiveRange];
|
||||||
result = YES;
|
result = YES;
|
||||||
|
|
||||||
while (YES)
|
while (YES)
|
||||||
|
@ -343,14 +363,15 @@ static Class NSMutableAttributedString_concrete_class;
|
||||||
}
|
}
|
||||||
if (NSMaxRange(ownEffectiveRange) < NSMaxRange(otherEffectiveRange))
|
if (NSMaxRange(ownEffectiveRange) < NSMaxRange(otherEffectiveRange))
|
||||||
{
|
{
|
||||||
ownDictionary = [self
|
ownDictionary = [self attributesAtIndex: NSMaxRange(ownEffectiveRange)
|
||||||
attributesAtIndex: NSMaxRange(ownEffectiveRange)
|
effectiveRange: &ownEffectiveRange];
|
||||||
effectiveRange: &ownEffectiveRange];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (NSMaxRange(otherEffectiveRange) >= length)
|
if (NSMaxRange(otherEffectiveRange) >= length)
|
||||||
break;//End of strings
|
{
|
||||||
|
break;//End of strings
|
||||||
|
}
|
||||||
otherDictionary = [otherString
|
otherDictionary = [otherString
|
||||||
attributesAtIndex: NSMaxRange(otherEffectiveRange)
|
attributesAtIndex: NSMaxRange(otherEffectiveRange)
|
||||||
effectiveRange: &otherEffectiveRange];
|
effectiveRange: &otherEffectiveRange];
|
||||||
|
|
|
@ -47,9 +47,12 @@
|
||||||
#include <Foundation/NSGAttributedString.h>
|
#include <Foundation/NSGAttributedString.h>
|
||||||
#include <Foundation/NSException.h>
|
#include <Foundation/NSException.h>
|
||||||
#include <Foundation/NSRange.h>
|
#include <Foundation/NSRange.h>
|
||||||
|
#include <Foundation/NSDebug.h>
|
||||||
#include <base/NSGArray.h>
|
#include <base/NSGArray.h>
|
||||||
#include <base/fast.x>
|
#include <base/fast.x>
|
||||||
|
|
||||||
|
#define SANITY_CHECKS 0
|
||||||
|
|
||||||
@interface GSAttrInfo : NSObject
|
@interface GSAttrInfo : NSObject
|
||||||
{
|
{
|
||||||
@public
|
@public
|
||||||
|
@ -78,6 +81,12 @@
|
||||||
NSDeallocateObject(self);
|
NSDeallocateObject(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString*) description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat: @"Attributes at %u are - %@",
|
||||||
|
loc, attrs];
|
||||||
|
}
|
||||||
|
|
||||||
- (Class) classForPortCoder
|
- (Class) classForPortCoder
|
||||||
{
|
{
|
||||||
return [self class];
|
return [self class];
|
||||||
|
@ -198,25 +207,35 @@ _attributesAtIndexEffectiveRange(
|
||||||
unsigned low, high, used, cnt, nextLoc;
|
unsigned low, high, used, cnt, nextLoc;
|
||||||
GSAttrInfo *found = nil;
|
GSAttrInfo *found = nil;
|
||||||
|
|
||||||
|
used = (*cntImp)(_infoArray, cntSel);
|
||||||
|
NSCAssert(used > 0, NSInternalInconsistencyException);
|
||||||
|
high = used - 1;
|
||||||
|
|
||||||
if (index >= tmpLength)
|
if (index >= tmpLength)
|
||||||
{
|
{
|
||||||
if (index == tmpLength)
|
if (index == tmpLength)
|
||||||
{
|
{
|
||||||
*foundIndex = index;
|
found = OBJECTAT(high);
|
||||||
return nil;
|
if (foundIndex != 0)
|
||||||
|
{
|
||||||
|
*foundIndex = high;
|
||||||
|
}
|
||||||
|
if (aRange != 0)
|
||||||
|
{
|
||||||
|
aRange->location = found->loc;
|
||||||
|
aRange->length = tmpLength - found->loc;
|
||||||
|
}
|
||||||
|
return found->attrs;
|
||||||
}
|
}
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"index is out of range in function "
|
format: @"index is out of range in function "
|
||||||
@"_attributesAtIndexEffectiveRange()"];
|
@"_attributesAtIndexEffectiveRange()"];
|
||||||
}
|
}
|
||||||
|
|
||||||
used = (*cntImp)(_infoArray, cntSel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Binary search for efficiency in huge attributed strings
|
* Binary search for efficiency in huge attributed strings
|
||||||
*/
|
*/
|
||||||
low = 0;
|
low = 0;
|
||||||
high = used - 1;
|
|
||||||
while (low <= high)
|
while (low <= high)
|
||||||
{
|
{
|
||||||
cnt = (low + high) / 2;
|
cnt = (low + high) / 2;
|
||||||
|
@ -240,12 +259,12 @@ _attributesAtIndexEffectiveRange(
|
||||||
if (found->loc == index || index < nextLoc)
|
if (found->loc == index || index < nextLoc)
|
||||||
{
|
{
|
||||||
//Found
|
//Found
|
||||||
if (aRange)
|
if (aRange != 0)
|
||||||
{
|
{
|
||||||
aRange->location = found->loc;
|
aRange->location = found->loc;
|
||||||
aRange->length = nextLoc - found->loc;
|
aRange->length = nextLoc - found->loc;
|
||||||
}
|
}
|
||||||
if (foundIndex)
|
if (foundIndex != 0)
|
||||||
{
|
{
|
||||||
*foundIndex = cnt;
|
*foundIndex = cnt;
|
||||||
}
|
}
|
||||||
|
@ -343,6 +362,33 @@ _attributesAtIndexEffectiveRange(
|
||||||
|
|
||||||
@implementation NSGMutableAttributedString
|
@implementation NSGMutableAttributedString
|
||||||
|
|
||||||
|
#if SANITY_CHECKS
|
||||||
|
|
||||||
|
#define SANITY() [self sanity]
|
||||||
|
|
||||||
|
- (void) sanity
|
||||||
|
{
|
||||||
|
GSAttrInfo *info;
|
||||||
|
unsigned i;
|
||||||
|
unsigned l = 0;
|
||||||
|
unsigned len = [_textChars length];
|
||||||
|
unsigned c = (*cntImp)(_infoArray, cntSel);
|
||||||
|
|
||||||
|
NSAssert(c > 0, NSInternalInconsistencyException);
|
||||||
|
info = OBJECTAT(0);
|
||||||
|
NSAssert(info->loc == 0, NSInternalInconsistencyException);
|
||||||
|
for (i = 1; i < c; i++)
|
||||||
|
{
|
||||||
|
info = OBJECTAT(i);
|
||||||
|
NSAssert(info->loc > l, NSInternalInconsistencyException);
|
||||||
|
NSAssert(info->loc <= len, NSInternalInconsistencyException);
|
||||||
|
l = info->loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define SANITY()
|
||||||
|
#endif
|
||||||
|
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
_setup();
|
_setup();
|
||||||
|
@ -385,6 +431,7 @@ _attributesAtIndexEffectiveRange(
|
||||||
|
|
||||||
aString = [as string];
|
aString = [as string];
|
||||||
_setAttributesFrom(as, NSMakeRange(0, [aString length]), _infoArray);
|
_setAttributesFrom(as, NSMakeRange(0, [aString length]), _infoArray);
|
||||||
|
SANITY();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -424,51 +471,72 @@ _attributesAtIndexEffectiveRange(
|
||||||
NSZone *z = fastZone(self);
|
NSZone *z = fastZone(self);
|
||||||
GSAttrInfo *info;
|
GSAttrInfo *info;
|
||||||
|
|
||||||
if (!attributes)
|
if (range.length == 0)
|
||||||
attributes = [NSDictionary dictionary];
|
{
|
||||||
|
NSWarnMLog(@"Attempt to set attribute for zero-length range", 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (attributes == nil)
|
||||||
|
{
|
||||||
|
attributes = [NSDictionary dictionary];
|
||||||
|
}
|
||||||
|
SANITY();
|
||||||
tmpLength = [_textChars length];
|
tmpLength = [_textChars length];
|
||||||
GS_RANGE_CHECK(range, tmpLength);
|
GS_RANGE_CHECK(range, tmpLength);
|
||||||
arraySize = (*cntImp)(_infoArray, cntSel);
|
arraySize = (*cntImp)(_infoArray, cntSel);
|
||||||
if (NSMaxRange(range) < tmpLength)
|
beginRangeLoc = range.location;
|
||||||
|
afterRangeLoc = NSMaxRange(range);
|
||||||
|
if (afterRangeLoc < tmpLength)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Locate the first range that extends beyond our range.
|
||||||
|
*/
|
||||||
attrs = _attributesAtIndexEffectiveRange(
|
attrs = _attributesAtIndexEffectiveRange(
|
||||||
NSMaxRange(range), &effectiveRange, tmpLength, _infoArray, &arrayIndex);
|
afterRangeLoc, &effectiveRange, tmpLength, _infoArray, &arrayIndex);
|
||||||
|
if (effectiveRange.location > beginRangeLoc)
|
||||||
afterRangeLoc = NSMaxRange(range);
|
|
||||||
if (effectiveRange.location > range.location)
|
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* The located range also starts at or after our range.
|
||||||
|
*/
|
||||||
info = OBJECTAT(arrayIndex);
|
info = OBJECTAT(arrayIndex);
|
||||||
info->loc = afterRangeLoc;
|
info->loc = afterRangeLoc;
|
||||||
|
arrayIndex--;
|
||||||
}
|
}
|
||||||
else
|
else if (effectiveRange.location < beginRangeLoc)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* The located range starts before our range.
|
||||||
|
* Create a subrange to go from our end to the end of the old range.
|
||||||
|
*/
|
||||||
info = NEWINFO(z, attrs, afterRangeLoc);
|
info = NEWINFO(z, attrs, afterRangeLoc);
|
||||||
arrayIndex++;
|
arrayIndex++;
|
||||||
INSOBJECT(info, arrayIndex);
|
INSOBJECT(info, arrayIndex);
|
||||||
RELEASE(info);
|
RELEASE(info);
|
||||||
|
arrayIndex--;
|
||||||
}
|
}
|
||||||
arrayIndex--;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
arrayIndex = arraySize - 1;
|
arrayIndex = arraySize - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove any ranges completely within ours
|
||||||
|
*/
|
||||||
while (arrayIndex > 0)
|
while (arrayIndex > 0)
|
||||||
{
|
{
|
||||||
info = OBJECTAT(arrayIndex-1);
|
info = OBJECTAT(arrayIndex-1);
|
||||||
if (info->loc < range.location)
|
if (info->loc < beginRangeLoc)
|
||||||
break;
|
break;
|
||||||
REMOVEAT(arrayIndex);
|
REMOVEAT(arrayIndex);
|
||||||
arrayIndex--;
|
arrayIndex--;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginRangeLoc = range.location;
|
|
||||||
info = OBJECTAT(arrayIndex);
|
info = OBJECTAT(arrayIndex);
|
||||||
location = info->loc;
|
location = info->loc;
|
||||||
if (location >= range.location)
|
if (location >= beginRangeLoc)
|
||||||
{
|
{
|
||||||
if (location > range.location)
|
if (location > beginRangeLoc)
|
||||||
{
|
{
|
||||||
info->loc = beginRangeLoc;
|
info->loc = beginRangeLoc;
|
||||||
}
|
}
|
||||||
|
@ -482,6 +550,7 @@ _attributesAtIndexEffectiveRange(
|
||||||
RELEASE(info);
|
RELEASE(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SANITY();
|
||||||
/*
|
/*
|
||||||
* Primitive method! Sets attributes and values for a given range of
|
* Primitive method! Sets attributes and values for a given range of
|
||||||
* characters, replacing any previous attributes and values for that
|
* characters, replacing any previous attributes and values for that
|
||||||
|
@ -500,71 +569,118 @@ _attributesAtIndexEffectiveRange(
|
||||||
- (void) replaceCharactersInRange: (NSRange)range
|
- (void) replaceCharactersInRange: (NSRange)range
|
||||||
withString: (NSString*)aString
|
withString: (NSString*)aString
|
||||||
{
|
{
|
||||||
unsigned tmpLength, arrayIndex, arraySize, cnt, moveLocations;
|
unsigned tmpLength, arrayIndex, arraySize;
|
||||||
NSRange effectiveRange;
|
NSRange effectiveRange;
|
||||||
NSDictionary *attrs;
|
NSDictionary *attrs;
|
||||||
unsigned afterRangeLoc;
|
|
||||||
GSAttrInfo *info;
|
GSAttrInfo *info;
|
||||||
|
int moveLocations;
|
||||||
NSZone *z = fastZone(self);
|
NSZone *z = fastZone(self);
|
||||||
|
unsigned start;
|
||||||
|
|
||||||
if (!aString)
|
SANITY();
|
||||||
aString = @"";
|
if (aString == nil)
|
||||||
|
{
|
||||||
|
aString = @"";
|
||||||
|
}
|
||||||
tmpLength = [_textChars length];
|
tmpLength = [_textChars length];
|
||||||
GS_RANGE_CHECK(range, tmpLength);
|
GS_RANGE_CHECK(range, tmpLength);
|
||||||
arraySize = (*cntImp)(_infoArray, cntSel);
|
if (range.location == tmpLength)
|
||||||
if (NSMaxRange(range) < tmpLength)
|
|
||||||
{
|
{
|
||||||
attrs = _attributesAtIndexEffectiveRange(
|
|
||||||
NSMaxRange(range), &effectiveRange, tmpLength, _infoArray, &arrayIndex);
|
|
||||||
|
|
||||||
moveLocations = [aString length] - range.length;
|
|
||||||
afterRangeLoc = NSMaxRange(range) + moveLocations;
|
|
||||||
|
|
||||||
if (effectiveRange.location > range.location)
|
|
||||||
{
|
|
||||||
info = OBJECTAT(arrayIndex);
|
|
||||||
info->loc = afterRangeLoc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = NEWINFO(z, attrs, afterRangeLoc);
|
|
||||||
arrayIndex++;
|
|
||||||
INSOBJECT(info, arrayIndex);
|
|
||||||
arraySize++;
|
|
||||||
RELEASE(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Everything after our modified range need to be shifted.
|
* Special case - replacing a zero length string at the end
|
||||||
|
* simply appends the new string and attributes are inherited.
|
||||||
*/
|
*/
|
||||||
if (arrayIndex + 1 < arraySize)
|
[_textChars appendString: aString];
|
||||||
|
SANITY();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arraySize = (*cntImp)(_infoArray, cntSel);
|
||||||
|
if (arraySize == 1)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Special case - if the string has only one set of attributes
|
||||||
|
* then the replacement characters will get them too.
|
||||||
|
*/
|
||||||
|
[_textChars replaceCharactersInRange: range withString: aString];
|
||||||
|
SANITY();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the attributes to associate with our replacement string.
|
||||||
|
* Should be those of the first character replaced.
|
||||||
|
* If the range replaced is empty, we use the attributes of the
|
||||||
|
* previous character (if possible).
|
||||||
|
*/
|
||||||
|
if (range.length == 0 && range.location > 0)
|
||||||
|
start = range.location - 1;
|
||||||
|
else
|
||||||
|
start = range.location;
|
||||||
|
attrs = _attributesAtIndexEffectiveRange(start, &effectiveRange,
|
||||||
|
tmpLength, _infoArray, &arrayIndex);
|
||||||
|
|
||||||
|
arrayIndex++;
|
||||||
|
if (NSMaxRange(effectiveRange) > NSMaxRange(range))
|
||||||
|
{
|
||||||
|
info = NEWINFO(z, attrs, NSMaxRange(range));
|
||||||
|
INSOBJECT(info, arrayIndex);
|
||||||
|
arraySize++;
|
||||||
|
}
|
||||||
|
else if (NSMaxRange(effectiveRange) < NSMaxRange(range))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Remove all range info for ranges enclosed within the one
|
||||||
|
* we are replacing. Adjust the start point of a range that
|
||||||
|
* extends beyond ours.
|
||||||
|
*/
|
||||||
|
info = OBJECTAT(arrayIndex);
|
||||||
|
if (info->loc < NSMaxRange(range))
|
||||||
{
|
{
|
||||||
unsigned l = arraySize - arrayIndex - 1;
|
int next = arrayIndex + 1;
|
||||||
NSRange r = NSMakeRange(arrayIndex + 1, l);
|
|
||||||
GSAttrInfo *objs[l];
|
while (next < arraySize)
|
||||||
|
|
||||||
[_infoArray getObjects: objs range: r];
|
|
||||||
for (cnt = 0; cnt < l; cnt++)
|
|
||||||
{
|
{
|
||||||
objs[cnt]->loc += moveLocations;
|
GSAttrInfo *n = OBJECTAT(next);
|
||||||
|
if (n->loc <= NSMaxRange(range))
|
||||||
|
{
|
||||||
|
REMOVEAT(arrayIndex);
|
||||||
|
arraySize--;
|
||||||
|
info = n;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arrayIndex--;
|
info->loc = NSMaxRange(range);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arrayIndex = arraySize - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (arrayIndex > 0)
|
moveLocations = [aString length] - range.length;
|
||||||
|
if (effectiveRange.location == range.location
|
||||||
|
&& (moveLocations + range.length) == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we are replacing a range with a zero length string and the
|
||||||
|
* range we are using matches the range replaced, then we must
|
||||||
|
* remove it from the array to avoid getting a zero length range.
|
||||||
|
*/
|
||||||
|
arrayIndex--;
|
||||||
|
REMOVEAT(arrayIndex);
|
||||||
|
arraySize--;
|
||||||
|
}
|
||||||
|
|
||||||
|
SANITY();
|
||||||
|
/*
|
||||||
|
* Now adjust the positions of the ranges following the one we are using.
|
||||||
|
*/
|
||||||
|
while (arrayIndex < arraySize)
|
||||||
{
|
{
|
||||||
info = OBJECTAT(arrayIndex);
|
info = OBJECTAT(arrayIndex);
|
||||||
if (info->loc <= range.location)
|
info->loc += moveLocations;
|
||||||
break;
|
arrayIndex++;
|
||||||
REMOVEAT(arrayIndex);
|
|
||||||
arrayIndex--;
|
|
||||||
}
|
}
|
||||||
|
SANITY();
|
||||||
[_textChars replaceCharactersInRange: range withString: aString];
|
[_textChars replaceCharactersInRange: range withString: aString];
|
||||||
|
SANITY();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue