/ CFAttributedString.c
CFAttributedString.c
1 // 2 // CFAttributedString.c 3 // CoreFoundation 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #include <CoreFoundation/CFAttributedString.h> 9 #include "CFRuntime.h" 10 #include "CFInternal.h" 11 #include "macros.h" 12 13 typedef struct 14 { 15 CFRange _range; 16 CFDictionaryRef _dictionary; 17 } __CFRunArrayItem; 18 19 typedef struct __CFAttributedString 20 { 21 CFRuntimeBase _base; 22 CFStringRef _string; 23 __CFRunArrayItem **_attributes; 24 CFIndex _runArrayCount; 25 Boolean _isMutable; 26 } __CFAttributedString; 27 28 static void mergeIntoDictionary(const void* key, const void* value, void* context) { 29 CFMutableDictionaryRef into = (CFMutableDictionaryRef)context; 30 CFDictionarySetValue(into, key, value); 31 } 32 33 static Boolean _CFRunArrayIsEqual(__CFAttributedString *aStr, __CFAttributedString *aStr2) 34 { 35 if (aStr->_runArrayCount != aStr2->_runArrayCount) 36 { 37 return false; 38 } 39 __CFRunArrayItem **inputArray = aStr->_attributes; 40 __CFRunArrayItem **inputArray2 = aStr2->_attributes; 41 if (inputArray == inputArray2) 42 { 43 return true; 44 } 45 if (inputArray == NULL || inputArray2 == NULL) 46 { 47 return false; 48 } 49 for (CFIndex next, i = 0; i < aStr->_runArrayCount; i = next) 50 { 51 __CFRunArrayItem *inp = inputArray[i]; 52 __CFRunArrayItem *inp2 = inputArray2[i]; 53 if (inp) 54 { 55 if (inp2 == NULL) 56 { 57 return false; 58 } 59 if (inp->_range.location != inp2->_range.location || inp->_range.length != inp2->_range.length) 60 { 61 return false; 62 } 63 if (!CFEqual(inp->_dictionary, inp2->_dictionary)) 64 { 65 return false; 66 } 67 next = i + inp->_range.length; 68 } 69 else 70 { 71 if (inp2) 72 { 73 return false; 74 } 75 next = i + 1; 76 } 77 } 78 return true; 79 } 80 81 82 static __CFRunArrayItem * _CFRunArrayItemInit(CFRange range, CFDictionaryRef dict) 83 { 84 __CFRunArrayItem *obj = (__CFRunArrayItem *)malloc(sizeof(__CFRunArrayItem)); 85 obj->_range = range; 86 obj->_dictionary = CFDictionaryCreateCopy(kCFAllocatorDefault, dict); 87 return obj; 88 } 89 90 static void _CFRunArrayItemDestroy(__CFRunArrayItem *ptr) 91 { 92 if (ptr->_dictionary) 93 { 94 CFRelease(ptr->_dictionary); 95 } 96 free(ptr); 97 } 98 99 static void _CFRunArrayDestroyAttributesOfString(__CFAttributedString *aStr) 100 { 101 __CFRunArrayItem **inputArray = aStr->_attributes; 102 if (inputArray == NULL) 103 { 104 return; 105 } 106 for (CFIndex next, i = 0; i < aStr->_runArrayCount; i = next) 107 { 108 __CFRunArrayItem *inp = inputArray[i]; 109 if (inp) 110 { 111 next = i + inp->_range.length; 112 _CFRunArrayItemDestroy(inp); 113 } 114 else 115 { 116 next = i + 1; 117 } 118 } 119 free(aStr->_attributes); 120 } 121 122 static void _CFRunArrayInsert(__CFAttributedString *attrStr, CFDictionaryRef dict, CFRange range, Boolean clearOther, CFStringRef subtractValue) 123 { 124 if (range.length == 0) 125 { 126 return; 127 } 128 129 CFIndex rangeLimit = range.location + range.length; 130 if (rangeLimit > attrStr->_runArrayCount) 131 { 132 __CFRunArrayItem **new = (__CFRunArrayItem **)calloc(sizeof(__CFRunArrayItem *), rangeLimit); 133 if (attrStr->_attributes != NULL) 134 { 135 memcpy(new, attrStr->_attributes, sizeof(__CFRunArrayItem *) * attrStr->_runArrayCount); 136 free(attrStr->_attributes); 137 } 138 attrStr->_attributes = new; 139 attrStr->_runArrayCount = rangeLimit; 140 } 141 142 __CFRunArrayItem **indexArray = attrStr->_attributes; 143 __CFRunArrayItem *ptr = indexArray[range.location]; 144 145 // First entry for this index 146 if (ptr == nil && !subtractValue) 147 { 148 __CFRunArrayItem *obj = _CFRunArrayItemInit(CFRangeMake(range.location, 0), dict); 149 for (CFIndex i = range.location; i < rangeLimit; i++) 150 { 151 if (indexArray[i] == nil) 152 { 153 indexArray[i] = obj; 154 obj->_range.length++; 155 } 156 else 157 { 158 _CFRunArrayInsert(attrStr, dict, CFRangeMake(i, rangeLimit - i), clearOther, subtractValue); 159 break; 160 } 161 } 162 return; 163 } 164 165 Boolean split = false; 166 CFIndex ptrRangeLimit = ptr->_range.location + ptr->_range.length; 167 168 CFMutableDictionaryRef mergeDict = NULL; 169 do { 170 if (clearOther) 171 { 172 mergeDict = (CFMutableDictionaryRef)dict; 173 CFRetain(mergeDict); 174 } 175 else 176 { 177 mergeDict = CFDictionaryCreateMutableCopy(NULL, 0, ptr->_dictionary); 178 if (subtractValue != NULL) 179 { 180 CFDictionaryRemoveValue(mergeDict, subtractValue); 181 } 182 else 183 { 184 CFDictionaryApplyFunction(dict, mergeIntoDictionary, mergeDict); 185 } 186 } 187 188 if (rangeLimit <= ptrRangeLimit && CFEqual(ptr->_dictionary, mergeDict)) 189 { 190 // No need to do anymore - the requested insert dictionary is already there 191 break; // CLEANUP 192 } 193 194 // Check for split at beginning of list 195 if (ptr->_range.location < range.location) 196 { 197 if (CFEqual(ptr->_dictionary, mergeDict)) 198 { 199 // Done with this item but still need to update subsequent run array items 200 CFIndex restStart = ptrRangeLimit; 201 CFIndex restLength = rangeLimit - restStart; 202 _CFRunArrayInsert(attrStr, dict, CFRangeMake(restStart, restLength), clearOther, subtractValue); 203 break; // CLEANUP 204 } 205 split = true; 206 CFIndex objRangeLimit = ptr->_range.location + ptr->_range.length; 207 ptr->_range.length = range.location - ptr->_range.location; 208 // if *ptr goes beyond range, need to copy element for new range after insert 209 if (objRangeLimit > rangeLimit) 210 { 211 __CFRunArrayItem *copy = _CFRunArrayItemInit(CFRangeMake(rangeLimit, objRangeLimit - rangeLimit), ptr->_dictionary); 212 for (CFIndex i = rangeLimit; i < objRangeLimit; i++) 213 { 214 indexArray[i] = copy; 215 } 216 } 217 // fall through - now that preceding (and successive) indices are ready 218 } 219 220 __CFRunArrayItem *obj; 221 CFIndex newRangeLength = __CFMin(range.length, ptrRangeLimit - range.location); 222 if (range.location > 0 && CFEqual(indexArray[range.location - 1]->_dictionary, mergeDict)) 223 { 224 // Can merge with previous 225 obj = indexArray[range.location - 1]; 226 obj->_range.length += newRangeLength; 227 } 228 else if (ptrRangeLimit == rangeLimit && 229 rangeLimit < attrStr->_runArrayCount && 230 CFEqual(mergeDict, indexArray[rangeLimit]->_dictionary)) 231 { 232 // can merge with next 233 indexArray[rangeLimit]->_range.length += rangeLimit - range.location; 234 indexArray[rangeLimit]->_range.location = range.location; 235 for (CFIndex i = range.location; i < rangeLimit; i++) 236 { 237 indexArray[i] = indexArray[rangeLimit]; 238 } 239 break; // CLEANUP 240 } 241 else 242 { 243 obj = _CFRunArrayItemInit(CFRangeMake(range.location, newRangeLength), mergeDict); 244 } 245 246 for (CFIndex i = range.location; i < range.location + newRangeLength; i++) 247 { 248 indexArray[i] = obj; 249 } 250 251 if (ptrRangeLimit > rangeLimit) 252 { 253 if (!split) 254 { 255 ptr->_range.location = rangeLimit; 256 ptr->_range.length = ptr->_range.length - range.length; 257 } 258 break; // CLEANUP 259 } 260 else 261 { 262 if (!split) 263 { 264 _CFRunArrayItemDestroy(ptr); 265 } 266 if (ptrRangeLimit < rangeLimit) 267 { 268 CFIndex restStart = ptrRangeLimit; 269 CFIndex restLength = rangeLimit - restStart; 270 _CFRunArrayInsert(attrStr, dict, CFRangeMake(restStart, restLength), clearOther, subtractValue); 271 } 272 } 273 } while(0); 274 275 // CLEANUP: 276 CFRelease(mergeDict); 277 } 278 279 static void _CFRunArrayCopy(__CFAttributedString *to, __CFAttributedString *from) 280 { 281 if (from->_attributes == NULL) 282 { 283 return; 284 } 285 _CFRunArrayDestroyAttributesOfString(to); 286 to->_attributes = (__CFRunArrayItem **)calloc(sizeof(__CFRunArrayItem *), from->_runArrayCount); 287 to->_runArrayCount = from->_runArrayCount; 288 __CFRunArrayItem **inputArray = from->_attributes; 289 __CFRunArrayItem **outputArray = to->_attributes; 290 for (CFIndex next, i = 0; i < from->_runArrayCount; i = next) 291 { 292 __CFRunArrayItem *inp = inputArray[i]; 293 if (inp) 294 { 295 outputArray[i] = _CFRunArrayItemInit(inp->_range, inp->_dictionary); 296 next = i + inp->_range.length; 297 for (CFIndex j = i; j < next; j++) 298 { 299 outputArray[j] = outputArray[i]; 300 } 301 } 302 else 303 { 304 next = i + 1; 305 } 306 } 307 } 308 309 static CFDictionaryRef _CFRunArrayObjectAtIndex(__CFAttributedString *aStr, CFIndex loc, CFRange *effectiveRange) 310 { 311 if (loc > aStr->_runArrayCount) 312 { 313 return NULL; 314 } 315 __CFRunArrayItem *ptr = aStr->_attributes[loc]; 316 if (effectiveRange) 317 { 318 *effectiveRange = ptr->_range; 319 } 320 return ptr->_dictionary; 321 } 322 323 static CFTypeID __kCFAttributedStringTypeID = _kCFRuntimeNotATypeID; 324 325 typedef CFTypeRef (*CF_STRING_CREATE_COPY)(CFAllocatorRef alloc, CFTypeRef theString); 326 327 static void __CFAttributedStringDeallocate(CFTypeRef cf) { 328 __CFAttributedString *ptr = (__CFAttributedString *)cf; 329 CFRelease(ptr->_string); 330 _CFRunArrayDestroyAttributesOfString(ptr); 331 } 332 333 static Boolean __CFAttributedStringEqual(CFTypeRef cf1, CFTypeRef cf2) { 334 __CFAttributedString *ptr1 = (__CFAttributedString *)cf1; 335 __CFAttributedString *ptr2 = (__CFAttributedString *)cf2; 336 if (!CFEqual(ptr1->_string, ptr2->_string)) 337 { 338 return false; 339 } 340 return _CFRunArrayIsEqual(ptr1, ptr2); 341 } 342 343 CFHashCode __CFAttributedStringHash(CFTypeRef cf) { 344 __CFAttributedString *ptr = (__CFAttributedString *)cf; 345 return CFHash(ptr->_string); 346 } 347 348 static CFStringRef __CFAttributedStringCopyDescription(CFTypeRef cf) 349 { 350 // Update attributes 351 __CFAttributedString *from = (__CFAttributedString *)cf; 352 __CFRunArrayItem **inputArray = from->_attributes; 353 if (inputArray == NULL) 354 { 355 return CFSTR(""); 356 } 357 CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); 358 for (CFIndex next, i = 0; i < from->_runArrayCount; i = next) 359 { 360 __CFRunArrayItem *inp = inputArray[i]; 361 if (inp) 362 { 363 next = i + inp->_range.length; 364 CFStringRef substr = CFStringCreateWithSubstring(NULL, from->_string, inp->_range); 365 CFStringAppendFormat(out, NULL, CFSTR("%@ %@ Len %d\n\n"), 366 substr, 367 inp->_dictionary, 368 (int)inp->_range.length); 369 CFRelease(substr); 370 } 371 else 372 { 373 next = i + 1; 374 } 375 } 376 return out; 377 } 378 379 static const CFRuntimeClass __CFAttributedStringClass = { 380 _kCFRuntimeScannedObject, 381 "CFAttributedString", 382 NULL, // init 383 (CF_STRING_CREATE_COPY)CFAttributedStringCreateCopy, 384 __CFAttributedStringDeallocate, 385 __CFAttributedStringEqual, 386 __CFAttributedStringHash, 387 NULL, 388 __CFAttributedStringCopyDescription 389 }; 390 391 CF_PRIVATE void __CFAttributedStringInitialize(void) { 392 __kCFAttributedStringTypeID = _CFRuntimeRegisterClass(&__CFAttributedStringClass); 393 } 394 395 CFTypeID CFAttributedStringGetTypeID(void) { 396 if (__kCFAttributedStringTypeID == _kCFRuntimeNotATypeID) { 397 __CFAttributedStringInitialize(); 398 } 399 return __kCFAttributedStringTypeID; 400 } 401 402 static void _CFAttributedStringCreateAttributes(CFAllocatorRef alloc, __CFAttributedString *attrStr, CFDictionaryRef attributes, CFIndex len) 403 { 404 CFRange range = CFRangeMake(0, len); 405 if (attributes != NULL) 406 { 407 _CFRunArrayInsert(attrStr, attributes, range, false, NULL); 408 } 409 else 410 { 411 CFDictionaryRef insertDict = CFDictionaryCreate(alloc, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 412 _CFRunArrayInsert(attrStr, insertDict, range, false, NULL); 413 CFRelease(insertDict); 414 } 415 } 416 417 CFAttributedStringRef CFAttributedStringCreate(CFAllocatorRef alloc, CFStringRef str, CFDictionaryRef attributes) 418 { 419 CFIndex size = sizeof(struct __CFAttributedString) - sizeof(CFRuntimeBase); 420 __CFAttributedString *newObj = (struct __CFAttributedString *)_CFRuntimeCreateInstance(alloc, CFAttributedStringGetTypeID(), size, NULL); 421 newObj->_string = CFStringCreateCopy(alloc, str); 422 newObj->_attributes = NULL; 423 newObj->_runArrayCount = 0; 424 newObj->_isMutable = false; 425 _CFAttributedStringCreateAttributes(alloc, newObj, attributes, CFStringGetLength(str)); 426 return (CFAttributedStringRef)newObj; 427 } 428 429 CFAttributedStringRef CFAttributedStringCreateWithSubstring(CFAllocatorRef alloc, CFAttributedStringRef aStr, CFRange range) 430 { 431 CFIndex length = CFAttributedStringGetLength(aStr); 432 if (range.length == 0 || range.location + range.length > length) 433 { 434 return NULL; 435 } 436 CFStringRef s = CFAttributedStringGetString(aStr); 437 CFStringRef substring = CFStringCreateWithSubstring(alloc, s, range); 438 CFMutableAttributedStringRef obj = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, substring, NULL); 439 CFRelease(substring); 440 441 CFIndex delta = range.location; 442 CFIndex newLength; 443 444 for (CFIndex i = 0; i < range.length; i = i + newLength) 445 { 446 CFRange effRange; 447 CFIndex selfIndex = i + delta; 448 CFDictionaryRef attrs = CFAttributedStringGetAttributes(aStr, selfIndex, &effRange); 449 newLength = effRange.length; 450 if (effRange.location < selfIndex) 451 { 452 newLength -= selfIndex - effRange.location; 453 } 454 newLength = __CFMin(newLength, range.length - i); 455 CFAttributedStringSetAttributes(obj, CFRangeMake(i, newLength), attrs, false); 456 } 457 return (CFAttributedStringRef)obj; 458 } 459 460 CFAttributedStringRef CFAttributedStringCreateCopy(CFAllocatorRef alloc, CFAttributedStringRef aStr) 461 { 462 if (((CFAttributedStringRef)aStr)->_isMutable) 463 { 464 CFAttributedStringRef retVal = (CFAttributedStringRef)CFAttributedStringCreateMutableCopy(alloc, 0, aStr); 465 ((__CFAttributedString *)retVal)->_isMutable = false; 466 return retVal; 467 } 468 else 469 { 470 return CFRetain(aStr); 471 } 472 } 473 474 CFStringRef CFAttributedStringGetString(CFAttributedStringRef aStr) 475 { 476 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFStringRef, (NSAttributedString *)aStr, string); 477 return ((__CFAttributedString *)aStr)->_string; 478 } 479 480 CFIndex CFAttributedStringGetLength(CFAttributedStringRef aStr) 481 { 482 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFIndex, (NSAttributedString *)aStr, length); 483 return CFStringGetLength(CFAttributedStringGetString(aStr)); 484 } 485 486 CFDictionaryRef CFAttributedStringGetAttributes(CFAttributedStringRef aStr, CFIndex loc, CFRange *effectiveRange) 487 { 488 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, CFDictionaryRef, (NSAttributedString *)aStr, attributesAtIndex:loc effectiveRange:effectiveRange); 489 return _CFRunArrayObjectAtIndex((__CFAttributedString *)aStr, loc, effectiveRange); 490 } 491 492 CFTypeRef CFAttributedStringGetAttribute(CFAttributedStringRef aStr, CFIndex loc, CFStringRef attrName, CFRange *effectiveRange) 493 { 494 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, id, (NSAttributedString *)aStr, attribute:attrName AtIndex:loc effectiveRange:effectiveRange); 495 CFDictionaryRef dict = CFAttributedStringGetAttributes(aStr, loc, effectiveRange); 496 return CFDictionaryGetValue(dict, attrName); 497 } 498 499 CFDictionaryRef CFAttributedStringGetAttributesAndLongestEffectiveRange(CFAttributedStringRef aStr, CFIndex loc, CFRange inRange, CFRange *longestEffectiveRange) 500 { 501 CFDictionaryRef retVal = CFAttributedStringGetAttributes(aStr, loc, longestEffectiveRange); 502 if (longestEffectiveRange == NULL) 503 { 504 return retVal; 505 } 506 CFIndex min = longestEffectiveRange->location; // inclusive end 507 CFIndex max = longestEffectiveRange->location + longestEffectiveRange->length; // exclusive end 508 CFRange tempRange; 509 CFTypeRef compareVal; 510 CFIndex minLimit = __CFMax(0, inRange.location); 511 while (min > minLimit && 512 (compareVal = CFAttributedStringGetAttributes(aStr, min - 1, &tempRange)) && 513 CFEqual(retVal, compareVal)) 514 { 515 min = tempRange.location; 516 } 517 518 CFIndex inRangeLimit = inRange.location + inRange.length; 519 CFIndex maxLimit = __CFMin(CFAttributedStringGetLength(aStr), inRangeLimit); 520 while (max < maxLimit && 521 (compareVal = CFAttributedStringGetAttributes(aStr, max, &tempRange)) && 522 CFEqual(retVal, compareVal)) 523 { 524 max = tempRange.location + tempRange.length; 525 } 526 CFIndex newLocation = __CFMax(min, inRange.location); 527 CFIndex newLength = __CFMin(max, inRangeLimit) - newLocation; 528 *longestEffectiveRange = CFRangeMake(newLocation, newLength); 529 return retVal; 530 } 531 532 CFTypeRef CFAttributedStringGetAttributeAndLongestEffectiveRange(CFAttributedStringRef aStr, CFIndex loc, CFStringRef attrName, CFRange inRange, CFRange *longestEffectiveRange) 533 { 534 CFTypeRef retVal = CFAttributedStringGetAttribute(aStr, loc, attrName, longestEffectiveRange); 535 if (longestEffectiveRange == NULL) 536 { 537 return retVal; 538 } 539 CFIndex min = longestEffectiveRange->location; // inclusive end 540 CFIndex max = longestEffectiveRange->location + longestEffectiveRange->length; // exclusive end 541 CFRange tempRange; 542 CFTypeRef compareVal; 543 CFIndex minLimit = __CFMax(0, inRange.location); 544 while (min > minLimit && 545 (compareVal = CFAttributedStringGetAttribute(aStr, min - 1, attrName, &tempRange)) && 546 CFEqual(retVal, compareVal)) 547 { 548 min = tempRange.location; 549 } 550 551 CFIndex inRangeLimit = inRange.location + inRange.length; 552 CFIndex maxLimit = __CFMin(CFAttributedStringGetLength(aStr), inRangeLimit); 553 while (max < maxLimit && 554 (compareVal = CFAttributedStringGetAttribute(aStr, max, attrName, &tempRange)) && 555 CFEqual(retVal, compareVal)) 556 { 557 max = tempRange.location + tempRange.length; 558 } 559 CFIndex newLocation = __CFMax(min, inRange.location); 560 CFIndex newLength = __CFMin(max, inRangeLimit) - newLocation; 561 *longestEffectiveRange = CFRangeMake(newLocation, newLength); 562 return retVal; 563 } 564 565 CFMutableAttributedStringRef CFAttributedStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFAttributedStringRef aStr) 566 { 567 CFMutableAttributedStringRef retVal = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, CFAttributedStringGetString(aStr), NULL); 568 _CFRunArrayCopy((__CFAttributedString *)retVal, (__CFAttributedString *)aStr); 569 ((__CFAttributedString *)retVal)->_isMutable = true; 570 return retVal; 571 } 572 573 CFMutableAttributedStringRef CFAttributedStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength) 574 { 575 // iOS also seems to ignore the maxLength parameter - it's passed to CFStringCreateMutable, but at most is used as 576 // a hint - not as an absolute limit as implied by docs. 577 CFMutableAttributedStringRef retVal = (CFMutableAttributedStringRef)CFAttributedStringCreate(alloc, CFSTR(""), NULL); 578 ((__CFAttributedString *)retVal)->_isMutable = true; 579 return retVal; 580 } 581 582 void CFAttributedStringReplaceString(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef replacement) 583 { 584 CF_OBJC_FUNCDISPATCHV (__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, replaceCharactersInRange:NSMakeRange(range.location, range.length) withString:replacement); 585 CFIndex adding = CFStringGetLength(replacement); 586 __CFAttributedString *ptr = (struct __CFAttributedString *)aStr; 587 CFIndex oldLength = CFStringGetLength(ptr->_string); 588 CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 0, ptr->_string); 589 CFStringReplace(str, range, replacement); 590 CFRelease(ptr->_string); 591 ptr->_string = str; 592 593 if (oldLength == 0) 594 { 595 _CFAttributedStringCreateAttributes(NULL, ptr, NULL, adding); 596 } 597 else if (range.location == oldLength) 598 { 599 // Extending old string at end. Attributes copied from last character 600 ptr->_attributes[oldLength - 1]->_range.length += adding; 601 ptr->_attributes = (__CFRunArrayItem **)realloc(ptr->_attributes, sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount + adding)); 602 ptr->_runArrayCount += adding; 603 for (CFIndex i = oldLength; i < ptr->_runArrayCount; i++) 604 { 605 ptr->_attributes[i] = ptr->_attributes[oldLength - 1]; 606 } 607 } 608 else 609 { 610 // Now fix up attribute ranges. Inserted string takes the attributes of the character at the start 611 CFIndex deleting = range.length; 612 CFIndex delta = adding - deleting; 613 614 __CFRunArrayItem *startRunPtr = ptr->_attributes[range.location]; 615 CFIndex next = startRunPtr->_range.location + startRunPtr->_range.length; 616 CFIndex deleteLimit = 0; 617 CFIndex startRangeDelta = range.location - startRunPtr->_range.location; 618 if (startRunPtr->_range.length - startRangeDelta >= deleting) 619 { 620 if (delta == 0) 621 { 622 // No adjustments necessary 623 return; 624 } 625 startRunPtr->_range.length += delta; 626 } 627 else 628 { 629 deleteLimit = range.location + range.length; 630 startRunPtr->_range.length = startRangeDelta + adding; 631 } 632 if (startRunPtr->_range.length == 0) 633 { 634 _CFRunArrayItemDestroy(startRunPtr); 635 } 636 for (CFIndex i = next; i < oldLength; i = next) 637 { 638 __CFRunArrayItem *runPtr = ptr->_attributes[i]; 639 next = runPtr->_range.location + runPtr->_range.length; 640 if (next <= deleteLimit) 641 { 642 _CFRunArrayItemDestroy(runPtr); 643 } 644 else if (i >= deleteLimit) 645 { 646 runPtr->_range.location += delta; 647 } 648 else 649 { 650 runPtr->_range.length -= deleteLimit - i; 651 runPtr->_range.location = range.location + adding; 652 } 653 } 654 // Get attributes array in the right place 655 if (delta > 0) 656 { 657 ptr->_attributes = (__CFRunArrayItem **)realloc(ptr->_attributes, sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount + delta)); 658 CFIndex updateStart = range.location; 659 memmove(&ptr->_attributes[updateStart + delta], &ptr->_attributes[updateStart], sizeof(__CFRunArrayItem *) * (ptr->_runArrayCount - updateStart)); 660 } 661 else if (delta < 0) 662 { 663 CFIndex updateStart = range.location + adding; 664 CFIndex count = ptr->_runArrayCount - updateStart + delta; 665 if (count > 0) 666 { 667 memmove(&ptr->_attributes[updateStart], &ptr->_attributes[updateStart - delta], sizeof(__CFRunArrayItem *) * count); 668 } 669 } 670 ptr->_runArrayCount += delta; 671 672 // Make sure attribute array is correct for length of new string 673 CFIndex endNewAdd = range.location + adding; 674 for (CFIndex i = range.location + 1; i < endNewAdd; i++) 675 { 676 ptr->_attributes[i] = startRunPtr; 677 } 678 } 679 } 680 681 CFMutableStringRef CFAttributedStringGetMutableString(CFMutableAttributedStringRef aStr) 682 { 683 // iOS only returns a real value for the NS version 684 return nil; 685 } 686 687 void CFAttributedStringSetAttributes(CFMutableAttributedStringRef aStr, CFRange range, CFDictionaryRef replacement, Boolean clearOtherAttributes) 688 { 689 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSMutableAttributedString *)aStr, addAttributes:(NSDictionary *)replacement range:NSMakeRange(range.location, range.length)); 690 _CFRunArrayInsert((__CFAttributedString *)aStr, replacement, range, clearOtherAttributes, NULL); 691 } 692 693 void CFAttributedStringSetAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName, CFTypeRef value) 694 { 695 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, setAttribute:attrName value:value range:NSMakeRange(range.location, range.length)); 696 697 CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)&attrName, (const void **)&value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 698 CFAttributedStringSetAttributes((__CFAttributedString *)aStr, range, dict, false); 699 CFRelease(dict); 700 } 701 702 void CFAttributedStringRemoveAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName) 703 { 704 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, removeAttribute:attrName range:NSMakeRange(range.location, range.length)); 705 _CFRunArrayInsert(aStr, NULL, range, false, attrName); 706 } 707 708 void CFAttributedStringReplaceAttributedString(CFMutableAttributedStringRef aStr, CFRange range, CFAttributedStringRef replacement) 709 { 710 CF_OBJC_FUNCDISPATCHV(__kCFAttributedStringTypeID, void, (NSAttributedString *)aStr, replaceCharactersInRange:NSMakeRange(range.location, range.length) withAttributeString:replacement); 711 712 // Replace string 713 CFStringRef replaceString = CFAttributedStringGetString(replacement); 714 CFAttributedStringReplaceString(aStr, range, replaceString); 715 716 // Update attributes 717 CFIndex len = CFStringGetLength(replaceString); 718 for (CFIndex next, i = 0; i < len; i = next) 719 { 720 CFRange replacementRange; 721 CFDictionaryRef attrs = CFAttributedStringGetAttributes(replacement, i, &replacementRange); 722 _CFRunArrayInsert(aStr, attrs, CFRangeMake(i + range.location, replacementRange.length), true, NULL); 723 next = i + replacementRange.length; 724 } 725 } 726 727 void CFAttributedStringBeginEditing(CFMutableAttributedStringRef aStr) 728 { 729 static int printed = 0; 730 if (printed == 0) 731 { 732 DEBUG_LOG("CFAttributedStringBeginEditing and CFAttributedStringEndEditing are currently no-ops"); 733 printed = 1; 734 } 735 } 736 737 void CFAttributedStringEndEditing(CFMutableAttributedStringRef aStr) 738 { 739 } 740 741 void _CFAttributedStringSetMutable(CFAttributedStringRef aStr, Boolean isMutable) 742 { 743 ((__CFAttributedString *)aStr)->_isMutable = isMutable; 744 }