Attributed string - fix all known bugs and boost performance.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@4055 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 1999-04-09 17:07:21 +00:00
parent c88c0a1f45
commit 9427d093bc
7 changed files with 806 additions and 547 deletions

View file

@ -1,3 +1,14 @@
Fri Apr 9 16:22:00 1999 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* Source/NSAttributedString.m: Implement tracking of changes to
string returned by [-mutableString] method. Simplified initialisation.
* Source/include/NSAttributedString.h: Updated.
* Source/NSGAttributedString.m: Rewrite to get rid of all known
problems and to boost performance hugely.
* Source/include/NSGAttributedString.h: Updated.
* Source/NSString.m: ([-_baseCount]) optimised.
* Source/NSGString.m: ([-_baseCount]) optimised.
1999-04-09 Adam Fedor <fedor@gnu.org>
* NSCharacterSets: Updated sets to Version 2.1.8

View file

@ -3,10 +3,12 @@
String class with attributes
Copyright (C) 1997 Free Software Foundation, Inc.
Copyright (C) 1997,1997 Free Software Foundation, Inc.
Written by: ANOQ of the sun <anoq@vip.cybercity.dk>
Date: November 1997
Rewrite by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: April 1999
This file is part of GNUStep-base
@ -28,22 +30,19 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//FIXME: 1) The NSMutableString object returned from the -mutableString method
// in NSMutableAttributedString is NOT tracked for changes to update
// NSMutableAttributedString's attributes as it should.
/* Warning - [-initWithString:attributes:] is the designated initialiser,
* but it doesn't provide any way to perform the function of the
* [-initWithAttributedString:] initialiser.
* In order to work youd this, the string argument of the
* designated initialiser has been overloaded such that it
* is expected to accept an NSAttributedString here instead of
* a string. If you create an NSAttributedString subclass, you
* must make sure that your implementation of the initialiser
* copes with either an NSString or an NSAttributedString.
* If it receives an NSAttributedString, it should ignore the
* attributes argument and use the values from the string.
*/
//FIXME: 2) If out-of-memory exceptions are raised in some methods,
// inconsistencies may develop, because the two internal arrays in
// NSGAttributedString and NSGMutableAttributedString called
// attributeArray and locateArray must always be syncronized.
//FIXME: 3) The method _setAttributesFrom: must be overridden by
// concrete subclasses of NSAttributedString which is WRONG and
// VERY bad! I haven't found any other way to make
// - initWithString:attributes: the designated initializer
// in NSAttributedString and still implement
// - initWithAttributedString: without having to override it
// in the concrete subclass.
#ifndef _NSXKit_H_NSAttributedString
#define _NSXKit_H_NSAttributedString

View file

@ -3,10 +3,12 @@
Concrete subclass of a string class with attributes
Copyright (C) 1997 Free Software Foundation, Inc.
Copyright (C) 1997,1999 Free Software Foundation, Inc.
Written by: ANOQ of the sun <anoq@vip.cybercity.dk>
Date: November 1997
Rewrite by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: April 1999
This file is part of GNUStep-base
@ -28,22 +30,20 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//FIXME: 1) The NSMutableString object returned from the -mutableString method
// in NSMutableAttributedString is NOT tracked for changes to update
// NSMutableAttributedString's attributes as it should.
//FIXME: 2) If out-of-memory exceptions are raised in some methods,
// inconsistencies may develop, because the two internal arrays in
// NSGAttributedString and NSGMutableAttributedString called
// attributeArray and locateArray must always be syncronized.
/* Warning - [-initWithString:attributes:] is the designated initialiser,
* but it doesn't provide any way to perform the function of the
* [-initWithAttributedString:] initialiser.
* In order to work youd this, the string argument of the
* designated initialiser has been overloaded such that it
* is expected to accept an NSAttributedString here instead of
* a string. If you create an NSAttributedString subclass, you
* must make sure that your implementation of the initialiser
* copes with either an NSString or an NSAttributedString.
* If it receives an NSAttributedString, it should ignore the
* attributes argument and use the values from the string.
*/
//FIXME: 3) The method _setAttributesFrom: must be overridden by
// concrete subclasses of NSAttributedString which is WRONG and
// VERY bad! I haven't found any other way to make
// - initWithString:attributes: the designated initializer
// in NSAttributedString and still implement
// - initWithAttributedString: without having to override it
// in the concrete subclass.
#ifndef _NSGAttributedString_h_INCLUDE
#define _NSGAttributedString_h_INCLUDE
@ -52,32 +52,33 @@
@interface NSGAttributedString : NSAttributedString
{
NSString *textChars;
NSMutableArray *attributeArray;
NSMutableArray *locateArray;
NSString *textChars;
NSMutableArray *infoArray;
}
- _setAttributesFrom:(NSAttributedString *)attributedString range:(NSRange)aRange;
- (id)initWithString:(NSString *)aString attributes:(NSDictionary *)attributes;
- (NSString *)string;
- (NSDictionary *)attributesAtIndex:(unsigned int)index effectiveRange:(NSRange *)aRange;
- (id) initWithString: (NSString*)aString
attributes: (NSDictionary*)attributes;
- (NSString*) string;
- (NSDictionary*) attributesAtIndex: (unsigned)index
effectiveRange: (NSRange*)aRange;
@end
@interface NSGMutableAttributedString : NSMutableAttributedString
{
NSMutableString *textChars;
NSMutableArray *attributeArray;
NSMutableArray *locateArray;
NSMutableString *textChars;
NSMutableArray *infoArray;
}
- _setAttributesFrom:(NSAttributedString *)attributedString range:(NSRange)aRange;
- (id)initWithString:(NSString *)aString attributes:(NSDictionary *)attributes;
- (NSString *)string;
- (NSMutableString *)mutableString;
- (NSDictionary *)attributesAtIndex:(unsigned int)index effectiveRange:(NSRange *)aRange;
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range;
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString;
- (id) initWithString: (NSString*)aString
attributes: (NSDictionary*)attributes;
- (NSString*) string;
- (NSDictionary*) attributesAtIndex: (unsigned)index
effectiveRange: (NSRange*)aRange;
- (void) setAttributes: (NSDictionary*) attributes
range: (NSRange)range;
- (void) replaceCharactersInRange: (NSRange)range
withString: (NSString*)aString;
@end

View file

