1 /* 2 * Copyright 2001-2006 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers, mflerackers@androme.be 7 * Stefano Ceccherini, burton666@libero.it 8 */ 9 10 /** Style storage used by BTextView */ 11 12 13 #include "InlineInput.h" 14 #include "StyleBuffer.h" 15 16 #include <View.h> 17 18 #include <stdio.h> 19 20 21 // #pragma mark - _BStyleRunDescBuffer_ 22 23 24 _BStyleRunDescBuffer_::_BStyleRunDescBuffer_() 25 : 26 _BTextViewSupportBuffer_<STEStyleRunDesc>(20) 27 { 28 } 29 30 31 void 32 _BStyleRunDescBuffer_::InsertDesc(STEStyleRunDesc* inDesc, int32 index) 33 { 34 InsertItemsAt(1, index, inDesc); 35 } 36 37 38 void 39 _BStyleRunDescBuffer_::RemoveDescs(int32 index, int32 count) 40 { 41 RemoveItemsAt(count, index); 42 } 43 44 45 int32 46 _BStyleRunDescBuffer_::OffsetToRun(int32 offset) const 47 { 48 if (fItemCount <= 1) 49 return 0; 50 51 int32 minIndex = 0; 52 int32 maxIndex = fItemCount; 53 int32 index = 0; 54 55 while (minIndex < maxIndex) { 56 index = (minIndex + maxIndex) >> 1; 57 if (offset >= fBuffer[index].offset) { 58 if (index >= fItemCount - 1 59 || offset < fBuffer[index + 1].offset) { 60 break; 61 } else 62 minIndex = index + 1; 63 } else 64 maxIndex = index; 65 } 66 67 return index; 68 } 69 70 71 void 72 _BStyleRunDescBuffer_::BumpOffset(int32 delta, int32 index) 73 { 74 for (int32 i = index; i < fItemCount; i++) 75 fBuffer[i].offset += delta; 76 } 77 78 79 // #pragma mark - _BStyleRecordBuffer_ 80 81 82 _BStyleRecordBuffer_::_BStyleRecordBuffer_() 83 : 84 _BTextViewSupportBuffer_<STEStyleRecord>() 85 { 86 } 87 88 89 int32 90 _BStyleRecordBuffer_::InsertRecord(const BFont* inFont, 91 const rgb_color* inColor) 92 { 93 int32 index = 0; 94 95 // look for style in buffer 96 if (MatchRecord(inFont, inColor, &index)) 97 return index; 98 99 // style not found, add it 100 font_height fh; 101 inFont->GetHeight(&fh); 102 103 // check if there's any unused space 104 for (index = 0; index < fItemCount; index++) { 105 if (fBuffer[index].refs < 1) { 106 fBuffer[index].refs = 0; 107 fBuffer[index].ascent = fh.ascent; 108 fBuffer[index].descent = fh.descent + fh.leading; 109 fBuffer[index].style.font = *inFont; 110 fBuffer[index].style.color = *inColor; 111 return index; 112 } 113 } 114 115 // no unused space, expand the buffer 116 const STEStyle style = { *inFont, *inColor }; 117 const STEStyleRecord newRecord = { 118 0, 119 fh.ascent, 120 fh.descent + fh.leading, 121 style 122 }; 123 InsertItemsAt(1, fItemCount, &newRecord); 124 125 return index; 126 } 127 128 129 void 130 _BStyleRecordBuffer_::CommitRecord(int32 index) 131 { 132 fBuffer[index].refs++; 133 } 134 135 136 void 137 _BStyleRecordBuffer_::RemoveRecord(int32 index) 138 { 139 fBuffer[index].refs--; 140 } 141 142 143 bool 144 _BStyleRecordBuffer_::MatchRecord(const BFont* inFont, const rgb_color* inColor, 145 int32* outIndex) 146 { 147 for (int32 i = 0; i < fItemCount; i++) { 148 if (*inFont == fBuffer[i].style.font 149 && *inColor == fBuffer[i].style.color) { 150 *outIndex = i; 151 return true; 152 } 153 } 154 155 return false; 156 } 157 158 159 // #pragma mark - SetStyleFromMode 160 161 162 static void 163 SetStyleFromMode(uint32 mode, const BFont* fromFont, BFont* toFont, 164 const rgb_color* fromColor, rgb_color* toColor) 165 { 166 if (fromFont != NULL && toFont != NULL) { 167 if ((mode & B_FONT_FAMILY_AND_STYLE) != 0) 168 toFont->SetFamilyAndStyle(fromFont->FamilyAndStyle()); 169 170 if ((mode & B_FONT_FACE) != 0) 171 toFont->SetFace(fromFont->Face()); 172 173 if ((mode & B_FONT_SIZE) != 0) 174 toFont->SetSize(fromFont->Size()); 175 176 if ((mode & B_FONT_SHEAR) != 0) 177 toFont->SetShear(fromFont->Shear()); 178 179 if ((mode & B_FONT_FALSE_BOLD_WIDTH) != 0) 180 toFont->SetFalseBoldWidth(fromFont->FalseBoldWidth()); 181 } 182 183 if (fromColor != NULL && toColor != NULL 184 && (mode == 0 || mode == B_FONT_ALL)) { 185 *toColor = *fromColor; 186 } 187 } 188 189 190 // #pragma mark - BTextView::StyleBuffer 191 192 193 BTextView::StyleBuffer::StyleBuffer(const BFont* inFont, 194 const rgb_color* inColor) 195 : 196 fValidNullStyle(true) 197 { 198 fNullStyle.font = *inFont; 199 fNullStyle.color = *inColor; 200 } 201 202 203 void 204 BTextView::StyleBuffer::InvalidateNullStyle() 205 { 206 fValidNullStyle = false; 207 } 208 209 210 bool 211 BTextView::StyleBuffer::IsValidNullStyle() const 212 { 213 return fValidNullStyle; 214 } 215 216 217 void 218 BTextView::StyleBuffer::SyncNullStyle(int32 offset) 219 { 220 if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) 221 return; 222 223 int32 index = OffsetToRun(offset); 224 fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; 225 226 fValidNullStyle = true; 227 } 228 229 230 void 231 BTextView::StyleBuffer::SetNullStyle(uint32 inMode, const BFont* inFont, 232 const rgb_color* inColor, int32 offset) 233 { 234 if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) { 235 SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, 236 &fNullStyle.color); 237 } else { 238 int32 index = OffsetToRun(offset - 1); 239 fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; 240 SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, 241 &fNullStyle.color); 242 } 243 244 fValidNullStyle = true; 245 } 246 247 248 void 249 BTextView::StyleBuffer::GetNullStyle(const BFont** font, 250 const rgb_color** color) const 251 { 252 if (font != NULL) 253 *font = &fNullStyle.font; 254 255 if (color != NULL) 256 *color = &fNullStyle.color; 257 } 258 259 260 STEStyleRange* 261 BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles) const 262 { 263 STEStyleRange* range = (STEStyleRange*)malloc(sizeof(int32) 264 + sizeof(STEStyleRun) * numStyles); 265 if (range != NULL) 266 range->count = numStyles; 267 268 return range; 269 } 270 271 272 void 273 BTextView::StyleBuffer::SetStyleRange(int32 fromOffset, int32 toOffset, 274 int32 textLen, uint32 inMode, const BFont* inFont, 275 const rgb_color* inColor) 276 { 277 if (inFont == NULL) 278 inFont = &fNullStyle.font; 279 280 if (inColor == NULL) 281 inColor = &fNullStyle.color; 282 283 if (fromOffset == toOffset) { 284 SetNullStyle(inMode, inFont, inColor, fromOffset); 285 return; 286 } 287 288 if (fStyleRunDesc.ItemCount() < 1) { 289 STEStyleRunDesc newDesc; 290 newDesc.offset = fromOffset; 291 newDesc.index = fStyleRecord.InsertRecord(inFont, inColor); 292 fStyleRunDesc.InsertDesc(&newDesc, 0); 293 fStyleRecord.CommitRecord(newDesc.index); 294 return; 295 } 296 297 int32 offset = fromOffset; 298 int32 runIndex = OffsetToRun(offset); 299 int32 styleIndex = 0; 300 do { 301 const STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex]; 302 int32 runEnd = textLen; 303 if (runIndex < fStyleRunDesc.ItemCount() - 1) 304 runEnd = fStyleRunDesc[runIndex + 1]->offset; 305 306 STEStyle style = fStyleRecord[runDesc.index]->style; 307 SetStyleFromMode(inMode, inFont, &style.font, inColor, &style.color); 308 309 styleIndex = fStyleRecord.InsertRecord(&style.font, &style.color); 310 311 if (runDesc.offset == offset && runIndex > 0 312 && fStyleRunDesc[runIndex - 1]->index == styleIndex) { 313 RemoveStyles(runIndex); 314 runIndex--; 315 } 316 317 if (styleIndex != runDesc.index) { 318 if (offset > runDesc.offset) { 319 STEStyleRunDesc newDesc; 320 newDesc.offset = offset; 321 newDesc.index = styleIndex; 322 fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); 323 fStyleRecord.CommitRecord(newDesc.index); 324 runIndex++; 325 } else { 326 fStyleRunDesc[runIndex]->index = styleIndex; 327 fStyleRecord.CommitRecord(styleIndex); 328 } 329 330 if (toOffset < runEnd) { 331 STEStyleRunDesc newDesc; 332 newDesc.offset = toOffset; 333 newDesc.index = runDesc.index; 334 fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); 335 fStyleRecord.CommitRecord(newDesc.index); 336 } 337 } 338 339 runIndex++; 340 offset = runEnd; 341 } while (offset < toOffset); 342 343 if (offset == toOffset && runIndex < fStyleRunDesc.ItemCount() 344 && fStyleRunDesc[runIndex]->index == styleIndex) { 345 RemoveStyles(runIndex); 346 } 347 } 348 349 350 void 351 BTextView::StyleBuffer::GetStyle(int32 inOffset, BFont* outFont, 352 rgb_color* outColor) const 353 { 354 if (fStyleRunDesc.ItemCount() < 1) { 355 if (outFont != NULL) 356 *outFont = fNullStyle.font; 357 358 if (outColor != NULL) 359 *outColor = fNullStyle.color; 360 361 return; 362 } 363 364 int32 runIndex = OffsetToRun(inOffset); 365 int32 styleIndex = fStyleRunDesc[runIndex]->index; 366 367 if (outFont != NULL) 368 *outFont = fStyleRecord[styleIndex]->style.font; 369 370 if (outColor != NULL) 371 *outColor = fStyleRecord[styleIndex]->style.color; 372 } 373 374 375 STEStyleRange* 376 BTextView::StyleBuffer::GetStyleRange(int32 startOffset, int32 endOffset) const 377 { 378 int32 startIndex = OffsetToRun(startOffset); 379 int32 endIndex = OffsetToRun(endOffset); 380 381 int32 numStyles = endIndex - startIndex + 1; 382 if (numStyles < 1) 383 numStyles = 1; 384 385 STEStyleRange* result = AllocateStyleRange(numStyles); 386 if (result == NULL) 387 return NULL; 388 389 STEStyleRun* run = &result->runs[0]; 390 for (int32 index = 0; index < numStyles; index++) { 391 *run = (*this)[startIndex + index]; 392 run->offset -= startOffset; 393 if (run->offset < 0) 394 run->offset = 0; 395 396 run++; 397 } 398 399 return result; 400 } 401 402 403 void 404 BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset, int32 toOffset) 405 { 406 int32 fromIndex = fStyleRunDesc.OffsetToRun(fromOffset); 407 int32 toIndex = fStyleRunDesc.OffsetToRun(toOffset) - 1; 408 409 int32 count = toIndex - fromIndex; 410 if (count > 0) { 411 RemoveStyles(fromIndex + 1, count); 412 toIndex = fromIndex; 413 } 414 415 fStyleRunDesc.BumpOffset(fromOffset - toOffset, fromIndex + 1); 416 417 if (toIndex == fromIndex && toIndex < fStyleRunDesc.ItemCount() - 1) { 418 STEStyleRunDesc* runDesc = fStyleRunDesc[toIndex + 1]; 419 runDesc->offset = fromOffset; 420 } 421 422 if (fromIndex < fStyleRunDesc.ItemCount() - 1) { 423 STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; 424 if (runDesc->offset == (runDesc + 1)->offset) { 425 RemoveStyles(fromIndex); 426 fromIndex--; 427 } 428 } 429 430 if (fromIndex >= 0 && fromIndex < fStyleRunDesc.ItemCount() - 1) { 431 STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; 432 if (runDesc->index == (runDesc + 1)->index) 433 RemoveStyles(fromIndex + 1); 434 } 435 } 436 437 438 void 439 BTextView::StyleBuffer::RemoveStyles(int32 index, int32 count) 440 { 441 for (int32 i = index; i < index + count; i++) 442 fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index); 443 444 fStyleRunDesc.RemoveDescs(index, count); 445 } 446 447 448 int32 449 BTextView::StyleBuffer::Iterate(int32 fromOffset, int32 length, 450 InlineInput* input, 451 const BFont** outFont, const rgb_color** outColor, 452 float* outAscent, float* outDescent, uint32*) const 453 { 454 // TODO: Handle the InlineInput style here in some way 455 int32 numRuns = fStyleRunDesc.ItemCount(); 456 if (length < 1 || numRuns < 1) 457 return 0; 458 459 int32 result = length; 460 int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset); 461 STEStyleRunDesc* run = fStyleRunDesc[runIndex]; 462 463 if (outFont != NULL) 464 *outFont = &fStyleRecord[run->index]->style.font; 465 466 if (outColor != NULL) 467 *outColor = &fStyleRecord[run->index]->style.color; 468 469 if (outAscent != NULL) 470 *outAscent = fStyleRecord[run->index]->ascent; 471 472 if (outDescent != NULL) 473 *outDescent = fStyleRecord[run->index]->descent; 474 475 if (runIndex < numRuns - 1) { 476 int32 nextOffset = (run + 1)->offset - fromOffset; 477 result = min_c(result, nextOffset); 478 } 479 480 return result; 481 } 482 483 484 int32 485 BTextView::StyleBuffer::OffsetToRun(int32 offset) const 486 { 487 return fStyleRunDesc.OffsetToRun(offset); 488 } 489 490 491 void 492 BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index) 493 { 494 fStyleRunDesc.BumpOffset(delta, index); 495 } 496 497 498 STEStyleRun 499 BTextView::StyleBuffer::operator[](int32 index) const 500 { 501 STEStyleRun run; 502 503 if (fStyleRunDesc.ItemCount() < 1) { 504 run.offset = 0; 505 run.style = fNullStyle; 506 } else { 507 STEStyleRunDesc* runDesc = fStyleRunDesc[index]; 508 run.offset = runDesc->offset; 509 run.style = fStyleRecord[runDesc->index]->style; 510 } 511 512 return run; 513 } 514 515 516 // TODO: Horrible name, but can't think of a better one 517 // ? CompareStyles ? 518 // ? FilterStyles ? 519 static void 520 FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode, 521 bool &sameColor) 522 { 523 if ((mode & B_FONT_FAMILY_AND_STYLE) != 0) { 524 if (firstStyle.font != otherStyle.font) 525 mode &= ~B_FONT_FAMILY_AND_STYLE; 526 } 527 if ((mode & B_FONT_SIZE) != 0) { 528 if (firstStyle.font.Size() != otherStyle.font.Size()) 529 mode &= ~B_FONT_SIZE; 530 } 531 if ((mode & B_FONT_SHEAR) != 0) { 532 if (firstStyle.font.Shear() != otherStyle.font.Shear()) 533 mode &= ~B_FONT_SHEAR; 534 } 535 if ((mode & B_FONT_FALSE_BOLD_WIDTH) != 0) { 536 if (firstStyle.font.FalseBoldWidth() 537 != otherStyle.font.FalseBoldWidth()) { 538 mode &= ~B_FONT_FALSE_BOLD_WIDTH; 539 } 540 } 541 if (firstStyle.color != otherStyle.color) 542 sameColor = false; 543 544 // TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc. 545 // if needed 546 } 547 548 549 void 550 BTextView::StyleBuffer::ContinuousGetStyle(BFont *outFont, uint32* ioMode, 551 rgb_color* outColor, bool* sameColor, int32 fromOffset, 552 int32 toOffset) const 553 { 554 uint32 mode = B_FONT_ALL; 555 556 if (fStyleRunDesc.ItemCount() < 1) { 557 if (ioMode) 558 *ioMode = mode; 559 560 if (outFont != NULL) 561 *outFont = fNullStyle.font; 562 563 if (outColor != NULL) 564 *outColor = fNullStyle.color; 565 566 if (sameColor != NULL) 567 *sameColor = true; 568 569 return; 570 } 571 572 int32 fromIndex = OffsetToRun(fromOffset); 573 int32 toIndex = OffsetToRun(toOffset - 1); 574 575 if (fromIndex == toIndex) { 576 int32 styleIndex = fStyleRunDesc[fromIndex]->index; 577 const STEStyle* style = &fStyleRecord[styleIndex]->style; 578 579 if (ioMode != NULL) 580 *ioMode = mode; 581 582 if (outFont != NULL) 583 *outFont = style->font; 584 585 if (outColor != NULL) 586 *outColor = style->color; 587 588 if (sameColor != NULL) 589 *sameColor = true; 590 } else { 591 bool oneColor = true; 592 int32 styleIndex = fStyleRunDesc[toIndex]->index; 593 STEStyle theStyle = fStyleRecord[styleIndex]->style; 594 595 for (int32 i = fromIndex; i < toIndex; i++) { 596 styleIndex = fStyleRunDesc[i]->index; 597 FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode, 598 oneColor); 599 } 600 601 if (ioMode != NULL) 602 *ioMode = mode; 603 604 if (outFont != NULL) 605 *outFont = theStyle.font; 606 607 if (outColor != NULL) 608 *outColor = theStyle.color; 609 610 if (sameColor != NULL) 611 *sameColor = oneColor; 612 } 613 } 614