mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-31 19:00:47 +00:00
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:
parent
74c70bafcc
commit
78a9ee7a6b
3 changed files with 95 additions and 205 deletions
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue