mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
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:
parent
c88c0a1f45
commit
9427d093bc
7 changed files with 806 additions and 547 deletions
11
ChangeLog
11
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue