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