* 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
*/
#include "InlineInput.h"
#include "StyleBuffer.h"
#include <View.h>
#include <stdio.h>
_BStyleRunDescBuffer_::_BStyleRunDescBuffer_()
:
_BTextViewSupportBuffer_<STEStyleRunDesc>(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;
}
_BStyleRecordBuffer_::_BStyleRecordBuffer_()
:
_BTextViewSupportBuffer_<STEStyleRecord>()
{
}
int32
_BStyleRecordBuffer_::InsertRecord(const BFont* inFont,
const rgb_color* inColor)
{
int32 index = 0;
if (MatchRecord(inFont, inColor, &index))
return index;
font_height fh;
inFont->GetHeight(&fh);
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;
}
}
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;
}
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;
}
}
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
{
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;
}
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;
}
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;
}
}