66 [
string setAttributedString:aString];
89 _rangeEntries = [makeRangeEntry(CPMakeRange(0, _string.length), attributes)];
127 - (unsigned)_indexOfEntryWithIndex:(
unsigned)anIndex
129 if (anIndex < 0 || anIndex > _string.length || anIndex === undefined)
133 var sortFunction =
function(index, entry)
136 if (CPLocationInRange(index, entry.range))
138 else if (CPMaxRange(entry.range) <= index)
144 return [_rangeEntries indexOfObject:anIndex inSortedRange:nil options:0 usingComparator:sortFunction];
166 - (
CPDictionary)attributesAtIndex:(
unsigned)anIndex effectiveRange:(CPRangePointer)aRange
169 var entryIndex = [
self _indexOfEntryWithIndex:anIndex];
174 var matchingRange = _rangeEntries[entryIndex];
177 aRange.location = matchingRange.range.location;
178 aRange.length = matchingRange.range.length;
181 return matchingRange.attributes;
205 - (
CPDictionary)attributesAtIndex:(
unsigned)anIndex longestEffectiveRange:(CPRangePointer)aRange inRange:(CPRange)rangeLimit
207 var startingEntryIndex = [
self _indexOfEntryWithIndex:anIndex];
213 return _rangeEntries[startingEntryIndex].attributes;
215 if (
CPRangeInRange(_rangeEntries[startingEntryIndex].range, rangeLimit))
217 aRange.location = rangeLimit.location;
218 aRange.length = rangeLimit.length;
220 return _rangeEntries[startingEntryIndex].attributes;
224 var nextRangeIndex = startingEntryIndex - 1,
225 currentEntry = _rangeEntries[startingEntryIndex],
226 comparisonDict = currentEntry.attributes;
228 while (nextRangeIndex >= 0)
230 var nextEntry = _rangeEntries[nextRangeIndex];
232 if (CPMaxRange(nextEntry.range) > rangeLimit.location && [nextEntry.attributes isEqualToDictionary:comparisonDict])
234 currentEntry = nextEntry;
241 aRange.location = MAX(currentEntry.range.location, rangeLimit.location);
244 currentEntry = _rangeEntries[startingEntryIndex];
245 nextRangeIndex = startingEntryIndex + 1;
247 while (nextRangeIndex < _rangeEntries.length)
249 var nextEntry = _rangeEntries[nextRangeIndex];
251 if (nextEntry.range.location < CPMaxRange(rangeLimit) && [nextEntry.attributes isEqualToDictionary:comparisonDict])
253 currentEntry = nextEntry;
260 aRange.length = MIN(CPMaxRange(currentEntry.range), CPMaxRange(rangeLimit)) - aRange.location;
262 return comparisonDict;
281 - (id)attribute:(
CPString)attribute atIndex:(
unsigned)index effectiveRange:(CPRangePointer)aRange
288 aRange.
length = _string.length;
318 - (id)attribute:(
CPString)attribute atIndex:(
unsigned)anIndex longestEffectiveRange:(CPRangePointer)aRange inRange:(CPRange)rangeLimit
321 var startingEntryIndex = [
self _indexOfEntryWithIndex:anIndex];
323 if (startingEntryIndex ==
CPNotFound || !attribute)
327 return [_rangeEntries[startingEntryIndex].attributes objectForKey:attribute];
329 if (
CPRangeInRange(_rangeEntries[startingEntryIndex].range, rangeLimit))
331 aRange.location = rangeLimit.location;
332 aRange.
length = rangeLimit.length;
334 return [_rangeEntries[startingEntryIndex].attributes objectForKey:attribute];
338 var nextRangeIndex = startingEntryIndex - 1,
339 currentEntry = _rangeEntries[startingEntryIndex],
340 comparisonAttribute = [currentEntry.attributes objectForKey:attribute];
342 while (nextRangeIndex >= 0)
344 var nextEntry = _rangeEntries[nextRangeIndex];
346 if (CPMaxRange(nextEntry.range) > rangeLimit.location &&
isEqual(comparisonAttribute, [nextEntry.attributes objectForKey:attribute]))
348 currentEntry = nextEntry;
355 aRange.location = MAX(currentEntry.range.location, rangeLimit.location);
358 currentEntry = _rangeEntries[startingEntryIndex];
359 nextRangeIndex = startingEntryIndex + 1;
361 while (nextRangeIndex < _rangeEntries.length)
363 var nextEntry = _rangeEntries[nextRangeIndex];
365 if (nextEntry.range.location < CPMaxRange(rangeLimit) &&
isEqual(comparisonAttribute, [nextEntry.attributes objectForKey:attribute]))
367 currentEntry = nextEntry;
374 aRange.length = MIN(CPMaxRange(currentEntry.range), CPMaxRange(rangeLimit)) - aRange.location;
376 return comparisonAttribute;
391 if (_string != [aString
string])
398 length = _string.length;
400 while (CPMaxRange(CPUnionRange(myRange, comparisonRange)) < length)
402 if (
CPIntersectionRange(myRange, comparisonRange).length > 0 && ![myAttributes isEqualToDictionary:comparisonAttributes])
404 if (CPMaxRange(myRange) < CPMaxRange(comparisonRange))
422 if (anObject ==
self)
425 if ([anObject isKindOfClass:[
self class]])
441 if (!aRange || CPMaxRange(aRange) > _string.length || aRange.location < 0)
443 reason:"tried to get attributedSubstring for an invalid range: "+(aRange?CPStringFromRange(aRange):"nil")];
445 var newString = [[
CPAttributedString alloc] initWithString:_string.substring(aRange.location, CPMaxRange(aRange))],
446 entryIndex = [
self _indexOfEntryWithIndex:aRange.location],
447 currentRangeEntry = _rangeEntries[entryIndex],
448 lastIndex = CPMaxRange(aRange);
450 newString._rangeEntries = [];
452 while (currentRangeEntry && CPMaxRange(currentRangeEntry.range) < lastIndex)
455 newEntry.range.location -= aRange.location;
457 if (newEntry.range.location < 0)
459 newEntry.range.length += newEntry.range.location;
460 newEntry.range.location = 0;
463 newString._rangeEntries.push(newEntry);
464 currentRangeEntry = _rangeEntries[++entryIndex];
467 if (currentRangeEntry)
471 newRangeEntry.range.length = CPMaxRange(aRange) - newRangeEntry.range.location;
472 newRangeEntry.range.location -= aRange.location;
474 if (newRangeEntry.range.location < 0)
476 newRangeEntry.range.length += newRangeEntry.range.location;
477 newRangeEntry.range.location = 0;
480 newString._rangeEntries.push(newRangeEntry);
501 - (void)replaceCharactersInRange:(CPRange)aRange withString:(
CPString)aString
506 var startingIndex = [
self _indexOfEntryWithIndex:aRange.location],
507 startingRangeEntry = _rangeEntries[startingIndex],
508 endingIndex = [
self _indexOfEntryWithIndex:MAX(CPMaxRange(aRange) - 1, 0)],
509 endingRangeEntry = _rangeEntries[endingIndex],
510 additionalLength = aString.
length - aRange.length;
512 _string = _string.substring(0, aRange.location) + aString + _string.substring(CPMaxRange(aRange));
514 if (startingIndex == endingIndex)
515 startingRangeEntry.range.
length += additionalLength;
518 endingRangeEntry.range.length = CPMaxRange(endingRangeEntry.range) - CPMaxRange(aRange);
519 endingRangeEntry.range.location = CPMaxRange(aRange);
521 startingRangeEntry.range.length = CPMaxRange(aRange) - startingRangeEntry.range.location;
523 _rangeEntries.splice(startingIndex, endingIndex - startingIndex);
526 endingIndex = startingIndex + 1;
528 while (endingIndex < _rangeEntries.length)
529 _rangeEntries[endingIndex++].range.location += additionalLength;
536 - (void)deleteCharactersInRange:(CPRange)aRange
555 var startingEntryIndex = [
self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
556 endingEntryIndex = [
self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
557 current = startingEntryIndex;
560 endingEntryIndex = _rangeEntries.length;
562 while (current < endingEntryIndex)
563 _rangeEntries[current++].attributes = [aDictionary
copy];
566 [
self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
581 var startingEntryIndex = [
self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
582 endingEntryIndex = [
self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
583 current = startingEntryIndex;
586 endingEntryIndex = _rangeEntries.length;
588 while (current < endingEntryIndex)
590 var keys = [aDictionary
allKeys],
591 count = [keys count];
594 [_rangeEntries[current].attributes setObject:[aDictionary
objectForKey:keys[count]] forKey:keys[count]];
600 [
self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
615 - (void)addAttribute:(
CPString)anAttribute value:(
id)aValue range:(CPRange)aRange
626 - (void)removeAttribute:(
CPString)anAttribute range:(CPRange)aRange
628 var startingEntryIndex = [
self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
629 endingEntryIndex = [
self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
630 current = startingEntryIndex;
633 endingEntryIndex = _rangeEntries.length;
635 while (current < endingEntryIndex)
636 [_rangeEntries[current++].attributes removeObjectForKey:anAttribute];
639 [
self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
664 if (anIndex < 0 || anIndex > [
self length])
665 [
CPException raise:CPRangeException
reason:"tried to insert attributed string at an invalid index: "+anIndex];
667 var entryIndexOfNextEntry = [
self _indexOfRangeEntryForIndex:anIndex splitOnMaxIndex:YES],
668 otherRangeEntries = aString._rangeEntries,
669 length = [aString
length];
672 entryIndexOfNextEntry = _rangeEntries.length;
674 _string = _string.substring(0, anIndex) + aString._string + _string.substring(anIndex);
676 var current = entryIndexOfNextEntry;
677 while (current < _rangeEntries.length)
678 _rangeEntries[current++].range.location += length;
680 var newRangeEntryCount = otherRangeEntries.length,
683 while (index < newRangeEntryCount)
686 entryCopy.range.location += anIndex;
688 _rangeEntries.splice(entryIndexOfNextEntry - 1 + index, 0, entryCopy);
716 _string = aString._string;
720 count = aString._rangeEntries.
length;
722 for (; i < count; i++)
727 - (Number)_indexOfRangeEntryForIndex:(
unsigned)characterIndex splitOnMaxIndex:(BOOL)split
729 var index = [
self _indexOfEntryWithIndex:characterIndex];
734 var rangeEntry = _rangeEntries[index];
736 if (rangeEntry.range.location == characterIndex || (CPMaxRange(rangeEntry.range) - 1 == characterIndex && !split))
739 var newEntries = splitRangeEntryAtIndex(rangeEntry, characterIndex);
740 _rangeEntries.splice(index, 1, newEntries[0], newEntries[1]);
746 - (void)_coalesceRangeEntriesFromIndex:(
unsigned)start toIndex:(
unsigned)end
750 if (end >= _rangeEntries.length)
751 end = _rangeEntries.length - 1;
753 while (current < end)
755 var a = _rangeEntries[current],
756 b = _rangeEntries[current + 1];
758 if ([a.attributes isEqualToDictionary:b.attributes])
760 a.range.length = CPMaxRange(b.range) - a.range.location;
761 _rangeEntries.splice(current + 1, 1);
810 if ([a respondsToSelector:
@selector(isEqual:)] && [a isEqual:b])
818 return {range:aRange, attributes:[attributes
copy]};
823 return makeRangeEntry(CPMakeRangeCopy(aRangeEntry.range), [aRangeEntry.attributes copy]);
829 cachedIndex = CPMaxRange(aRangeEntry.range);
831 aRangeEntry.range.length = anIndex - aRangeEntry.range.location;
832 newRangeEntry.range.location = anIndex;
833 newRangeEntry.range.length = cachedIndex - anIndex;
834 newRangeEntry.attributes = [newRangeEntry.attributes copy];
836 return [aRangeEntry, newRangeEntry];