mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-29 21:47:39 +00:00
Updated with fixes from base
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@6241 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
882971a38a
commit
9f9e15ced7
1 changed files with 181 additions and 81 deletions
|
@ -43,8 +43,11 @@
|
|||
#include <Foundation/NSException.h>
|
||||
#include <Foundation/NSRange.h>
|
||||
#include <Foundation/NSGArray.h>
|
||||
#include <Foundation/NSDebug.h>
|
||||
#include <AppKit/NSTextStorage.h>
|
||||
|
||||
#define SANITY_CHECKS 0
|
||||
|
||||
@interface GSTextStorage : NSTextStorage
|
||||
{
|
||||
NSMutableString *textChars;
|
||||
|
@ -80,6 +83,12 @@
|
|||
NSDeallocateObject(self);
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
return [NSString stringWithFormat: @"Attributes at %u are - %@",
|
||||
loc, attrs];
|
||||
}
|
||||
|
||||
- (Class) classForPortCoder
|
||||
{
|
||||
return [self class];
|
||||
|
@ -198,25 +207,35 @@ _attributesAtIndexEffectiveRange(
|
|||
unsigned low, high, used, cnt, nextLoc;
|
||||
GSTextInfo *found = nil;
|
||||
|
||||
used = (*cntImp)(infoArray, cntSel);
|
||||
NSCAssert(used > 0, NSInternalInconsistencyException);
|
||||
high = used - 1;
|
||||
|
||||
if (index >= tmpLength)
|
||||
{
|
||||
if (index == tmpLength)
|
||||
{
|
||||
*foundIndex = tmpLength;
|
||||
return nil;
|
||||
found = OBJECTAT(high);
|
||||
if (foundIndex != 0)
|
||||
{
|
||||
*foundIndex = high;
|
||||
}
|
||||
if (aRange != 0)
|
||||
{
|
||||
aRange->location = found->loc;
|
||||
aRange->length = tmpLength - found->loc;
|
||||
}
|
||||
return found->attrs;
|
||||
}
|
||||
[NSException raise: NSRangeException
|
||||
format: @"index is out of range in function "
|
||||
@"_attributesAtIndexEffectiveRange()"];
|
||||
}
|
||||
|
||||
used = (*cntImp)(infoArray, cntSel);
|
||||
|
||||
/*
|
||||
* Binary search for efficiency in huge attributed strings
|
||||
*/
|
||||
low = 0;
|
||||
high = used - 1;
|
||||
while (low <= high)
|
||||
{
|
||||
cnt = (low + high) / 2;
|
||||
|
@ -240,12 +259,12 @@ _attributesAtIndexEffectiveRange(
|
|||
if (found->loc == index || index < nextLoc)
|
||||
{
|
||||
//Found
|
||||
if (aRange)
|
||||
if (aRange != 0)
|
||||
{
|
||||
aRange->location = found->loc;
|
||||
aRange->length = nextLoc - found->loc;
|
||||
}
|
||||
if (foundIndex)
|
||||
if (foundIndex != 0)
|
||||
{
|
||||
*foundIndex = cnt;
|
||||
}
|
||||
|
@ -263,6 +282,33 @@ _attributesAtIndexEffectiveRange(
|
|||
|
||||
@implementation GSTextStorage
|
||||
|
||||
#if SANITY_CHECKS
|
||||
|
||||
#define SANITY() [self sanity]
|
||||
|
||||
- (void) sanity
|
||||
{
|
||||
GSTextInfo *info;
|
||||
unsigned i;
|
||||
unsigned l = 0;
|
||||
unsigned len = [textChars length];
|
||||
unsigned c = (*cntImp)(infoArray, cntSel);
|
||||
|
||||
NSAssert(c > 0, NSInternalInconsistencyException);
|
||||
info = OBJECTAT(0);
|
||||
NSAssert(info->loc == 0, NSInternalInconsistencyException);
|
||||
for (i = 1; i < c; i++)
|
||||
{
|
||||
info = OBJECTAT(i);
|
||||
NSAssert(info->loc > l, NSInternalInconsistencyException);
|
||||
NSAssert(info->loc <= len, NSInternalInconsistencyException);
|
||||
l = info->loc;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define SANITY()
|
||||
#endif
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
_setup();
|
||||
|
@ -356,51 +402,72 @@ _attributesAtIndexEffectiveRange(
|
|||
NSZone *z = [self zone];
|
||||
GSTextInfo *info;
|
||||
|
||||
if (!attributes)
|
||||
attributes = [NSDictionary dictionary];
|
||||
if (range.length == 0)
|
||||
{
|
||||
NSWarnMLog(@"Attempt to set attribute for zero-length range", 0);
|
||||
return;
|
||||
}
|
||||
if (attributes == nil)
|
||||
{
|
||||
attributes = [NSDictionary dictionary];
|
||||
}
|
||||
SANITY();
|
||||
tmpLength = [textChars length];
|
||||
GS_RANGE_CHECK(range, tmpLength);
|
||||
arraySize = (*cntImp)(infoArray, cntSel);
|
||||
if (NSMaxRange(range) < tmpLength)
|
||||
beginRangeLoc = range.location;
|
||||
afterRangeLoc = NSMaxRange(range);
|
||||
if (afterRangeLoc < tmpLength)
|
||||
{
|
||||
/*
|
||||
* Locate the first range that extends beyond our range.
|
||||
*/
|
||||
attrs = _attributesAtIndexEffectiveRange(
|
||||
NSMaxRange(range), &effectiveRange, tmpLength, infoArray, &arrayIndex);
|
||||
|
||||
afterRangeLoc = NSMaxRange(range);
|
||||
if (effectiveRange.location > range.location)
|
||||
afterRangeLoc, &effectiveRange, tmpLength, infoArray, &arrayIndex);
|
||||
if (effectiveRange.location > beginRangeLoc)
|
||||
{
|
||||
/*
|
||||
* The located range also starts at or after our range.
|
||||
*/
|
||||
info = OBJECTAT(arrayIndex);
|
||||
info->loc = afterRangeLoc;
|
||||
arrayIndex--;
|
||||
}
|
||||
else
|
||||
else if (effectiveRange.location < beginRangeLoc)
|
||||
{
|
||||
/*
|
||||
* The located range starts before our range.
|
||||
* Create a subrange to go from our end to the end of the old range.
|
||||
*/
|
||||
info = NEWINFO(z, attrs, afterRangeLoc);
|
||||
arrayIndex++;
|
||||
INSOBJECT(info, arrayIndex);
|
||||
RELEASE(info);
|
||||
arrayIndex--;
|
||||
}
|
||||
arrayIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
arrayIndex = arraySize - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any ranges completely within ours
|
||||
*/
|
||||
while (arrayIndex > 0)
|
||||
{
|
||||
info = OBJECTAT(arrayIndex-1);
|
||||
if (info->loc < range.location)
|
||||
if (info->loc < beginRangeLoc)
|
||||
break;
|
||||
REMOVEAT(arrayIndex);
|
||||
arrayIndex--;
|
||||
}
|
||||
|
||||
beginRangeLoc = range.location;
|
||||
info = OBJECTAT(arrayIndex);
|
||||
location = info->loc;
|
||||
if (location >= range.location)
|
||||
if (location >= beginRangeLoc)
|
||||
{
|
||||
if (location > range.location)
|
||||
if (location > beginRangeLoc)
|
||||
{
|
||||
info->loc = beginRangeLoc;
|
||||
}
|
||||
|
@ -413,92 +480,125 @@ _attributesAtIndexEffectiveRange(
|
|||
INSOBJECT(info, arrayIndex);
|
||||
RELEASE(info);
|
||||
}
|
||||
|
||||
/* post changes */
|
||||
|
||||
[self edited: NSTextStorageEditedAttributes
|
||||
range: range
|
||||
changeInLength: 0];
|
||||
|
||||
|
||||
SANITY();
|
||||
}
|
||||
|
||||
- (void) replaceCharactersInRange: (NSRange)range
|
||||
withString: (NSString*)aString
|
||||
{
|
||||
unsigned aLength;
|
||||
unsigned tmpLength, arrayIndex, arraySize, cnt, moveLocations;
|
||||
unsigned tmpLength, arrayIndex, arraySize;
|
||||
NSRange effectiveRange;
|
||||
NSDictionary *attrs;
|
||||
unsigned afterRangeLoc;
|
||||
GSTextInfo *info;
|
||||
int moveLocations;
|
||||
NSZone *z = [self zone];
|
||||
unsigned start;
|
||||
|
||||
if (!aString)
|
||||
aString = @"";
|
||||
aLength = [aString length];
|
||||
SANITY();
|
||||
if (aString == nil)
|
||||
{
|
||||
aString = @"";
|
||||
}
|
||||
tmpLength = [textChars length];
|
||||
GS_RANGE_CHECK(range, tmpLength);
|
||||
arraySize = (*cntImp)(infoArray, cntSel);
|
||||
if (NSMaxRange(range) < tmpLength)
|
||||
if (range.location == tmpLength)
|
||||
{
|
||||
attrs = _attributesAtIndexEffectiveRange(
|
||||
NSMaxRange(range), &effectiveRange, tmpLength, infoArray, &arrayIndex);
|
||||
|
||||
moveLocations = aLength - range.length;
|
||||
afterRangeLoc = NSMaxRange(range) + moveLocations;
|
||||
|
||||
if (effectiveRange.location > range.location)
|
||||
{
|
||||
info = OBJECTAT(arrayIndex);
|
||||
info->loc = afterRangeLoc;
|
||||
}
|
||||
else
|
||||
{
|
||||
info = NEWINFO(z, attrs, afterRangeLoc);
|
||||
arrayIndex++;
|
||||
INSOBJECT(info, arrayIndex);
|
||||
arraySize++;
|
||||
RELEASE(info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything after our modified range need to be shifted.
|
||||
* Special case - replacing a zero length string at the end
|
||||
* simply appends the new string and attributes are inherited.
|
||||
*/
|
||||
if (arrayIndex + 1 < arraySize)
|
||||
[textChars appendString: aString];
|
||||
SANITY();
|
||||
return;
|
||||
}
|
||||
|
||||
arraySize = (*cntImp)(infoArray, cntSel);
|
||||
if (arraySize == 1)
|
||||
{
|
||||
/*
|
||||
* Special case - if the string has only one set of attributes
|
||||
* then the replacement characters will get them too.
|
||||
*/
|
||||
[textChars replaceCharactersInRange: range withString: aString];
|
||||
SANITY();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the attributes to associate with our replacement string.
|
||||
* Should be those of the first character replaced.
|
||||
* If the range replaced is empty, we use the attributes of the
|
||||
* previous character (if possible).
|
||||
*/
|
||||
if (range.length == 0 && range.location > 0)
|
||||
start = range.location - 1;
|
||||
else
|
||||
start = range.location;
|
||||
attrs = _attributesAtIndexEffectiveRange(start, &effectiveRange,
|
||||
tmpLength, infoArray, &arrayIndex);
|
||||
|
||||
arrayIndex++;
|
||||
if (NSMaxRange(effectiveRange) > NSMaxRange(range))
|
||||
{
|
||||
info = NEWINFO(z, attrs, NSMaxRange(range));
|
||||
INSOBJECT(info, arrayIndex);
|
||||
arraySize++;
|
||||
}
|
||||
else if (NSMaxRange(effectiveRange) < NSMaxRange(range))
|
||||
{
|
||||
/*
|
||||
* Remove all range info for ranges enclosed within the one
|
||||
* we are replacing. Adjust the start point of a range that
|
||||
* extends beyond ours.
|
||||
*/
|
||||
info = OBJECTAT(arrayIndex);
|
||||
if (info->loc < NSMaxRange(range))
|
||||
{
|
||||
unsigned l = arraySize - arrayIndex - 1;
|
||||
NSRange r = NSMakeRange(arrayIndex + 1, l);
|
||||
GSTextInfo *objs[l];
|
||||
|
||||
[infoArray getObjects: objs range: r];
|
||||
for (cnt = 0; cnt < l; cnt++)
|
||||
int next = arrayIndex + 1;
|
||||
|
||||
while (next < arraySize)
|
||||
{
|
||||
objs[cnt]->loc += moveLocations;
|
||||
GSTextInfo *n = OBJECTAT(next);
|
||||
if (n->loc <= NSMaxRange(range))
|
||||
{
|
||||
REMOVEAT(arrayIndex);
|
||||
arraySize--;
|
||||
info = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
arrayIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
arrayIndex = arraySize - 1;
|
||||
info->loc = NSMaxRange(range);
|
||||
}
|
||||
|
||||
while (arrayIndex > 0)
|
||||
moveLocations = [aString length] - range.length;
|
||||
if (effectiveRange.location == range.location
|
||||
&& (moveLocations + range.length) == 0)
|
||||
{
|
||||
/*
|
||||
* If we are replacing a range with a zero length string and the
|
||||
* range we are using matches the range replaced, then we must
|
||||
* remove it from the array to avoid getting a zero length range.
|
||||
*/
|
||||
arrayIndex--;
|
||||
REMOVEAT(arrayIndex);
|
||||
arraySize--;
|
||||
}
|
||||
|
||||
SANITY();
|
||||
/*
|
||||
* Now adjust the positions of the ranges following the one we are using.
|
||||
*/
|
||||
while (arrayIndex < arraySize)
|
||||
{
|
||||
info = OBJECTAT(arrayIndex);
|
||||
if (info->loc <= range.location)
|
||||
break;
|
||||
REMOVEAT(arrayIndex);
|
||||
arrayIndex--;
|
||||
info->loc += moveLocations;
|
||||
arrayIndex++;
|
||||
}
|
||||
SANITY();
|
||||
[textChars replaceCharactersInRange: range withString: aString];
|
||||
|
||||
/* notify of changes */
|
||||
|
||||
[self edited: NSTextStorageEditedCharacters
|
||||
range: range
|
||||
changeInLength: [aString length] - range.length];
|
||||
|
||||
SANITY();
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue