gui: implement NSLayoutManager temporary attributes

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@32503 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
ericwa 2011-03-09 05:28:47 +00:00
parent 74c70bafcc
commit 78a9ee7a6b
3 changed files with 95 additions and 205 deletions

View file

@ -1,3 +1,8 @@
2011-03-08 Eric Wasylishen <ewasylishen@gmail.com>
* Source/NSLayoutManager.m: Implement temporary attributes
* Header/AppKit/NSLayoutManager.h: Add a _temporaryAttributes ivar
2011-03-07 Fred Kiefer <FredKiefer@gmx.de> 2011-03-07 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSGraphicsContext.m (+saveGraphicsState): Release the * Source/NSGraphicsContext.m (+saveGraphicsState): Release the

View file

@ -67,6 +67,8 @@ typedef enum {
should release the old value and retain the new one. It is nil originally should release the old value and retain the new one. It is nil originally
and will be released when the NSLayoutManager is deallocated. */ and will be released when the NSLayoutManager is deallocated. */
NSMutableDictionary *_typingAttributes; NSMutableDictionary *_typingAttributes;
NSMutableAttributedString *_temporaryAttributes;
} }
/* TODO */ /* TODO */

View file

@ -133,6 +133,19 @@ first. Remaining cases, highest priority first:
} }
@end @end
/**
* Temporary attributes are implemented using an NSMutableAttributedString
* stored in the _temporaryAttributes ivar. We only care about this attributed
* string's attributes, not its characters, so to save space, _temporaryAttributes
* is initialized with an instance of the following NSMutableString subclass,
* which doesn't store any character data.
*/
@interface GSDummyMutableString : NSMutableString
{
NSUInteger _length;
}
- (id)initWithLength: (NSUInteger)aLength;
@end
/* Helper for searching for the line frag of a glyph. */ /* Helper for searching for the line frag of a glyph. */
#define LINEFRAG_FOR_GLYPH(glyph) \ #define LINEFRAG_FOR_GLYPH(glyph) \
@ -1918,6 +1931,7 @@ static void GSDrawPatternLine(NSPoint start, NSPoint end, NSInteger pattern, CGF
-(void) dealloc -(void) dealloc
{ {
DESTROY(_typingAttributes); DESTROY(_typingAttributes);
DESTROY(_temporaryAttributes);
[super dealloc]; [super dealloc];
} }
@ -2177,6 +2191,16 @@ this file describes this.
if (!(mask & NSTextStorageEditedCharacters)) if (!(mask & NSTextStorageEditedCharacters))
lengthChange = 0; lengthChange = 0;
if (_temporaryAttributes != nil && (mask & NSTextStorageEditedCharacters) != 0)
{
NSRange oldRange = NSMakeRange(range.location, range.length - lengthChange);
NSString *replacementString = [[GSDummyMutableString alloc] initWithLength: range.length];
[_temporaryAttributes replaceCharactersInRange: oldRange
withString: replacementString];
[replacementString release];
}
[self invalidateGlyphsForCharacterRange: invalidatedRange [self invalidateGlyphsForCharacterRange: invalidatedRange
changeInLength: lengthChange changeInLength: lengthChange
actualCharacterRange: &r]; actualCharacterRange: &r];
@ -2607,143 +2631,92 @@ no_soft_invalidation:
@end @end
@implementation GSDummyMutableString
- (id)initWithLength: (NSUInteger)aLength
{
self = [super init];
if (self != nil)
{
self->_length = aLength;
}
return self;
}
- (NSUInteger)length
{
return _length;
}
- (unichar)characterAtIndex: (NSUInteger)index
{
return 0;
}
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString
{
_length = (_length - range.length) + [aString length];
}
- (id) copyWithZone: (NSZone*)zone
{
return [self mutableCopyWithZone: zone];
}
- (id) mutableCopyWithZone: (NSZone*)zone
{
return [[GSDummyMutableString allocWithZone: zone] initWithLength: _length];
}
@end
@implementation NSLayoutManager (temporaryattributes) @implementation NSLayoutManager (temporaryattributes)
- (NSMutableAttributedString*) _temporaryAttributes
{
if (_temporaryAttributes == nil)
{
NSString *dummyString = [[GSDummyMutableString alloc] initWithLength: [[self textStorage] length]];
_temporaryAttributes = [[NSMutableAttributedString alloc] initWithString: dummyString];
[dummyString release];
}
return _temporaryAttributes;
}
- (void) setTemporaryAttributes: (NSDictionary *)attrs - (void) setTemporaryAttributes: (NSDictionary *)attrs
forCharacterRange: (NSRange)range forCharacterRange: (NSRange)range
{ {
// to be implemented [[self _temporaryAttributes] setAttributes: attrs range: range];
[self invalidateDisplayForCharacterRange: range];
} }
- (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index - (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index
effectiveRange: (NSRange*)longestRange effectiveRange: (NSRange*)longestRange
{ {
// to be implemented return [[self _temporaryAttributes] attributesAtIndex: index effectiveRange: longestRange];
return nil;
} }
- (void) addTemporaryAttributes: (NSDictionary *)attrs - (void) addTemporaryAttributes: (NSDictionary *)attrs
forCharacterRange: (NSRange)range forCharacterRange: (NSRange)range
{ {
NSRange effectiveRange; [[self _temporaryAttributes] addAttributes: attrs range: range];
NSDictionary *attrDict; [self invalidateDisplayForCharacterRange: range];
NSMutableDictionary *newDict;
unsigned int tmpLength;
if (!attrs)
{
[NSException raise: NSInvalidArgumentException
format: @"attributes is nil in method -addTemporaryAttributes:forCharacterRange: "
@"in class NSLayoutManager"];
}
tmpLength = [_textStorage length];
if (NSMaxRange(range) > tmpLength)
{
[NSException raise: NSRangeException
format: @"RangeError in method -addTemporaryAttributes:forCharacterRange: "
@"in class NSLayoutManager"];
}
attrDict = [self temporaryAttributesAtCharacterIndex: range.location
effectiveRange: &effectiveRange];
while (effectiveRange.location < NSMaxRange(range))
{
effectiveRange = NSIntersectionRange(range, effectiveRange);
newDict = [attrDict mutableCopy];
[newDict addEntriesFromDictionary: attrs];
[self setTemporaryAttributes: newDict
forCharacterRange: effectiveRange];
RELEASE(newDict);
if (NSMaxRange(effectiveRange) >= NSMaxRange(range))
{
effectiveRange.location = NSMaxRange(range);// stop the loop...
}
else if (NSMaxRange(effectiveRange) < tmpLength)
{
attrDict = [self temporaryAttributesAtCharacterIndex: NSMaxRange(effectiveRange)
effectiveRange: &effectiveRange];
}
}
} }
- (void) addTemporaryAttribute: (NSString *)attr - (void) addTemporaryAttribute: (NSString *)attr
value: (id)value value: (id)value
forCharacterRange: (NSRange)range forCharacterRange: (NSRange)range
{ {
NSDictionary *dict = [[NSDictionary alloc] initWithObjects: &value [[self _temporaryAttributes] addAttribute: attr value: value range: range];
forKeys: &attr [self invalidateDisplayForCharacterRange: range];
count: 1];
[self addTemporaryAttributes: dict forCharacterRange: range];
RELEASE(dict);
} }
- (void) removeTemporaryAttribute: (NSString *)attr - (void) removeTemporaryAttribute: (NSString *)attr
forCharacterRange: (NSRange)range forCharacterRange: (NSRange)range
{ {
NSRange effectiveRange; [[self _temporaryAttributes] removeAttribute: attr range: range];
NSDictionary *attrDict; [self invalidateDisplayForCharacterRange: range];
NSMutableDictionary *newDict;
unsigned int tmpLength;
tmpLength = [_textStorage length];
if (NSMaxRange(range) > tmpLength)
{
[NSException raise: NSRangeException
format: @"RangeError in method -removeTemporaryAttribute:forCharacterRange: "
@"in class NSLayoutManager"];
}
attrDict = [self temporaryAttributesAtCharacterIndex: range.location
effectiveRange: &effectiveRange];
while (effectiveRange.location < NSMaxRange(range))
{
effectiveRange = NSIntersectionRange(range, effectiveRange);
newDict = [attrDict mutableCopy];
[newDict removeObjectForKey: attr];
[self setTemporaryAttributes: newDict
forCharacterRange: effectiveRange];
RELEASE(newDict);
if (NSMaxRange(effectiveRange) >= NSMaxRange(range))
{
effectiveRange.location = NSMaxRange(range);// stop the loop...
}
else if (NSMaxRange(effectiveRange) < tmpLength)
{
attrDict = [self temporaryAttributesAtCharacterIndex: NSMaxRange(effectiveRange)
effectiveRange: &effectiveRange];
}
}
} }
- (id) temporaryAttribute: (NSString *)attr - (id) temporaryAttribute: (NSString *)attr
atCharacterIndex: (NSUInteger)index atCharacterIndex: (NSUInteger)index
effectiveRange: (NSRange*)range effectiveRange: (NSRange*)range
{ {
NSDictionary *tmpDictionary; return [[self _temporaryAttributes] attribute: attr atIndex: index effectiveRange: range];
if (attr == nil)
{
if (range != 0)
{
*range = NSMakeRange(0, [_textStorage length]);
/*
* If attr is nil, then the attribute will not exist in the
* entire text - therefore range of the entire text must be correct
*/
}
return nil;
}
tmpDictionary = [self temporaryAttributesAtCharacterIndex: index
effectiveRange: range];
return [tmpDictionary objectForKey: attr];
} }
- (id) temporaryAttribute: (NSString *)attr - (id) temporaryAttribute: (NSString *)attr
@ -2751,113 +2724,23 @@ no_soft_invalidation:
longestEffectiveRange: (NSRange*)longestRange longestEffectiveRange: (NSRange*)longestRange
inRange: (NSRange)range inRange: (NSRange)range
{ {
id attrValue; return [[self _temporaryAttributes] attribute: attr atIndex: index longestEffectiveRange: longestRange inRange: range];
id tmpAttrValue;
NSRange tmpRange;
if (NSMaxRange(range) > [_textStorage length])
{
[NSException raise: NSRangeException
format: @"RangeError in method -temporaryAttribute:atCharacterIndex:longestEffectiveRange:inRange: in class NSLayoutManager"];
}
if (attr == nil)
return nil;
attrValue = [self temporaryAttribute: attr
atCharacterIndex: index
effectiveRange: longestRange];
if (longestRange == 0)
return attrValue;
while (longestRange->location > range.location)
{
//Check extend range backwards
tmpAttrValue = [self temporaryAttribute: attr
atCharacterIndex: longestRange->location-1
effectiveRange: &tmpRange];
if (tmpAttrValue == attrValue
|| (attrValue != nil && [attrValue isEqual: tmpAttrValue]))
{
longestRange->length = NSMaxRange(*longestRange) - tmpRange.location;
longestRange->location = tmpRange.location;
}
else
{
break;
}
}
while (NSMaxRange(*longestRange) < NSMaxRange(range))
{
//Check extend range forwards
tmpAttrValue = [self temporaryAttribute: attr
atCharacterIndex: longestRange->location-1
effectiveRange: &tmpRange];
if (tmpAttrValue == attrValue
|| (attrValue != nil && [attrValue isEqual: tmpAttrValue]))
{
longestRange->length = NSMaxRange(tmpRange) - longestRange->location;
}
else
{
break;
}
}
*longestRange = NSIntersectionRange(*longestRange,range);//Clip to rangeLimit
return attrValue;
} }
- (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index - (NSDictionary *) temporaryAttributesAtCharacterIndex: (NSUInteger)index
longestEffectiveRange: (NSRange*)longestRange longestEffectiveRange: (NSRange*)longestRange
inRange: (NSRange)range inRange: (NSRange)range
{ {
NSDictionary *attrDictionary, *tmpDictionary; return [[self _temporaryAttributes] attributesAtIndex: index longestEffectiveRange: longestRange inRange: range];
NSRange tmpRange; }
if (NSMaxRange(range) > [_textStorage length]) /**
{ * Most of this is implemented in GSLayoutManager
[NSException raise: NSRangeException */
format: @"RangeError in method -temporaryAttributesAtCharacterIndex:longestEffectiveRange:inRange: in class NSLayoutManager"]; - (void) setTextStorage: (NSTextStorage *)aTextStorage
} {
DESTROY(_temporaryAttributes);
attrDictionary = [self temporaryAttributesAtCharacterIndex: index [super setTextStorage: aTextStorage];
effectiveRange: longestRange];
if (longestRange == 0)
return attrDictionary;
while (longestRange->location > range.location)
{
//Check extend range backwards
tmpDictionary = [self temporaryAttributesAtCharacterIndex: longestRange->location-1
effectiveRange: &tmpRange];
if ([tmpDictionary isEqualToDictionary: attrDictionary])
{
longestRange->length = NSMaxRange(*longestRange) - tmpRange.location;
longestRange->location = tmpRange.location;
}
else
{
break;
}
}
while (NSMaxRange(*longestRange) < NSMaxRange(range))
{
//Check extend range forwards
tmpDictionary = [self temporaryAttributesAtCharacterIndex: NSMaxRange(*longestRange)
effectiveRange: &tmpRange];
if ([tmpDictionary isEqualToDictionary: attrDictionary])
{
longestRange->length = NSMaxRange(tmpRange) - longestRange->location;
}
else
{
break;
}
}
//Clip to range
*longestRange = NSIntersectionRange(*longestRange, range);
return attrDictionary;
} }
@end @end