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 175 if (fromColor != NULL && toColor != NULL 176 && (mode == 0 || mode == B_FONT_ALL)) 177 *toColor = *fromColor; 178 } 179 180 181 BTextView::StyleBuffer::StyleBuffer(const BFont *inFont, 182 const rgb_color *inColor) 183 : 184 fValidNullStyle(true) 185 { 186 fNullStyle.font = *inFont; 187 fNullStyle.color = *inColor; 188 } 189 190 191 void 192 BTextView::StyleBuffer::InvalidateNullStyle() 193 { 194 fValidNullStyle = false; 195 } 196 197 198 bool 199 BTextView::StyleBuffer::IsValidNullStyle() const 200 { 201 return fValidNullStyle; 202 } 203 204 205 void 206 BTextView::StyleBuffer::SyncNullStyle(int32 offset) 207 { 208 if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) 209 return; 210 211 int32 index = OffsetToRun(offset); 212 fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; 213 214 fValidNullStyle = true; 215 } 216 217 218 void 219 BTextView::StyleBuffer::SetNullStyle(uint32 inMode, const BFont *inFont, 220 const rgb_color *inColor, int32 offset) 221 { 222 if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) { 223 SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, 224 &fNullStyle.color); 225 } else { 226 int32 index = OffsetToRun(offset - 1); 227 fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; 228 SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, 229 &fNullStyle.color); 230 } 231 232 fValidNullStyle = true; 233 } 234 235 236 void 237 BTextView::StyleBuffer::GetNullStyle(const BFont **font, 238 const rgb_color **color) const 239 { 240 if (font != NULL) 241 *font = &fNullStyle.font; 242 if (color != NULL) 243 *color = &fNullStyle.color; 244 } 245 246 247 STEStyleRange * 248 BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles) const 249 { 250 STEStyleRange* range = (STEStyleRange *)malloc(sizeof(int32) 251 + sizeof(STEStyleRun) * numStyles); 252 if (range != NULL) 253 range->count = numStyles; 254 return range; 255 } 256 257 258 void 259 BTextView::StyleBuffer::SetStyleRange(int32 fromOffset, int32 toOffset, 260 int32 textLen, uint32 inMode, const BFont *inFont, 261 const rgb_color *inColor) 262 { 263 if (inFont == NULL) 264 inFont = &fNullStyle.font; 265 266 if (inColor == NULL) 267 inColor = &fNullStyle.color; 268 269 if (fromOffset == toOffset) { 270 SetNullStyle(inMode, inFont, inColor, fromOffset); 271 return; 272 } 273 274 if (fStyleRunDesc.ItemCount() < 1) { 275 STEStyleRunDesc newDesc; 276 newDesc.offset = fromOffset; 277 newDesc.index = fStyleRecord.InsertRecord(inFont, inColor); 278 fStyleRunDesc.InsertDesc(&newDesc, 0); 279 fStyleRecord.CommitRecord(newDesc.index); 280 return; 281 } 282 283 int32 offset = fromOffset; 284 int32 runIndex = OffsetToRun(offset); 285 int32 styleIndex = 0; 286 do { 287 const STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex]; 288 int32 runEnd = textLen; 289 if (runIndex < fStyleRunDesc.ItemCount() - 1) 290 runEnd = fStyleRunDesc[runIndex + 1]->offset; 291 292 STEStyle style = fStyleRecord[runDesc.index]->style; 293 SetStyleFromMode(inMode, inFont, &style.font, inColor, &style.color); 294 295 styleIndex = fStyleRecord.InsertRecord(&style.font, &style.color); 296 297 if (runDesc.offset == offset && runIndex > 0 298 && fStyleRunDesc[runIndex - 1]->index == styleIndex) { 299 RemoveStyles(runIndex); 300 runIndex--; 301 } 302 303 if (styleIndex != runDesc.index) { 304 if (offset > runDesc.offset) { 305 STEStyleRunDesc newDesc; 306 newDesc.offset = offset; 307 newDesc.index = styleIndex; 308 fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); 309 fStyleRecord.CommitRecord(newDesc.index); 310 runIndex++; 311 } else { 312 fStyleRunDesc[runIndex]->index = styleIndex; 313 fStyleRecord.CommitRecord(styleIndex); 314 } 315 316 if (toOffset < runEnd) { 317 STEStyleRunDesc newDesc; 318 newDesc.offset = toOffset; 319 newDesc.index = runDesc.index; 320 fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); 321 fStyleRecord.CommitRecord(newDesc.index); 322 } 323 } 324 325 runIndex++; 326 offset = runEnd; 327 } while (offset < toOffset); 328 329 if (offset == toOffset && runIndex < fStyleRunDesc.ItemCount() 330 && fStyleRunDesc[runIndex]->index == styleIndex) 331 RemoveStyles(runIndex); 332 } 333 334 335 void 336 BTextView::StyleBuffer::GetStyle(int32 inOffset, BFont *outFont, 337 rgb_color *outColor) const 338 { 339 if (fStyleRunDesc.ItemCount() < 1) { 340 if (outFont) 341 *outFont = fNullStyle.font; 342 if (outColor) 343 *outColor = fNullStyle.color; 344 return; 345 } 346 347 int32 runIndex = OffsetToRun(inOffset); 348 int32 styleIndex = fStyleRunDesc[runIndex]->index; 349 350 if (outFont) 351 *outFont = fStyleRecord[styleIndex]->style.font; 352 if (outColor) 353 *outColor = fStyleRecord[styleIndex]->style.color; 354 } 355 356 357 STEStyleRange* 358 BTextView::StyleBuffer::GetStyleRange(int32 startOffset, int32 endOffset) const 359 { 360 int32 startIndex = OffsetToRun(startOffset); 361 int32 endIndex = OffsetToRun(endOffset); 362 363 int32 numStyles = endIndex - startIndex + 1; 364 if (numStyles < 1) 365 numStyles = 1; 366 367 STEStyleRange* result = AllocateStyleRange(numStyles); 368 if (!result) 369 return NULL; 370 371 STEStyleRun* run = &result->runs[0]; 372 for (int32 index = 0; index < numStyles; index++) { 373 *run = (*this)[startIndex + index]; 374 run->offset -= startOffset; 375 if (run->offset < 0) 376 run->offset = 0; 377 run++; 378 } 379 380 return result; 381 } 382 383 384 void 385 BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset, int32 toOffset) 386 { 387 int32 fromIndex = fStyleRunDesc.OffsetToRun(fromOffset); 388 int32 toIndex = fStyleRunDesc.OffsetToRun(toOffset) - 1; 389 390 int32 count = toIndex - fromIndex; 391 if (count > 0) { 392 RemoveStyles(fromIndex + 1, count); 393 toIndex = fromIndex; 394 } 395 396 fStyleRunDesc.BumpOffset(fromOffset - toOffset, fromIndex + 1); 397 398 if (toIndex == fromIndex && toIndex < fStyleRunDesc.ItemCount() - 1) { 399 STEStyleRunDesc* runDesc = fStyleRunDesc[toIndex + 1]; 400 runDesc->offset = fromOffset; 401 } 402 403 if (fromIndex < fStyleRunDesc.ItemCount() - 1) { 404 STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; 405 if (runDesc->offset == (runDesc + 1)->offset) { 406 RemoveStyles(fromIndex); 407 fromIndex--; 408 } 409 } 410 411 if (fromIndex >= 0 && fromIndex < fStyleRunDesc.ItemCount() - 1) { 412 STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; 413 if (runDesc->index == (runDesc + 1)->index) 414 RemoveStyles(fromIndex + 1); 415 } 416 } 417 418 419 void 420 BTextView::StyleBuffer::RemoveStyles(int32 index, int32 count) 421 { 422 for (int32 i = index; i < (index + count); i++) 423 fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index); 424 425 fStyleRunDesc.RemoveDescs(index, count); 426 } 427 428 429 int32 430 BTextView::StyleBuffer::Iterate(int32 fromOffset, int32 length, 431 InlineInput *input, 432 const BFont **outFont, const rgb_color **outColor, 433 float *outAscent, float *outDescent, uint32 *) const 434 { 435 // TODO: Handle the InlineInput style here in some way 436 int32 numRuns = fStyleRunDesc.ItemCount(); 437 if (length < 1 || numRuns < 1) 438 return 0; 439 440 int32 result = length; 441 int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset); 442 STEStyleRunDesc* run = fStyleRunDesc[runIndex]; 443 444 if (outFont != NULL) 445 *outFont = &fStyleRecord[run->index]->style.font; 446 if (outColor != NULL) 447 *outColor = &fStyleRecord[run->index]->style.color; 448 if (outAscent != NULL) 449 *outAscent = fStyleRecord[run->index]->ascent; 450 if (outDescent != NULL) 451 *outDescent = fStyleRecord[run->index]->descent; 452 453 if (runIndex < numRuns - 1) { 454 int32 nextOffset = (run + 1)->offset - fromOffset; 455 result = min_c(result, nextOffset); 456 } 457 458 return result; 459 } 460 461 462 int32 463 BTextView::StyleBuffer::OffsetToRun(int32 offset) const 464 { 465 return fStyleRunDesc.OffsetToRun(offset); 466 } 467 468 469 void 470 BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index) 471 { 472 fStyleRunDesc.BumpOffset(delta, index); 473 } 474 475 476 STEStyleRun 477 BTextView::StyleBuffer::operator[](int32 index) const 478 { 479 STEStyleRun run; 480 481 if (fStyleRunDesc.ItemCount() < 1) { 482 run.offset = 0; 483 run.style = fNullStyle; 484 } else { 485 STEStyleRunDesc* runDesc = fStyleRunDesc[index]; 486 run.offset = runDesc->offset; 487 run.style = fStyleRecord[runDesc->index]->style; 488 } 489 490 return run; 491 } 492 493 494 // TODO: Horrible name, but can't think of a better one 495 // ? CompareStyles ? 496 // ? FilterStyles ? 497 static void 498 FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode, 499 bool &sameColor) 500 { 501 if (mode & B_FONT_FAMILY_AND_STYLE) { 502 if (firstStyle.font != otherStyle.font) 503 mode &= ~B_FONT_FAMILY_AND_STYLE; 504 } 505 if (mode & B_FONT_SIZE) { 506 if (firstStyle.font.Size() != otherStyle.font.Size()) 507 mode &= ~B_FONT_SIZE; 508 } 509 if (mode & B_FONT_SHEAR) { 510 if (firstStyle.font.Shear() != otherStyle.font.Shear()) 511 mode &= ~B_FONT_SHEAR; 512 } 513 if (firstStyle.color != otherStyle.color) 514 sameColor = false; 515 516 // TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc. 517 // if needed 518 } 519 520 521 void 522 BTextView::StyleBuffer::ContinuousGetStyle(BFont *outFont, uint32 *ioMode, 523 rgb_color *outColor, bool *sameColor, int32 fromOffset, 524 int32 toOffset) const 525 { 526 uint32 mode = B_FONT_ALL; 527 528 if (fStyleRunDesc.ItemCount() < 1) { 529 if (ioMode) 530 *ioMode = mode; 531 if (outFont) 532 *outFont = fNullStyle.font; 533 if (outColor) 534 *outColor = fNullStyle.color; 535 if (sameColor) 536 *sameColor = true; 537 return; 538 } 539 540 int32 fromIndex = OffsetToRun(fromOffset); 541 int32 toIndex = OffsetToRun(toOffset - 1); 542 543 if (fromIndex == toIndex) { 544 int32 styleIndex = fStyleRunDesc[fromIndex]->index; 545 const STEStyle* style = &fStyleRecord[styleIndex]->style; 546 547 if (ioMode) 548 *ioMode = mode; 549 if (outFont) 550 *outFont = style->font; 551 if (outColor) 552 *outColor = style->color; 553 if (sameColor) 554 *sameColor = true; 555 } else { 556 bool oneColor = true; 557 int32 styleIndex = fStyleRunDesc[toIndex]->index; 558 STEStyle theStyle = fStyleRecord[styleIndex]->style; 559 560 for (int32 i = fromIndex; i < toIndex; i++) { 561 styleIndex = fStyleRunDesc[i]->index; 562 FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode, 563 oneColor); 564 } 565 566 if (ioMode) 567 *ioMode = mode; 568 if (outFont) 569 *outFont = theStyle.font; 570 if (outColor) 571 *outColor = theStyle.color; 572 if (sameColor) 573 *sameColor = oneColor; 574 } 575 } 576