diff --git a/ChangeLog b/ChangeLog index 14ee33e06..d2ae8ad6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Fri Apr 9 16:22:00 1999 Richard Frith-Macdonald + + * 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 * NSCharacterSets: Updated sets to Version 2.1.8 diff --git a/Headers/gnustep/base/NSAttributedString.h b/Headers/gnustep/base/NSAttributedString.h index 73d2abe67..bfed02fad 100644 --- a/Headers/gnustep/base/NSAttributedString.h +++ b/Headers/gnustep/base/NSAttributedString.h @@ -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 Date: November 1997 + Rewrite by: Richard Frith-Macdonald + 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 diff --git a/Headers/gnustep/base/NSGAttributedString.h b/Headers/gnustep/base/NSGAttributedString.h index 143222b46..59787bced 100644 --- a/Headers/gnustep/base/NSGAttributedString.h +++ b/Headers/gnustep/base/NSGAttributedString.h @@ -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 Date: November 1997 + Rewrite by: Richard Frith-Macdonald + 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 diff --git a/Source/NSAttributedString.m b/Source/NSAttributedString.m index 13bd07324..b2ca76989 100644 --- a/Source/NSAttributedString.m +++ b/Source/NSAttributedString.m @@ -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 Date: November 1997 + Rewrite by: Richard Frith-Macdonald + 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 +#include +#include #include #include @@ -51,6 +53,15 @@ #include #include + +@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 + diff --git a/Source/NSGAttributedString.m b/Source/NSGAttributedString.m index 7f04fb4b8..f07c4d776 100644 --- a/Source/NSGAttributedString.m +++ b/Source/NSGAttributedString.m @@ -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 Date: November 1997 + Rewrite by: Richard Frith-Macdonald + 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 #include #include -@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 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]; } diff --git a/Source/NSGString.m b/Source/NSGString.m index f492ff8fa..c7cd4b886 100644 --- a/Source/NSGString.m +++ b/Source/NSGString.m @@ -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; } diff --git a/Source/NSString.m b/Source/NSString.m index 3d217fc18..a612302a1 100644 --- a/Source/NSString.m +++ b/Source/NSString.m @@ -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; }