@ -3,12 +3,14 @@
Implementation of string class with attributes
Copyright (C) 1997 Free Software Foundation, Inc.
Copyright (C) 1997,1999 Free Software Foundation, Inc.
Written by: ANOQ of the sun <anoq@vip.cybercity.dk>
Date: November 1997
Rewrite by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: April 1999
This file is part of GNUStep-base
This file is part of GNUstep-base
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@ -28,22 +30,22 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//FIXME: 1) The NSMutableString object returned from the -mutableString method
// in NSMutableAttributedString is NOT tracked for changes to update
// NSMutableAttributedString's attributes as it should.
/* Warning - [-initWithString:attributes:] is the designated initialiser,
* but it doesn't provide any way to perform the function of the
* [-initWithAttributedString:] initialiser.
* In order to work youd this, the string argument of the
* designated initialiser has been overloaded such that it
* is expected to accept an NSAttributedString here instead of
* a string. If you create an NSAttributedString subclass, you
* must make sure that your implementation of the initialiser
* copes with either an NSString or an NSAttributedString.
* If it receives an NSAttributedString, it should ignore the
* attributes argument and use the values from the string.
*/
//FIXME: 2) If out-of-memory exceptions are raised in some methods,
// inconsistencies may develop, because the two internal arrays in
// NSGAttributedString and NSGMutableAttributedString called
// attributeArray and locateArray must always be syncronized.
//FIXME: 3) The method _setAttributesFrom: must be overridden by
// concrete subclasses of NSAttributedString which is WRONG and
// VERY bad! I haven't found any other way to make
// - initWithString:attributes: the designated initializer
// in NSAttributedString and still implement
// - initWithAttributedString: without having to override it
// in the concrete subclass.
#include <base/preface.h>
#include <base/fast.x>
#include <base/Unicode.h>
#include <Foundation/NSAttributedString.h>
#include <Foundation/NSGAttributedString.h>
@ -51,6 +53,15 @@
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSPortCoder.h>
@interface GSMutableAttributedStringTracker : NSMutableString
{
NSMutableAttributedString *_owner;
}
+ (NSMutableString*) stringWithOwner: (NSMutableAttributedString*)as;
@end
@implementation NSAttributedString
static Class NSAttributedString_concrete_class;
@ -77,37 +88,31 @@ static Class NSMutableAttributedString_concrete_class;
return NSMutableAttributedString_concrete_class;
}
- _setAttributesFrom:(NSAttributedString *)attributedString range:(NSRange)aRange
{
//Private method for implementing -initWithAttributedString: and
//-attributedSubstringFromRange:
[self subclassResponsibility:_cmd];
return self;
}
+ (void) initialize
{
if (self == [NSAttributedString class])
{
NSAttributedString_concrete_class = [NSGAttributedString class];
NSMutableAttributedString_concrete_class = [NSGMutableAttributedString class];
}
{
NSAttributedString_concrete_class
= [NSGAttributedString class];
NSMutableAttributedString_concrete_class
= [NSGMutableAttributedString class];
}
}
+ allocWithZone: (NSZone*)z
+ (id) allocWithZone: (NSZone*)z
{
return NSAllocateObject([self _concreteClass], 0, z);
}
//NSCoding protocol
- (void) encodeWithCoder: anEncoder
- (void) encodeWithCoder: (NSCoder*)anEncoder
{
[super encodeWithCoder:anEncoder];
[super encodeWithCoder: anEncoder];
}
- initWithCoder: aDecoder
- (id) initWithCoder: (NSCoder*)aDecoder
{
return [super initWithCoder:aDecoder];
return [super initWithCoder: aDecoder];
}
- (Class) classForPortCoder
@ -123,169 +128,165 @@ static Class NSMutableAttributedString_concrete_class;
}
//NSCopying protocol
- copyWithZone: (NSZone*)zone
- (id) copyWithZone: (NSZone*)zone
{
if ([self isKindOfClass: [NSMutableAttributedString class]] ||
NSShouldRetainWithZone(self, zone) == NO)
return [[[[self class] _concreteClass] allocWithZone:zone]
initWithAttributedString:self];
return [[[[self class] _concreteClass] allocWithZone: zone]
initWithAttributedString: self];
else
return [self retain];
}
//NSMutableCopying protocol
- mutableCopyWithZone: (NSZone*)zone
- (id) mutableCopyWithZone: (NSZone*)zone
{
return [[[[self class] _mutableConcreteClass] allocWithZone:zone]
initWithAttributedString:self];
return [[[[self class] _mutableConcreteClass] allocWithZone: zone]
initWithAttributedString: self];
}
//Creating an NSAttributedString
- (id)init
- (id) init
{
return [self initWithString:nil attributes:nil];//Designated initializer
return [self initWithString: nil attributes: nil];
}
- (id)initWithString:(NSString *)aString
- (id) initWithString: (NSString*)aString
{
return [self initWithString:aString attributes:nil];//Designated initializer
return [self initWithString: aString attributes: nil];
}
- (id)initWithAttributedString:(NSAttributedString *)attributedString
- (id) initWithAttributedString: (NSAttributedString*)attributedString
{
NSString *tmpStr;
if(!attributedString)
[self initWithString:nil attributes:nil];//Designated initializer
else
{
tmpStr = [attributedString string];
[self initWithString:tmpStr attributes:nil];//Designated initializer
[self _setAttributesFrom:attributedString range:NSMakeRange(0,[tmpStr length])];
}
return self;
return [self initWithString: (NSString*)attributedString attributes: nil];
}
- (id)initWithString:(NSString *)aString attributes:(NSDictionary *)attributes
- (id) initWithString: (NSString*)aString attributes: (NSDictionary*)attributes
{
//This is the designated initializer
return [super init];
[self subclassResponsibility: _cmd];/* Primitive method! */
return nil;
}
//Retrieving character information
- (unsigned int)length
- (unsigned int) length
{
return [[self string] length];
}
- (NSString *)string
- (NSString *) string
{
[self subclassResponsibility:_cmd];/* Primitive method! */
[self subclassResponsibility: _cmd];/* Primitive method! */
return nil;
}
//Retrieving attribute information
- (NSDictionary *)attributesAtIndex:(unsigned int)index effectiveRange:(NSRange *)aRange
- (NSDictionary*) attributesAtIndex: (unsigned)index
effectiveRange: (NSRange*)aRange
{
[self subclassResponsibility:_cmd];/* Primitive method! */
[self subclassResponsibility: _cmd];/* Primitive method! */
return nil;
}
- (NSDictionary *)attributesAtIndex:(unsigned int)index longestEffectiveRange:(NSRange *)aRange inRange:(NSRange)rangeLimit
- (NSDictionary*) attributesAtIndex: (unsigned)index
longestEffectiveRange: (NSRange*)aRange
inRange: (NSRange)rangeLimit
{
NSDictionary *attrDictionary,*tmpDictionary;
NSRange tmpRange;
NSDictionary *attrDictionary, *tmpDictionary;
NSRange tmpRange;
if(rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
{
[NSException raise:NSRangeException format:
@"RangeError in method -attributesAtIndex:longestEffectiveRange:inRange: in class NSAttributedString"];
}
attrDictionary = [self attributesAtIndex:index effectiveRange:aRange];
if(!aRange)
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
{
[NSException raise: NSRangeException format:
@"RangeError in method -attributesAtIndex: longestEffectiveRange: inRange: in class NSAttributedString"];
}
attrDictionary = [self attributesAtIndex: index effectiveRange: aRange];
if (!aRange)
return attrDictionary;
while(aRange->location > rangeLimit.location)
{
//Check extend range backwards
tmpDictionary =
[self attributesAtIndex:aRange->location-1
effectiveRange:&tmpRange];
if([tmpDictionary isEqualToDictionary:attrDictionary])
[self attributesAtIndex: aRange->location-1
effectiveRange: &tmpRange];
if ([tmpDictionary isEqualToDictionary: attrDictionary])
aRange->location = tmpRange.location;
}
while(NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
{
//Check extend range forwards
tmpDictionary =
[self attributesAtIndex:NSMaxRange(*aRange)
effectiveRange:&tmpRange];
if([tmpDictionary isEqualToDictionary:attrDictionary])
[self attributesAtIndex: NSMaxRange(*aRange)
effectiveRange: &tmpRange];
if ([tmpDictionary isEqualToDictionary: attrDictionary])
aRange->length = NSMaxRange(tmpRange) - aRange->location;
}
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
return attrDictionary;
}
- (id)attribute:(NSString *)attributeName atIndex:(unsigned int)index effectiveRange:(NSRange *)aRange
- (id) attribute: (NSString*)attributeName
atIndex: (unsigned)index
effectiveRange: (NSRange*)aRange
{
NSDictionary *tmpDictionary;
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)
{
if(aRange)
if (aRange)
*aRange = NSMakeRange(0,[self length]);
//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;
}
attrValue = [tmpDictionary objectForKey:attributeName];
attrValue = [tmpDictionary objectForKey: attributeName];
return attrValue;
}
- (id)attribute:(NSString *)attributeName atIndex:(unsigned int)index longestEffectiveRange:(NSRange *)aRange inRange:(NSRange)rangeLimit
- (id) attribute: (NSString*)attributeName atIndex: (unsigned int)index longestEffectiveRange: (NSRange *)aRange inRange: (NSRange)rangeLimit
{
NSDictionary *tmpDictionary;
id attrValue,tmpAttrValue;
NSRange tmpRange;
if(rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
if (rangeLimit.location < 0 || NSMaxRange(rangeLimit) > [self length])
{
[NSException raise:NSRangeException format:
@"RangeError in method -attribute:atIndex:longestEffectiveRange:inRange: in class NSAttributedString"];
[NSException raise: NSRangeException format:
@"RangeError in method -attribute: atIndex: longestEffectiveRange: inRange: in class NSAttributedString"];
}
attrValue = [self attribute:attributeName atIndex:index effectiveRange:aRange];
attrValue = [self attribute: attributeName atIndex: index effectiveRange: aRange];
//Raises exception if index is out of range, so that I don't have to test this...
if(!attributeName)
return nil;//attribute:atIndex:effectiveRange: handles this case...
if(!aRange)
if (!attributeName)
return nil;//attribute: atIndex: effectiveRange: handles this case...
if (!aRange)
return attrValue;
while(aRange->location > rangeLimit.location)
{
//Check extend range backwards
tmpDictionary =
[self attributesAtIndex:aRange->location-1
effectiveRange:&tmpRange];
tmpAttrValue = [tmpDictionary objectForKey:attributeName];
if(tmpAttrValue == attrValue)
[self attributesAtIndex: aRange->location-1
effectiveRange: &tmpRange];
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
if (tmpAttrValue == attrValue)
aRange->location = tmpRange.location;
}
while(NSMaxRange(*aRange) < NSMaxRange(rangeLimit))
{
//Check extend range forwards
tmpDictionary =
[self attributesAtIndex:NSMaxRange(*aRange)
effectiveRange:&tmpRange];
tmpAttrValue = [tmpDictionary objectForKey:attributeName];
if(tmpAttrValue == attrValue)
[self attributesAtIndex: NSMaxRange(*aRange)
effectiveRange: &tmpRange];
tmpAttrValue = [tmpDictionary objectForKey: attributeName];
if (tmpAttrValue == attrValue)
aRange->length = NSMaxRange(tmpRange) - aRange->location;
}
*aRange = NSIntersectionRange(*aRange,rangeLimit);//Clip to rangeLimit
@ -293,49 +294,49 @@ static Class NSMutableAttributedString_concrete_class;
}
//Comparing attributed strings
- (BOOL)isEqualToAttributedString:(NSAttributedString *)otherString
- (BOOL) isEqualToAttributedString: (NSAttributedString *)otherString
{
NSRange ownEffectiveRange,otherEffectiveRange;
unsigned int length;
NSDictionary *ownDictionary,*otherDictionary;
BOOL result;
if(!otherString)
if (!otherString)
return NO;
if(![[otherString string] isEqual:[self string]])
if (![[otherString string] isEqual: [self string]])
return NO;
length = [otherString length];
if(length<=0)
if (length<=0)
return YES;
ownDictionary = [self attributesAtIndex:0
effectiveRange:&ownEffectiveRange];
otherDictionary = [otherString attributesAtIndex:0
effectiveRange:&otherEffectiveRange];
ownDictionary = [self attributesAtIndex: 0
effectiveRange: &ownEffectiveRange];
otherDictionary = [otherString attributesAtIndex: 0
effectiveRange: &otherEffectiveRange];
result = YES;
while(YES)
{
if(NSIntersectionRange(ownEffectiveRange,otherEffectiveRange).length > 0 &&
![ownDictionary isEqualToDictionary:otherDictionary])
if (NSIntersectionRange(ownEffectiveRange,otherEffectiveRange).length > 0 &&
![ownDictionary isEqualToDictionary: otherDictionary])
{
result = NO;
break;
}
if(NSMaxRange(ownEffectiveRange) < NSMaxRange(otherEffectiveRange))
if (NSMaxRange(ownEffectiveRange) < NSMaxRange(otherEffectiveRange))
{
ownDictionary = [self
attributesAtIndex:NSMaxRange(ownEffectiveRange)
effectiveRange:&ownEffectiveRange];
attributesAtIndex: NSMaxRange(ownEffectiveRange)
effectiveRange: &ownEffectiveRange];
}
else
{
if(NSMaxRange(otherEffectiveRange) >= length)
if (NSMaxRange(otherEffectiveRange) >= length)
break;//End of strings
otherDictionary = [otherString
attributesAtIndex:NSMaxRange(otherEffectiveRange)
effectiveRange:&otherEffectiveRange];
attributesAtIndex: NSMaxRange(otherEffectiveRange)
effectiveRange: &otherEffectiveRange];
}
}
return result;
@ -345,28 +346,56 @@ static Class NSMutableAttributedString_concrete_class;
{
if (anObject == self)
return YES;
if ([anObject isKindOf:[NSAttributedString class]])
return [self isEqualToAttributedString:anObject];
if ([anObject isKindOf: [NSAttributedString class]])
return [self isEqualToAttributedString: anObject];
return NO;
}
//Extracting a substring
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange
- (NSAttributedString *) attributedSubstringFromRange: (NSRange)aRange
{
NSAttributedString *newAttrString;
NSString *newSubstring;
NSAttributedString *newAttrString;
NSString *newSubstring;
NSDictionary *attrs;
NSRange range;
if(aRange.location<0 || aRange.length<0 || NSMaxRange(aRange)>[self length])
[NSException raise:NSRangeException
format:@"RangeError in method -attributedSubstringFromRange: in class NSAttributedString"];
if (aRange.location<0 || aRange.length<0 || NSMaxRange(aRange)>[self length])
[NSException raise: NSRangeException
format: @"RangeError in method -attributedSubstringFromRange: "
@"in class NSAttributedString"];
newSubstring = [[self string] substringFromRange:aRange];//Should already be autoreleased
newSubstring = [[self string] substringFromRange: aRange];
attrs = [self attributesAtIndex: aRange.location effectiveRange: &range];
range = NSIntersectionRange(range, aRange);
if (NSEqualRanges(range, aRange) == YES)
{
newAttrString = [[NSAttributedString alloc] initWithString: newSubstring
attributes: attrs];
}
else
{
NSMutableAttributedString *m;
NSRange rangeToSet = range;
m = [[NSMutableAttributedString alloc] initWithString: newSubstring
attributes: nil];
rangeToSet.location = 0;
[m setAttributes: attrs range: rangeToSet];
while (NSMaxRange(range) < NSMaxRange(aRange))
{
attrs = [self attributesAtIndex: NSMaxRange(range)
effectiveRange: &range];
rangeToSet = NSIntersectionRange(range, aRange);
rangeToSet.location -= aRange.location;
[m setAttributes: attrs range: rangeToSet];
}
newAttrString = [m copy];
[m release];
}
newAttrString = [[NSAttributedString alloc] initWithString:newSubstring attributes:nil];
[newAttrString autorelease];
[newAttrString _setAttributesFrom:self range:aRange];
return newAttrString;
}
@ -380,25 +409,24 @@ static Class NSMutableAttributedString_concrete_class;
}
//Retrieving character information
- (NSMutableString *)mutableString
- (NSMutableString *) mutableString
{
[self subclassResponsibility:_cmd];
return nil;
return [GSMutableAttributedStringTracker stringWithOwner: self];
}
//Changing characters
- (void)deleteCharactersInRange:(NSRange)aRange
- (void) deleteCharactersInRange: (NSRange)aRange
{
[self replaceCharactersInRange:aRange withString:nil];
[self replaceCharactersInRange: aRange withString: nil];
}
//Changing attributes
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)aRange
- (void) setAttributes: (NSDictionary *)attributes range: (NSRange)aRange
{
[self subclassResponsibility:_cmd];// Primitive method!
[self subclassResponsibility: _cmd];// Primitive method!
}
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)aRange
- (void) addAttribute: (NSString *)name value: (id)value range: (NSRange)aRange
{
NSRange effectiveRange;
NSDictionary *attrDict;
@ -406,77 +434,77 @@ static Class NSMutableAttributedString_concrete_class;
unsigned int tmpLength;
tmpLength = [self length];
if(aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
if (aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
{
[NSException raise:NSRangeException
format:@"RangeError in method -addAttribute:value:range: in class NSMutableAttributedString"];
[NSException raise: NSRangeException
format: @"RangeError in method -addAttribute: value: range: in class NSMutableAttributedString"];
}
attrDict = [self attributesAtIndex:aRange.location
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: aRange.location
effectiveRange: &effectiveRange];
while(effectiveRange.location < NSMaxRange(aRange))
{
effectiveRange = NSIntersectionRange(aRange,effectiveRange);
newDict = [[NSMutableDictionary alloc] initWithDictionary:attrDict];
newDict = [[NSMutableDictionary alloc] initWithDictionary: attrDict];
[newDict autorelease];
[newDict setObject:value forKey:name];
[self setAttributes:newDict range:effectiveRange];
[newDict setObject: value forKey: name];
[self setAttributes: newDict range: effectiveRange];
if(NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
if (NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
effectiveRange.location = NSMaxRange(aRange);//This stops the loop...
else if(NSMaxRange(effectiveRange) < tmpLength)
else if (NSMaxRange(effectiveRange) < tmpLength)
{
attrDict = [self attributesAtIndex:NSMaxRange(effectiveRange)
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: NSMaxRange(effectiveRange)
effectiveRange: &effectiveRange];
}
}
}
- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)aRange
- (void) addAttributes: (NSDictionary *)attributes range: (NSRange)aRange
{
NSRange effectiveRange;
NSDictionary *attrDict;
NSMutableDictionary *newDict;
unsigned int tmpLength;
if(!attributes)
if (!attributes)
{
//I cannot use NSParameterAssert here, if is has to be an NSInvalidArgumentException
[NSException raise:NSInvalidArgumentException
format:@"attributes is nil in method -addAttributes:range: in class NSMutableAtrributedString"];
[NSException raise: NSInvalidArgumentException
format: @"attributes is nil in method -addAttributes: range: in class NSMutableAtrributedString"];
}
tmpLength = [self length];
if(aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
if (aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
{
[NSException raise:NSRangeException
format:@"RangeError in method -addAttribute:value:range: in class NSMutableAttributedString"];
[NSException raise: NSRangeException
format: @"RangeError in method -addAttribute: value: range: in class NSMutableAttributedString"];
}
attrDict = [self attributesAtIndex:aRange.location
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: aRange.location
effectiveRange: &effectiveRange];
while(effectiveRange.location < NSMaxRange(aRange))
{
effectiveRange = NSIntersectionRange(aRange,effectiveRange);
newDict = [[NSMutableDictionary alloc] initWithDictionary:attrDict];
newDict = [[NSMutableDictionary alloc] initWithDictionary: attrDict];
[newDict autorelease];
[newDict addEntriesFromDictionary:attributes];
[self setAttributes:newDict range:effectiveRange];
[newDict addEntriesFromDictionary: attributes];
[self setAttributes: newDict range: effectiveRange];
if(NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
if (NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
effectiveRange.location = NSMaxRange(aRange);//This stops the loop...
else if(NSMaxRange(effectiveRange) < tmpLength)
else if (NSMaxRange(effectiveRange) < tmpLength)
{
attrDict = [self attributesAtIndex:NSMaxRange(effectiveRange)
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: NSMaxRange(effectiveRange)
effectiveRange: &effectiveRange];
}
}
}
- (void)removeAttribute:(NSString *)name range:(NSRange)aRange
- (void) removeAttribute: (NSString *)name range: (NSRange)aRange
{
NSRange effectiveRange;
NSDictionary *attrDict;
@ -484,89 +512,180 @@ static Class NSMutableAttributedString_concrete_class;
unsigned int tmpLength;
tmpLength = [self length];
if(aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
if (aRange.location <= 0 || NSMaxRange(aRange) > tmpLength)
{
[NSException raise:NSRangeException
format:@"RangeError in method -addAttribute:value:range: in class NSMutableAttributedString"];
[NSException raise: NSRangeException
format: @"RangeError in method -addAttribute: value: range: in class NSMutableAttributedString"];
}
attrDict = [self attributesAtIndex:aRange.location
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: aRange.location
effectiveRange: &effectiveRange];
while(effectiveRange.location < NSMaxRange(aRange))
{
effectiveRange = NSIntersectionRange(aRange,effectiveRange);
newDict = [[NSMutableDictionary alloc] initWithDictionary:attrDict];
newDict = [[NSMutableDictionary alloc] initWithDictionary: attrDict];
[newDict autorelease];
[newDict removeObjectForKey:name];
[self setAttributes:newDict range:effectiveRange];
[newDict removeObjectForKey: name];
[self setAttributes: newDict range: effectiveRange];
if(NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
if (NSMaxRange(effectiveRange) >= NSMaxRange(aRange))
effectiveRange.location = NSMaxRange(aRange);//This stops the loop...
else if(NSMaxRange(effectiveRange) < tmpLength)
else if (NSMaxRange(effectiveRange) < tmpLength)
{
attrDict = [self attributesAtIndex:NSMaxRange(effectiveRange)
effectiveRange:&effectiveRange];
attrDict = [self attributesAtIndex: NSMaxRange(effectiveRange)
effectiveRange: &effectiveRange];
}
}
}
//Changing characters and attributes
- (void)appendAttributedString:(NSAttributedString *)attributedString
- (void) appendAttributedString: (NSAttributedString *)attributedString
{
[self replaceCharactersInRange:NSMakeRange([self length],0)
withAttributedString:attributedString];
[self replaceCharactersInRange: NSMakeRange([self length],0)
withAttributedString: attributedString];
}
- (void)insertAttributedString:(NSAttributedString *)attributedString atIndex:(unsigned int)index
- (void) insertAttributedString: (NSAttributedString *)attributedString atIndex: (unsigned int)index
{
[self replaceCharactersInRange:NSMakeRange(index,0)
withAttributedString:attributedString];
[self replaceCharactersInRange: NSMakeRange(index,0)
withAttributedString: attributedString];
}
- (void)replaceCharactersInRange:(NSRange)aRange withAttributedString:(NSAttributedString *)attributedString
- (void) replaceCharactersInRange: (NSRange)aRange withAttributedString: (NSAttributedString *)attributedString
{
NSRange effectiveRange,clipRange,ownRange;
NSDictionary *attrDict;
NSString *tmpStr;
tmpStr = [attributedString string];
[self replaceCharactersInRange:aRange
withString:tmpStr];
[self replaceCharactersInRange: aRange
withString: tmpStr];
effectiveRange = NSMakeRange(0,0);
clipRange = NSMakeRange(0,[tmpStr length]);
while(NSMaxRange(effectiveRange) < NSMaxRange(clipRange))
{
attrDict = [attributedString attributesAtIndex:effectiveRange.location
effectiveRange:&effectiveRange];
attrDict = [attributedString attributesAtIndex: effectiveRange.location
effectiveRange: &effectiveRange];
ownRange = NSIntersectionRange(clipRange,effectiveRange);
ownRange.location += aRange.location;
[self setAttributes:attrDict range:ownRange];
[self setAttributes: attrDict range: ownRange];
}
}
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString
- (void) replaceCharactersInRange: (NSRange)aRange withString: (NSString *)aString
{
[self subclassResponsibility:_cmd];// Primitive method!
[self subclassResponsibility: _cmd];// Primitive method!
}
- (void)setAttributedString:(NSAttributedString *)attributedString
- (void) setAttributedString: (NSAttributedString *)attributedString
{
[self replaceCharactersInRange:NSMakeRange(0,[self length])
withAttributedString:attributedString];
[self replaceCharactersInRange: NSMakeRange(0,[self length])
withAttributedString: attributedString];
}
//Grouping changes
- (void)beginEditing
- (void) beginEditing
{
//Overridden by subclasses
}
- (void)endEditing
- (void) endEditing
{
//Overridden by subclasses
}
@end //NSMutableAttributedString
/*
* The GSMutableAttributedStringTracker class is a concrete subclass of
* NSMutableString which keeps it's owner informed of any changes made
* to it.
*/
@implementation GSMutableAttributedStringTracker
+ (NSMutableString*) stringWithOwner: (NSMutableAttributedString*)as
{
GSMutableAttributedStringTracker *str;
NSZone *z = NSDefaultMallocZone();
str = (GSMutableAttributedStringTracker*) NSAllocateObject(self, 0, z);
str->_owner = RETAIN(as);
return [str autorelease];
}
- (void) dealloc
{
RELEASE(_owner);
NSDeallocateObject(self);
}
- (unsigned int) length
{
return [[_owner string] length];
}
- (unichar) characterAtIndex: (unsigned int)index
{
return [[_owner string] characterAtIndex: index];
}
- (void)getCharacters: (unichar*)buffer
{
return [[_owner string] getCharacters: buffer];
}
- (void)getCharacters: (unichar*)buffer range: (NSRange)aRange
{
return [[_owner string] getCharacters: buffer range: aRange];
}
- (const char *) cString
{
return [[_owner string] cString];
}
- (unsigned int) cStringLength
{
return [[_owner string] cStringLength];
}
- (NSStringEncoding) fastestEncoding
{
return [[_owner string] fastestEncoding];
}
- (NSStringEncoding) smallestEncoding
{
return [[_owner string] smallestEncoding];
}
- (int) _baseLength
{
return [[_owner string] _baseLength];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
return [[_owner string] encodeWithCoder: aCoder];
}
- (Class) classForCoder
{
return [[_owner string] classForCoder];
}
- (void) replaceCharactersInRange: (NSRange)aRange
withString: (NSString*)aString
{
[_owner replaceCharactersInRange: aRange withString: aString];
}
@end

View file

@ -3,10 +3,12 @@
Implementation of concrete subclass of a string class with attributes
Copyright (C) 1997 Free Software Foundation, Inc.
Copyright (C) 1997,1999 Free Software Foundation, Inc.
Written by: ANOQ of the sun <anoq@vip.cybercity.dk>
Date: November 1997
Rewrite by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: April 1999
This file is part of GNUStep-base
@ -28,191 +30,267 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//FIXME: 1) The NSMutableString object returned from the -mutableString method
// in NSMutableAttributedString is NOT tracked for changes to update
// NSMutableAttributedString's attributes as it should.
//FIXME: 2) If out-of-memory exceptions are raised in some methods,
// inconsistencies may develop, because the two internal arrays in
// NSGAttributedString and NSGMutableAttributedString called
// attributeArray and locateArray must always be syncronized.
//FIXME: 3) The method _setAttributesFrom: must be overridden by
// concrete subclasses of NSAttributedString which is WRONG and
// VERY bad! I haven't found any other way to make
// - initWithString:attributes: the designated initializer
// in NSAttributedString and still implement
// - initWithAttributedString: without having to override it
// in the concrete subclass.
/* Warning - [-initWithString:attributes:] is the designated initialiser,
* but it doesn't provide any way to perform the function of the
* [-initWithAttributedString:] initialiser.
* In order to work youd this, the string argument of the
* designated initialiser has been overloaded such that it
* is expected to accept an NSAttributedString here instead of
* a string. If you create an NSAttributedString subclass, you
* must make sure that your implementation of the initialiser
* copes with either an NSString or an NSAttributedString.
* If it receives an NSAttributedString, it should ignore the
* attributes argument and use the values from the string.
*/
#include <Foundation/NSGAttributedString.h>
#include <Foundation/NSException.h>
#include <Foundation/NSValue.h>
@implementation NSGAttributedString
void _setAttributesFrom(
NSAttributedString *attributedString,
NSRange aRange,
NSMutableArray *attributeArray,
NSMutableArray *locateArray)
@interface GSAttrInfo : NSObject
{
//always called immediately after -initWithString:attributes:
NSRange effectiveRange;
NSDictionary *attributeDict;
if(aRange.length <= 0)
return;//No attributes
attributeDict = [attributedString attributesAtIndex:aRange.location
effectiveRange:&effectiveRange];
[attributeArray replaceObjectAtIndex:0 withObject:attributeDict];
while (NSMaxRange(effectiveRange) < NSMaxRange(aRange))
{
attributeDict =
[attributedString attributesAtIndex:NSMaxRange(effectiveRange)
effectiveRange:&effectiveRange];
[attributeArray addObject:attributeDict];
[locateArray addObject:
[NSNumber numberWithUnsignedInt:effectiveRange.location-aRange.location]];
}
return;
@public
unsigned loc;
NSDictionary *attrs;
}
void _initWithString(
NSString *aString,
NSDictionary *attributes,
NSString **textChars,
NSMutableArray **attributeArray,
NSMutableArray **locateArray)
+ (GSAttrInfo*) newWithZone: (NSZone*)z value: (NSDictionary*)a at: (unsigned)l;
@end
@implementation GSAttrInfo
+ (GSAttrInfo*) newWithZone: (NSZone*)z value: (NSDictionary*)a at: (unsigned)l;
{
if (aString)
*textChars = [(*textChars) initWithString:aString];
else
*textChars = [(*textChars) init];
*attributeArray = [[NSMutableArray alloc] init];
*locateArray = [[NSMutableArray alloc] init];
if(!attributes)
attributes = [[[NSDictionary alloc] init] autorelease];
[(*attributeArray) addObject:attributes];
[(*locateArray) addObject:[NSNumber numberWithUnsignedInt:0]];
GSAttrInfo *info = (GSAttrInfo*)NSAllocateObject(self, 0, z);
info->loc = l;
info->attrs = [a copy];
return info;
}
NSDictionary *_attributesAtIndexEffectiveRange(
unsigned int index,
NSRange *aRange,
unsigned int tmpLength,
NSMutableArray *attributeArray,
NSMutableArray *locateArray,
unsigned int *foundIndex)
- (void) dealloc
{
unsigned int low,high,used,cnt,foundLoc,nextLoc;
NSDictionary *foundDict;
if(index<0 || index >= tmpLength)
{
[NSException raise:NSRangeException format:
@"index is out of range in function _attributesAtIndexEffectiveRange()"];
}
//Binary search for efficiency in huge attributed strings
used = [attributeArray count];
low=0;
high = used - 1;
while(low<=high)
{
cnt=(low+high)/2;
foundDict = [attributeArray objectAtIndex:cnt];
foundLoc = [[locateArray objectAtIndex:cnt] unsignedIntValue];
if(foundLoc > index)
{
high = cnt-1;
}
else
{
if(cnt >= used -1)
nextLoc = tmpLength;
else
nextLoc = [[locateArray objectAtIndex:cnt+1] unsignedIntValue];
if(foundLoc == index ||
index < nextLoc)
{
//Found
if(aRange)
{
aRange->location = foundLoc;
aRange->length = nextLoc - foundLoc;
}
if(foundIndex)
*foundIndex = cnt;
return foundDict;
}
else
low = cnt+1;
}
}
NSCAssert(NO,@"Error in binary search algorithm");
return nil;
RELEASE(attrs);
NSDeallocateObject(self);
}
- (Class) classForPortCoder
{
return [self class];
}
- replacementObjectForPortCoder:(NSPortCoder*)aCoder
{
return self;
}
- (void) encodeWithCoder: aCoder
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:textChars];
[aCoder encodeObject:attributeArray];
[aCoder encodeObject:locateArray];
}
- initWithCoder: aCoder
{
self = [super initWithCoder:aCoder];
textChars = [[aCoder decodeObject] retain];
attributeArray = [[aCoder decodeObject] retain];
locateArray = [[aCoder decodeObject] retain];
return self;
}
- _setAttributesFrom:(NSAttributedString *)attributedString range:(NSRange)aRange
- (void) encodeWithCoder: (NSCoder*)aCoder
{
//always called immediately after -initWithString:attributes:
_setAttributesFrom(attributedString,aRange,attributeArray,locateArray);
[super encodeWithCoder: aCoder];
[aCoder encodeValueOfObjCType: @encode(unsigned) at: &loc];
[aCoder encodeValueOfObjCType: @encode(id) at: &attrs];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
self = [super initWithCoder: aCoder];
[aCoder decodeValueOfObjCType: @encode(unsigned) at: &loc];
[aCoder decodeValueOfObjCType: @encode(id) at: &attrs];
return self;
}
- (id)initWithString:(NSString *)aString attributes:(NSDictionary *)attributes
@end
@implementation NSGAttributedString
static SEL infSel = @selector(newWithZone:value:at:);
static IMP infImp = 0;
static Class infCls = 0;
void _setAttributesFrom(
NSAttributedString *attributedString,
NSRange aRange,
NSMutableArray *infoArray)
{
NSZone *z = [infoArray zone];
NSRange range;
NSDictionary *attr;
GSAttrInfo *info;
unsigned loc;
/*
* remove any old attributes of the string.
*/
[infoArray removeAllObjects];
if (aRange.length <= 0)
return;
attr = [attributedString attributesAtIndex: aRange.location
effectiveRange: &range];
info = [GSAttrInfo newWithZone: z value: attr at: 0];
[infoArray addObject: info];
RELEASE(info);
while ((loc = NSMaxRange(range)) < NSMaxRange(aRange))
{
attr = [attributedString attributesAtIndex: loc
effectiveRange: &range];
info = [GSAttrInfo newWithZone: z value: attr at: loc - aRange.location];
[infoArray addObject: info];
RELEASE(info);
}
}
NSDictionary *_attributesAtIndexEffectiveRange(
unsigned int index,
NSRange *aRange,
unsigned int tmpLength,
NSMutableArray *infoArray,
unsigned int *foundIndex)
{
unsigned low, high, used, cnt, nextLoc;
GSAttrInfo *found = nil;
if (index >= tmpLength)
{
[NSException raise: NSRangeException
format: @"index is out of range in function "
@"_attributesAtIndexEffectiveRange()"];
}
used = [infoArray count];
/*
* Binary search for efficiency in huge attributed strings
*/
low = 0;
high = used - 1;
while (low <= high)
{
cnt = (low + high) / 2;
found = [infoArray objectAtIndex: cnt];
if (found->loc > index)
{
high = cnt - 1;
}
else
{
if (cnt >= used - 1)
{
nextLoc = tmpLength;
}
else
{
GSAttrInfo *inf = [infoArray objectAtIndex: cnt + 1];
nextLoc = inf->loc;
}
if (found->loc == index || index < nextLoc)
{
//Found
if (aRange)
{
aRange->location = found->loc;
aRange->length = nextLoc - found->loc;
}
if (foundIndex)
{
*foundIndex = cnt;
}
return found->attrs;
}
else
{
low = cnt + 1;
}
}
}
NSCAssert(NO,@"Error in binary search algorithm");
return nil;
}
+ (void) initialize
{
if (infCls == 0)
{
infCls = [GSAttrInfo class];
infImp = [infCls methodForSelector: infSel];
}
}
- (Class) classForPortCoder
{
return [self class];
}
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
{
self = [super initWithString:aString attributes:attributes];
textChars = [NSString alloc];
_initWithString(aString,attributes,&textChars,&attributeArray,&locateArray);
return self;
}
- (NSString *)string
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[super encodeWithCoder: aCoder];
[aCoder encodeValueOfObjCType: @encode(id) at: &textChars];
[aCoder encodeValueOfObjCType: @encode(id) at: &infoArray];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
self = [super initWithCoder: aCoder];
[aCoder decodeValueOfObjCType: @encode(id) at: &textChars];
[aCoder decodeValueOfObjCType: @encode(id) at: &infoArray];
return self;
}
- (id) initWithString: (NSString*)aString
attributes: (NSDictionary*)attributes
{
NSZone *z = [self zone];
infoArray = [[NSMutableArray allocWithZone: z] initWithCapacity: 1];
if (aString != nil && [aString isKindOfClass: [NSAttributedString class]])
{
NSAttributedString *as = (NSAttributedString*)aString;
aString = [as string];
_setAttributesFrom(as, NSMakeRange(0, [aString length]), infoArray);
}
else
{
GSAttrInfo *info;
info = (*infImp)(infCls, infSel, z, attributes, 0);
[infoArray addObject: info];
RELEASE(info);
}
if (aString == nil)
textChars = @"";
else
textChars = [aString copyWithZone: z];
return self;
}
- (NSString*) string
{
return textChars;
}
- (NSDictionary *)attributesAtIndex:(unsigned int)index effectiveRange:(NSRange *)aRange
- (NSDictionary*) attributesAtIndex: (unsigned)index
effectiveRange: (NSRange*)aRange
{
return _attributesAtIndexEffectiveRange(
index,aRange,[self length],attributeArray,locateArray,NULL);
index, aRange, [self length], infoArray, NULL);
}
- (void)dealloc
- (void) dealloc
{
[textChars release];
[attributeArray release];
[locateArray release];
RELEASE(textChars);
RELEASE(infoArray);
[super dealloc];
}
@ -221,203 +299,245 @@ NSDictionary *_attributesAtIndexEffectiveRange(
@implementation NSGMutableAttributedString
+ (void) initialize
{
if (infCls == 0)
{
infCls = [GSAttrInfo class];
infImp = [infCls methodForSelector: infSel];
}
}
- (Class) classForPortCoder
{
return [self class];
}
- replacementObjectForPortCoder:(NSPortCoder*)aCoder
{
return self;
}
- (void) encodeWithCoder: aCoder
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:textChars];
[aCoder encodeObject:attributeArray];
[aCoder encodeObject:locateArray];
}
- initWithCoder: aCoder
{
self = [super initWithCoder:aCoder];
textChars = [[aCoder decodeObject] retain];
attributeArray = [[aCoder decodeObject] retain];
locateArray = [[aCoder decodeObject] retain];
return self;
}
- _setAttributesFrom:(NSAttributedString *)attributedString range:(NSRange)aRange
- (void) encodeWithCoder: (NSCoder*)aCoder
{
//always called immediately after -initWithString:attributes:
_setAttributesFrom(attributedString,aRange,attributeArray,locateArray);
[super encodeWithCoder: aCoder];
[aCoder encodeValueOfObjCType: @encode(id) at: &textChars];
[aCoder encodeValueOfObjCType: @encode(id) at: &infoArray];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
self = [super initWithCoder: aCoder];
[aCoder decodeValueOfObjCType: @encode(id) at: &textChars];
[aCoder decodeValueOfObjCType: @encode(id) at: &infoArray];
return self;
}
- (id)initWithString:(NSString *)aString attributes:(NSDictionary *)attributes
- (id) initWithString: (NSString*)aString
attributes: (NSDictionary*)attributes
{
self = [super initWithString:aString attributes:attributes];
textChars = [NSMutableString alloc];
_initWithString(aString,attributes,&textChars,&attributeArray,&locateArray);
NSZone *z = [self zone];
infoArray = [[NSMutableArray allocWithZone: z] initWithCapacity: 1];
if (aString != nil && [aString isKindOfClass: [NSAttributedString class]])
{
NSAttributedString *as = (NSAttributedString*)aString;
aString = [as string];
_setAttributesFrom(as, NSMakeRange(0, [aString length]), infoArray);
}
else
{
GSAttrInfo *info;
info = (*infImp)(infCls, infSel, z, attributes, 0);
[infoArray addObject: info];
RELEASE(info);
}
if (aString == nil)
textChars = [[NSMutableString alloc] init];
else
textChars = [aString mutableCopyWithZone: z];
return self;
}
- (NSString *)string
- (NSString*) string
{
return textChars;
}
- (NSMutableString *)mutableString
{
return textChars;
}
- (NSDictionary *)attributesAtIndex:(unsigned int)index effectiveRange:(NSRange *)aRange
- (NSDictionary*) attributesAtIndex: (unsigned)index
effectiveRange: (NSRange*)aRange
{
return _attributesAtIndexEffectiveRange(
index,aRange,[self length],attributeArray,locateArray,NULL);
index, aRange, [self length], infoArray, NULL);
}
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range
- (void) setAttributes: (NSDictionary*)attributes
range: (NSRange)range
{
unsigned int tmpLength,arrayIndex,arraySize,location;
NSRange effectiveRange;
NSNumber *afterRangeLocation,*beginRangeLocation;
NSDictionary *attrs;
if(!attributes)
unsigned tmpLength, arrayIndex, arraySize, location;
NSRange effectiveRange;
unsigned afterRangeLoc, beginRangeLoc;
NSDictionary *attrs;
NSZone *z = [self zone];
GSAttrInfo *info;
if (!attributes)
attributes = [NSDictionary dictionary];
tmpLength = [self length];
if(range.location < 0 || NSMaxRange(range) > tmpLength)
{
[NSException raise:NSRangeException
format:@"RangeError in method -replaceCharactersInRange:withString: in class NSMutableAttributedString"];
}
arraySize = [locateArray count];
if(NSMaxRange(range) < tmpLength)
{
attrs = _attributesAtIndexEffectiveRange(
NSMaxRange(range),&effectiveRange,tmpLength,attributeArray,locateArray,&arrayIndex);
afterRangeLocation =
[NSNumber numberWithUnsignedInt:NSMaxRange(range)];
if(effectiveRange.location > range.location)
if (NSMaxRange(range) > tmpLength)
{
[locateArray replaceObjectAtIndex:arrayIndex
withObject:afterRangeLocation];
[NSException raise: NSRangeException
format: @"RangeError in method -replaceCharactersInRange: "
@"withString: in class NSMutableAttributedString"];
}
else
arraySize = [infoArray count];
if (NSMaxRange(range) < tmpLength)
{
attrs = _attributesAtIndexEffectiveRange(
NSMaxRange(range), &effectiveRange, tmpLength, infoArray, &arrayIndex);
afterRangeLoc = NSMaxRange(range);
if (effectiveRange.location > range.location)
{
info = [infoArray objectAtIndex: arrayIndex];
info->loc = afterRangeLoc;
}
else
{
info = (*infImp)(infCls, infSel, z, attrs, afterRangeLoc);
[infoArray insertObject: info atIndex: ++arrayIndex];
RELEASE(info);
}
arrayIndex--;
}
else
{
arrayIndex = arraySize - 1;
}
while (arrayIndex > 0)
{
info = [infoArray objectAtIndex: arrayIndex-1];
if (info->loc < range.location)
break;
[infoArray removeObjectAtIndex: arrayIndex];
arrayIndex--;
}
beginRangeLoc = range.location;
info = [infoArray objectAtIndex: arrayIndex];
location = info->loc;
if (location >= range.location)
{
if (location > range.location)
{
info->loc = beginRangeLoc;
}
ASSIGN(info->attrs, attributes);
}
else
{
arrayIndex++;
//There shouldn't be anything wrong in putting an object (attrs) in
//an array more than once should there? The object will not change.
[attributeArray insertObject:attrs atIndex:arrayIndex];
[locateArray insertObject:afterRangeLocation atIndex:arrayIndex];
info = (*infImp)(infCls, infSel, z, attributes, beginRangeLoc);
[infoArray insertObject: info atIndex: arrayIndex];
RELEASE(info);
}
arrayIndex--;
}
else
arrayIndex = arraySize - 1;
while(arrayIndex > 0 &&
[[locateArray objectAtIndex:arrayIndex-1] unsignedIntValue] >= range.location)
{
[locateArray removeObjectAtIndex:arrayIndex];
[attributeArray removeObjectAtIndex:arrayIndex];
arrayIndex--;
}
beginRangeLocation = [NSNumber numberWithUnsignedInt:range.location];
location = [[locateArray objectAtIndex:arrayIndex] unsignedIntValue];
if(location >= range.location)
{
if(location > range.location)
{
[locateArray replaceObjectAtIndex:arrayIndex
withObject:beginRangeLocation];
}
[attributeArray replaceObjectAtIndex:arrayIndex
withObject:attributes];
}
else
{
arrayIndex++;
[attributeArray insertObject:attributes atIndex:arrayIndex];
[locateArray insertObject:beginRangeLocation atIndex:arrayIndex];
}
/* Primitive method! Sets attributes and values for a given range of characters, replacing any previous attributes
and values for that range.*/
/*
* Primitive method! Sets attributes and values for a given range of
* characters, replacing any previous attributes and values for that
* range.
*/
/*Sets the attributes for the characters in aRange to attributes. These new attributes replace any attributes
previously associated with the characters in aRange. Raises an NSRangeException if any part of aRange lies beyond
the end of the receiver's characters.
See also: - addAtributes:range:, - removeAttributes:range:*/
/*
* Sets the attributes for the characters in aRange to attributes.
* These new attributes replace any attributes previously associated
* with the characters in aRange. Raises an NSRangeException if any
* part of aRange lies beyond the end of the receiver's characters.
* See also: - addAtributes: range: , - removeAttributes: range:
*/
}
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString
- (void) replaceCharactersInRange: (NSRange)range
withString: (NSString*)aString
{
unsigned int tmpLength,arrayIndex,arraySize,cnt,location,moveLocations;
NSRange effectiveRange;
NSDictionary *attrs;
NSNumber *afterRangeLocation;
unsigned tmpLength, arrayIndex, arraySize, cnt, location, moveLocations;
NSRange effectiveRange;
NSDictionary *attrs;
unsigned afterRangeLoc;
GSAttrInfo *info;
NSZone *z = [self zone];
if(!aString)
if (!aString)
aString = @"";
tmpLength = [self length];
if(range.location < 0 || NSMaxRange(range) > tmpLength)
{
[NSException raise:NSRangeException
format:@"RangeError in method -replaceCharactersInRange:withString: in class NSMutableAttributedString"];
}
arraySize = [locateArray count];
if(NSMaxRange(range) < tmpLength)
{
attrs = _attributesAtIndexEffectiveRange(
NSMaxRange(range),&effectiveRange,tmpLength,attributeArray,locateArray,&arrayIndex);
moveLocations = [aString length] - range.length;
afterRangeLocation =
[NSNumber numberWithUnsignedInt:NSMaxRange(range)+moveLocations];
if(effectiveRange.location > range.location)
if (NSMaxRange(range) > tmpLength)
{
[locateArray replaceObjectAtIndex:arrayIndex
withObject:afterRangeLocation];
[NSException raise: NSRangeException
format: @"RangeError in method -replaceCharactersInRange: "
@"withString: in class NSMutableAttributedString"];
}
else
arraySize = [infoArray count];
if (NSMaxRange(range) < tmpLength)
{
arrayIndex++;
//There shouldn't be anything wrong in putting an object (attrs) in
//an array more than once should there? The object will not change.
[attributeArray insertObject:attrs atIndex:arrayIndex];
[locateArray insertObject:afterRangeLocation atIndex:arrayIndex];
attrs = _attributesAtIndexEffectiveRange(
NSMaxRange(range), &effectiveRange, tmpLength, infoArray, &arrayIndex);
moveLocations = [aString length] - range.length;
afterRangeLoc = NSMaxRange(range) + moveLocations;
if (effectiveRange.location > range.location)
{
info = [infoArray objectAtIndex: arrayIndex];
info->loc = afterRangeLoc;
}
else
{
info = (*infImp)(infCls, infSel, z, attrs, afterRangeLoc);
[infoArray insertObject: info atIndex: ++arrayIndex];
arraySize++;
RELEASE(info);
}
/*
* Everything after our modified range need to be shifted.
*/
if (arrayIndex + 1 < arraySize)
{
unsigned l = arraySize - arrayIndex - 1;
NSRange r = NSMakeRange(arrayIndex + 1, l);
GSAttrInfo *objs[l];
[infoArray getObjects: objs range: r];
for (cnt = 0; cnt < l; cnt++)
{
objs[cnt]->loc += moveLocations;
}
}
arrayIndex--;
}
for(cnt=arrayIndex+1;cnt<arraySize;cnt++)
{
location = [[locateArray objectAtIndex:cnt] unsignedIntValue] + moveLocations;
[locateArray replaceObjectAtIndex:cnt
withObject:[NSNumber numberWithUnsignedInt:location]];
}
arrayIndex--;
}
else
arrayIndex = arraySize - 1;
while(arrayIndex > 0 &&
[[locateArray objectAtIndex:arrayIndex] unsignedIntValue] > range.location)
{
[locateArray removeObjectAtIndex:arrayIndex];
[attributeArray removeObjectAtIndex:arrayIndex];
arrayIndex--;
}
[textChars replaceCharactersInRange:range withString:aString];
{
arrayIndex = arraySize - 1;
}
while (arrayIndex > 0)
{
info = [infoArray objectAtIndex: arrayIndex];
if (info->loc <= range.location)
break;
[infoArray removeObjectAtIndex: arrayIndex];
arrayIndex--;
}
[textChars replaceCharactersInRange: range withString: aString];
}
- (void)dealloc
- (void) dealloc
{
[textChars release];
[attributeArray release];
[locateArray release];
RELEASE(textChars);
RELEASE(infoArray);
[super dealloc];
}

View file

@ -250,10 +250,11 @@
// private method for Unicode level 3 implementation
- (int) _baseLength
{
int count=0;
int blen=0;
while(count < [self length])
if(!uni_isnonsp([self characterAtIndex: count++]))
int count = 0;
int blen = 0;
while (count < _count)
if (!uni_isnonsp(_contents_chars[count++]))
blen++;
return blen;
}

View file

@ -2606,11 +2606,19 @@ else
// private methods for Unicode level 3 implementation
- (int) _baseLength
{
int count=0;
int blen=0;
while (count < [self length])
if (!uni_isnonsp([self characterAtIndex: count++]))
blen++;
int blen = 0;
unsigned len = [self length];
if (len > 0)
{
int count = 0;
SEL caiSel = @selector(characterAtIndex:);
unichar (*caiImp)() = (unichar (*)())[self methodForSelector: caiSel];
while (count < len)
if (!uni_isnonsp((*caiImp)(self, caiSel, count++)))
blen++;
}
return blen;
}