/* * Copyright 2001-2006 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers, mflerackers@androme.be * Stefano Ceccherini, burton666@libero.it */ /** Style storage used by BTextView */ #include "InlineInput.h" #include "StyleBuffer.h" #include #include // #pragma mark - _BStyleRunDescBuffer_ _BStyleRunDescBuffer_::_BStyleRunDescBuffer_() : _BTextViewSupportBuffer_(20) { } void _BStyleRunDescBuffer_::InsertDesc(STEStyleRunDesc* inDesc, int32 index) { InsertItemsAt(1, index, inDesc); } void _BStyleRunDescBuffer_::RemoveDescs(int32 index, int32 count) { RemoveItemsAt(count, index); } int32 _BStyleRunDescBuffer_::OffsetToRun(int32 offset) const { if (fItemCount <= 1) return 0; int32 minIndex = 0; int32 maxIndex = fItemCount; int32 index = 0; while (minIndex < maxIndex) { index = (minIndex + maxIndex) >> 1; if (offset >= fBuffer[index].offset) { if (index >= fItemCount - 1 || offset < fBuffer[index + 1].offset) { break; } else minIndex = index + 1; } else maxIndex = index; } return index; } void _BStyleRunDescBuffer_::BumpOffset(int32 delta, int32 index) { for (int32 i = index; i < fItemCount; i++) fBuffer[i].offset += delta; } // #pragma mark - _BStyleRecordBuffer_ _BStyleRecordBuffer_::_BStyleRecordBuffer_() : _BTextViewSupportBuffer_() { } int32 _BStyleRecordBuffer_::InsertRecord(const BFont* inFont, const rgb_color* inColor) { int32 index = 0; // look for style in buffer if (MatchRecord(inFont, inColor, &index)) return index; // style not found, add it font_height fh; inFont->GetHeight(&fh); // check if there's any unused space for (index = 0; index < fItemCount; index++) { if (fBuffer[index].refs < 1) { fBuffer[index].refs = 0; fBuffer[index].ascent = fh.ascent; fBuffer[index].descent = fh.descent + fh.leading; fBuffer[index].style.font = *inFont; fBuffer[index].style.color = *inColor; return index; } } // no unused space, expand the buffer const STEStyle style = { *inFont, *inColor }; const STEStyleRecord newRecord = { 0, fh.ascent, fh.descent + fh.leading, style }; InsertItemsAt(1, fItemCount, &newRecord); return index; } void _BStyleRecordBuffer_::CommitRecord(int32 index) { fBuffer[index].refs++; } void _BStyleRecordBuffer_::RemoveRecord(int32 index) { fBuffer[index].refs--; } bool _BStyleRecordBuffer_::MatchRecord(const BFont* inFont, const rgb_color* inColor, int32* outIndex) { for (int32 i = 0; i < fItemCount; i++) { if (*inFont == fBuffer[i].style.font && *inColor == fBuffer[i].style.color) { *outIndex = i; return true; } } return false; } // #pragma mark - SetStyleFromMode static void SetStyleFromMode(uint32 mode, const BFont* fromFont, BFont* toFont, const rgb_color* fromColor, rgb_color* toColor) { if (fromFont != NULL && toFont != NULL) { if ((mode & B_FONT_FAMILY_AND_STYLE) != 0) toFont->SetFamilyAndStyle(fromFont->FamilyAndStyle()); if ((mode & B_FONT_FACE) != 0) toFont->SetFace(fromFont->Face()); if ((mode & B_FONT_SIZE) != 0) toFont->SetSize(fromFont->Size()); if ((mode & B_FONT_SHEAR) != 0) toFont->SetShear(fromFont->Shear()); if ((mode & B_FONT_FALSE_BOLD_WIDTH) != 0) toFont->SetFalseBoldWidth(fromFont->FalseBoldWidth()); } if (fromColor != NULL && toColor != NULL && (mode == 0 || mode == B_FONT_ALL)) { *toColor = *fromColor; } } // #pragma mark - BTextView::StyleBuffer BTextView::StyleBuffer::StyleBuffer(const BFont* inFont, const rgb_color* inColor) : fValidNullStyle(true) { fNullStyle.font = *inFont; fNullStyle.color = *inColor; } void BTextView::StyleBuffer::InvalidateNullStyle() { fValidNullStyle = false; } bool BTextView::StyleBuffer::IsValidNullStyle() const { return fValidNullStyle; } void BTextView::StyleBuffer::SyncNullStyle(int32 offset) { if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) return; int32 index = OffsetToRun(offset); fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; fValidNullStyle = true; } void BTextView::StyleBuffer::SetNullStyle(uint32 inMode, const BFont* inFont, const rgb_color* inColor, int32 offset) { if (fValidNullStyle || fStyleRunDesc.ItemCount() < 1) { SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, &fNullStyle.color); } else { int32 index = OffsetToRun(offset - 1); fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; SetStyleFromMode(inMode, inFont, &fNullStyle.font, inColor, &fNullStyle.color); } fValidNullStyle = true; } void BTextView::StyleBuffer::GetNullStyle(const BFont** font, const rgb_color** color) const { if (font != NULL) *font = &fNullStyle.font; if (color != NULL) *color = &fNullStyle.color; } STEStyleRange* BTextView::StyleBuffer::AllocateStyleRange(const int32 numStyles) const { STEStyleRange* range = (STEStyleRange*)malloc(sizeof(int32) + sizeof(STEStyleRun) * numStyles); if (range != NULL) range->count = numStyles; return range; } void BTextView::StyleBuffer::SetStyleRange(int32 fromOffset, int32 toOffset, int32 textLen, uint32 inMode, const BFont* inFont, const rgb_color* inColor) { if (inFont == NULL) inFont = &fNullStyle.font; if (inColor == NULL) inColor = &fNullStyle.color; if (fromOffset == toOffset) { SetNullStyle(inMode, inFont, inColor, fromOffset); return; } if (fStyleRunDesc.ItemCount() < 1) { STEStyleRunDesc newDesc; newDesc.offset = fromOffset; newDesc.index = fStyleRecord.InsertRecord(inFont, inColor); fStyleRunDesc.InsertDesc(&newDesc, 0); fStyleRecord.CommitRecord(newDesc.index); return; } int32 offset = fromOffset; int32 runIndex = OffsetToRun(offset); int32 styleIndex = 0; do { const STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex]; int32 runEnd = textLen; if (runIndex < fStyleRunDesc.ItemCount() - 1) runEnd = fStyleRunDesc[runIndex + 1]->offset; STEStyle style = fStyleRecord[runDesc.index]->style; SetStyleFromMode(inMode, inFont, &style.font, inColor, &style.color); styleIndex = fStyleRecord.InsertRecord(&style.font, &style.color); if (runDesc.offset == offset && runIndex > 0 && fStyleRunDesc[runIndex - 1]->index == styleIndex) { RemoveStyles(runIndex); runIndex--; } if (styleIndex != runDesc.index) { if (offset > runDesc.offset) { STEStyleRunDesc newDesc; newDesc.offset = offset; newDesc.index = styleIndex; fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); fStyleRecord.CommitRecord(newDesc.index); runIndex++; } else { fStyleRunDesc[runIndex]->index = styleIndex; fStyleRecord.CommitRecord(styleIndex); } if (toOffset < runEnd) { STEStyleRunDesc newDesc; newDesc.offset = toOffset; newDesc.index = runDesc.index; fStyleRunDesc.InsertDesc(&newDesc, runIndex + 1); fStyleRecord.CommitRecord(newDesc.index); } } runIndex++; offset = runEnd; } while (offset < toOffset); if (offset == toOffset && runIndex < fStyleRunDesc.ItemCount() && fStyleRunDesc[runIndex]->index == styleIndex) { RemoveStyles(runIndex); } } void BTextView::StyleBuffer::GetStyle(int32 inOffset, BFont* outFont, rgb_color* outColor) const { if (fStyleRunDesc.ItemCount() < 1) { if (outFont != NULL) *outFont = fNullStyle.font; if (outColor != NULL) *outColor = fNullStyle.color; return; } int32 runIndex = OffsetToRun(inOffset); int32 styleIndex = fStyleRunDesc[runIndex]->index; if (outFont != NULL) *outFont = fStyleRecord[styleIndex]->style.font; if (outColor != NULL) *outColor = fStyleRecord[styleIndex]->style.color; } STEStyleRange* BTextView::StyleBuffer::GetStyleRange(int32 startOffset, int32 endOffset) const { int32 startIndex = OffsetToRun(startOffset); int32 endIndex = OffsetToRun(endOffset); int32 numStyles = endIndex - startIndex + 1; if (numStyles < 1) numStyles = 1; STEStyleRange* result = AllocateStyleRange(numStyles); if (result == NULL) return NULL; STEStyleRun* run = &result->runs[0]; for (int32 index = 0; index < numStyles; index++) { *run = (*this)[startIndex + index]; run->offset -= startOffset; if (run->offset < 0) run->offset = 0; run++; } return result; } void BTextView::StyleBuffer::RemoveStyleRange(int32 fromOffset, int32 toOffset) { int32 fromIndex = fStyleRunDesc.OffsetToRun(fromOffset); int32 toIndex = fStyleRunDesc.OffsetToRun(toOffset) - 1; int32 count = toIndex - fromIndex; if (count > 0) { RemoveStyles(fromIndex + 1, count); toIndex = fromIndex; } fStyleRunDesc.BumpOffset(fromOffset - toOffset, fromIndex + 1); if (toIndex == fromIndex && toIndex < fStyleRunDesc.ItemCount() - 1) { STEStyleRunDesc* runDesc = fStyleRunDesc[toIndex + 1]; runDesc->offset = fromOffset; } if (fromIndex < fStyleRunDesc.ItemCount() - 1) { STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; if (runDesc->offset == (runDesc + 1)->offset) { RemoveStyles(fromIndex); fromIndex--; } } if (fromIndex >= 0 && fromIndex < fStyleRunDesc.ItemCount() - 1) { STEStyleRunDesc* runDesc = fStyleRunDesc[fromIndex]; if (runDesc->index == (runDesc + 1)->index) RemoveStyles(fromIndex + 1); } } void BTextView::StyleBuffer::RemoveStyles(int32 index, int32 count) { for (int32 i = index; i < index + count; i++) fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index); fStyleRunDesc.RemoveDescs(index, count); } int32 BTextView::StyleBuffer::Iterate(int32 fromOffset, int32 length, InlineInput* input, const BFont** outFont, const rgb_color** outColor, float* outAscent, float* outDescent, uint32*) const { // TODO: Handle the InlineInput style here in some way int32 numRuns = fStyleRunDesc.ItemCount(); if (length < 1 || numRuns < 1) return 0; int32 result = length; int32 runIndex = fStyleRunDesc.OffsetToRun(fromOffset); STEStyleRunDesc* run = fStyleRunDesc[runIndex]; if (outFont != NULL) *outFont = &fStyleRecord[run->index]->style.font; if (outColor != NULL) *outColor = &fStyleRecord[run->index]->style.color; if (outAscent != NULL) *outAscent = fStyleRecord[run->index]->ascent; if (outDescent != NULL) *outDescent = fStyleRecord[run->index]->descent; if (runIndex < numRuns - 1) { int32 nextOffset = (run + 1)->offset - fromOffset; result = min_c(result, nextOffset); } return result; } int32 BTextView::StyleBuffer::OffsetToRun(int32 offset) const { return fStyleRunDesc.OffsetToRun(offset); } void BTextView::StyleBuffer::BumpOffset(int32 delta, int32 index) { fStyleRunDesc.BumpOffset(delta, index); } STEStyleRun BTextView::StyleBuffer::operator[](int32 index) const { STEStyleRun run; if (fStyleRunDesc.ItemCount() < 1) { run.offset = 0; run.style = fNullStyle; } else { STEStyleRunDesc* runDesc = fStyleRunDesc[index]; run.offset = runDesc->offset; run.style = fStyleRecord[runDesc->index]->style; } return run; } // TODO: Horrible name, but can't think of a better one // ? CompareStyles ? // ? FilterStyles ? static void FixupMode(const STEStyle &firstStyle, const STEStyle &otherStyle, uint32 &mode, bool &sameColor) { if ((mode & B_FONT_FAMILY_AND_STYLE) != 0) { if (firstStyle.font != otherStyle.font) mode &= ~B_FONT_FAMILY_AND_STYLE; } if ((mode & B_FONT_SIZE) != 0) { if (firstStyle.font.Size() != otherStyle.font.Size()) mode &= ~B_FONT_SIZE; } if ((mode & B_FONT_SHEAR) != 0) { if (firstStyle.font.Shear() != otherStyle.font.Shear()) mode &= ~B_FONT_SHEAR; } if ((mode & B_FONT_FALSE_BOLD_WIDTH) != 0) { if (firstStyle.font.FalseBoldWidth() != otherStyle.font.FalseBoldWidth()) { mode &= ~B_FONT_FALSE_BOLD_WIDTH; } } if (firstStyle.color != otherStyle.color) sameColor = false; // TODO: Finish this: handle B_FONT_FACE, B_FONT_FLAGS, etc. // if needed } void BTextView::StyleBuffer::ContinuousGetStyle(BFont *outFont, uint32* ioMode, rgb_color* outColor, bool* sameColor, int32 fromOffset, int32 toOffset) const { uint32 mode = B_FONT_ALL; if (fStyleRunDesc.ItemCount() < 1) { if (ioMode) *ioMode = mode; if (outFont != NULL) *outFont = fNullStyle.font; if (outColor != NULL) *outColor = fNullStyle.color; if (sameColor != NULL) *sameColor = true; return; } int32 fromIndex = OffsetToRun(fromOffset); int32 toIndex = OffsetToRun(toOffset - 1); if (fromIndex == toIndex) { int32 styleIndex = fStyleRunDesc[fromIndex]->index; const STEStyle* style = &fStyleRecord[styleIndex]->style; if (ioMode != NULL) *ioMode = mode; if (outFont != NULL) *outFont = style->font; if (outColor != NULL) *outColor = style->color; if (sameColor != NULL) *sameColor = true; } else { bool oneColor = true; int32 styleIndex = fStyleRunDesc[toIndex]->index; STEStyle theStyle = fStyleRecord[styleIndex]->style; for (int32 i = fromIndex; i < toIndex; i++) { styleIndex = fStyleRunDesc[i]->index; FixupMode(fStyleRecord[styleIndex]->style, theStyle, mode, oneColor); } if (ioMode != NULL) *ioMode = mode; if (outFont != NULL) *outFont = theStyle.font; if (outColor != NULL) *outColor = theStyle.color; if (sameColor != NULL) *sameColor = oneColor; } }