diff --git a/src/kits/interface/BTextView/LineBuffer.cpp b/src/kits/interface/BTextView/LineBuffer.cpp index 6225920e0b..567977e6ca 100644 --- a/src/kits/interface/BTextView/LineBuffer.cpp +++ b/src/kits/interface/BTextView/LineBuffer.cpp @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -25,7 +25,6 @@ //------------------------------------------------------------------------------ // Standard Includes ----------------------------------------------------------- -#include // System Includes ------------------------------------------------------------- #include "LineBuffer.h" @@ -40,90 +39,94 @@ //------------------------------------------------------------------------------ _BLineBuffer_::_BLineBuffer_() - : fBlockSize(20), - fItemCount(2), - fPhysicalSize(22), - fObjectList(NULL) + : _BTextViewSupportBuffer_(20, 2) { - fObjectList = new STELine[fPhysicalSize]; - - memset(fObjectList, 0, fPhysicalSize * sizeof(STELine)); } //------------------------------------------------------------------------------ _BLineBuffer_::~_BLineBuffer_() { - delete[] fObjectList; } //------------------------------------------------------------------------------ -void _BLineBuffer_::InsertLine(STELine *line, int32 index) +void +_BLineBuffer_::InsertLine(STELine *inLine, int32 index) { - if (fItemCount == fPhysicalSize - 1) - { - STELine *new_list = new STELine[fPhysicalSize + fBlockSize]; + InsertItemsAt(1, index, inLine); +} +//------------------------------------------------------------------------------ +void +_BLineBuffer_::RemoveLines(int32 index, int32 count) +{ + RemoveItemsAt(count, index); +} +//------------------------------------------------------------------------------ +void +_BLineBuffer_::RemoveLineRange(int32 fromOffset, int32 toOffset) +{ + int32 fromLine = OffsetToLine(fromOffset); + int32 toLine = OffsetToLine(toOffset); - memcpy(new_list, fObjectList, fPhysicalSize * sizeof(STELine)); - delete fObjectList; - fObjectList = new_list; + int32 count = toLine - fromLine; + if (count > 0) + RemoveLines(fromLine + 1, count); - fPhysicalSize += fBlockSize; + BumpOffset(fromOffset - toOffset, fromLine + 1); +} +//------------------------------------------------------------------------------ +int32 +_BLineBuffer_::OffsetToLine(int32 offset) const +{ + int32 minIndex = 0; + int32 maxIndex = fItemCount - 1; + int32 index = 0; + + while (minIndex < maxIndex) { + index = (minIndex + maxIndex) >> 1; + if (offset >= fBuffer[index].offset) { + if (offset < fBuffer[index + 1].offset) + break; + else + minIndex = index + 1; + } + else + maxIndex = index; } - - if (index < fItemCount) - memmove(fObjectList + index + 1, fObjectList + index, - (fItemCount - index) * sizeof(STELine)); - memcpy(fObjectList + index, line, sizeof(STELine)); - - fItemCount++; + + return index; } //------------------------------------------------------------------------------ -void _BLineBuffer_::RemoveLines(int32 index, int32 count) +int32 _BLineBuffer_::PixelToLine(float pixel) const { - memmove(fObjectList + index, fObjectList + index + count, - (fItemCount - index - count) * sizeof(STELine)); - - fItemCount -= count; -} -//------------------------------------------------------------------------------ -void _BLineBuffer_::RemoveLineRange(int32 from, int32 to) -{ - int32 linefrom = OffsetToLine(from); - int32 lineto = OffsetToLine(to); - - if (linefrom < lineto) - RemoveLines(linefrom + 1, lineto - linefrom); - - BumpOffset(linefrom + 1, from - to); -} -//------------------------------------------------------------------------------ -int32 _BLineBuffer_::OffsetToLine(int32 offset) const -{ - if (offset == 0) - return 0; - - for (int i = 0; i < fItemCount; i++) - { - if (offset < fObjectList[i].fOffset) - return i - 1; + int32 minIndex = 0; + int32 maxIndex = fItemCount - 1; + int32 index = 0; + + while (minIndex < maxIndex) { + index = (minIndex + maxIndex) >> 1; + if (pixel >= fBuffer[index].origin) { + if (pixel < fBuffer[index + 1].origin) + break; + else + minIndex = index + 1; + } + else + maxIndex = index; } - - return fItemCount - 2; + + return index; } //------------------------------------------------------------------------------ -int32 _BLineBuffer_::PixelToLine(float height) const -{ - for (int i = 0; i < fItemCount; i++) - { - if (height < fObjectList[i].fHeight) - return i - 1; - } - - return fItemCount - 2; +void +_BLineBuffer_::BumpOrigin(float delta, long index) +{ + for (long i = index; i < fItemCount; i++) + fBuffer[i].origin += delta; } //------------------------------------------------------------------------------ -void _BLineBuffer_::BumpOffset(int32 line, int32 offset) +void +_BLineBuffer_::BumpOffset(int32 delta, int32 index) { - for (int i = line; i < fItemCount; i++) - fObjectList[i].fOffset += offset; + for (long i = index; i < fItemCount; i++) + fBuffer[i].offset += delta; } //------------------------------------------------------------------------------ diff --git a/src/kits/interface/BTextView/LineBuffer.h b/src/kits/interface/BTextView/LineBuffer.h index 0563b64f9f..67fde9c789 100644 --- a/src/kits/interface/BTextView/LineBuffer.h +++ b/src/kits/interface/BTextView/LineBuffer.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -27,45 +27,56 @@ // Standard Includes ----------------------------------------------------------- // System Includes ------------------------------------------------------------- -#include "SupportDefs.h" +#include +#include "TextViewSupportBuffer.h" // Project Includes ------------------------------------------------------------ // Local Includes -------------------------------------------------------------- // Local Defines --------------------------------------------------------------- -struct STELine { - int32 fOffset; - float fHeight; - float fLineHeight; - float fWidth; -}; +typedef struct STELine { + long offset; // offset of first character of line + float origin; // pixel position of top of line + float ascent; // maximum ascent for line + float width; // width of line +} STELine, *STELinePtr; // Globals --------------------------------------------------------------------- // _BLineBuffer_ class --------------------------------------------------------- -class _BLineBuffer_ { +class _BLineBuffer_ : public _BTextViewSupportBuffer_ { public: - _BLineBuffer_(); -virtual ~_BLineBuffer_(); + _BLineBuffer_(); +virtual ~_BLineBuffer_(); - void InsertLine(STELine *line, int32); - void RemoveLines(int32 index, int32 count); - void RemoveLineRange(int32 from, int32 to); + void InsertLine(STELine *inLine, int32 index); + void RemoveLines(int32 index, int32 count = 1); + void RemoveLineRange(int32 fromOffset, int32 toOffset); - int32 OffsetToLine(int32 offset) const; - int32 PixelToLine(float height) const; + int32 OffsetToLine(int32 offset) const; + int32 PixelToLine(float pixel) const; - void BumpOffset(int32 line, int32 offset); - void BumpOrigin(float, int32); + void BumpOrigin(float delta, int32 index); + void BumpOffset(int32 delta, int32 index); - int32 fBlockSize; - int32 fItemCount; - size_t fPhysicalSize; - STELine *fObjectList; + long NumLines() const; + const STELinePtr operator[](int32 index) const; }; //------------------------------------------------------------------------------ +inline int32 +_BLineBuffer_::NumLines() const +{ + return fItemCount - 1; +} +//------------------------------------------------------------------------------ +inline const STELinePtr +_BLineBuffer_::operator[](int32 index) const +{ + return &fBuffer[index]; +} +//------------------------------------------------------------------------------ /* * $Log $ diff --git a/src/kits/interface/BTextView/StyleBuffer.cpp b/src/kits/interface/BTextView/StyleBuffer.cpp index 476b58e16e..b6e6b2ad9d 100644 --- a/src/kits/interface/BTextView/StyleBuffer.cpp +++ b/src/kits/interface/BTextView/StyleBuffer.cpp @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -38,115 +38,536 @@ // Globals --------------------------------------------------------------------- //------------------------------------------------------------------------------ -_BStyleBuffer_::_BStyleBuffer_(const BFont *font, const rgb_color *color) - : fRuns(10, 10), - fRecords(10, 10) -{ - rgb_color black = {0, 0, 0, 255}; - - SetNullStyle(0, font ? font : be_plain_font, color ? color : &black, 0); -} -//------------------------------------------------------------------------------ -_BStyleBuffer_::~_BStyleBuffer_() +_BStyleRunDescBuffer_::_BStyleRunDescBuffer_() + : _BTextViewSupportBuffer_(20) { } //------------------------------------------------------------------------------ -void _BStyleBuffer_::SetNullStyle(uint32 mode, const BFont *font, - const rgb_color *color, int32) +void +_BStyleRunDescBuffer_::InsertDesc(STEStyleRunDescPtr inDesc, int32 index) { - // TODO: use mode. - if (font) - fNullStyle.fFont = *font; - - if (color) - fNullStyle.fColor = *color; + InsertItemsAt(1, index, inDesc); } //------------------------------------------------------------------------------ -void _BStyleBuffer_::GetNullStyle(const BFont **font, const rgb_color **color) const +void +_BStyleRunDescBuffer_::RemoveDescs(int32 index, int32 count) { - *font = &fNullStyle.fFont; - *color = &fNullStyle.fColor; + RemoveItemsAt(count, index); } //------------------------------------------------------------------------------ -void _BStyleBuffer_::SyncNullStyle(int32) +int32 +_BStyleRunDescBuffer_::OffsetToRun(int32 offset) const { -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::InvalidateNullStyle() -{ -} -//------------------------------------------------------------------------------ -bool _BStyleBuffer_::IsValidNullStyle() const -{ - // TODO: why would a style be invalid? - return true; -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::SetStyle(uint32 mode, const BFont *font, BFont *, - const rgb_color *color, rgb_color *) const -{ -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::GetStyle(uint32 mode, BFont *font, rgb_color *color) const -{ -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::SetStyleRange(int32 from, int32 to, int32, uint32 mode, - const BFont *, const rgb_color *) -{ -} -//------------------------------------------------------------------------------ -int32 _BStyleBuffer_::GetStyleRange(int32 from, int32 to) const -{ - return 0; -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::ContinuousGetStyle(BFont *, uint32 *, rgb_color *, bool *, - int32, int32) const -{ -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::RemoveStyles(int32 from, int32 to) -{ -} -//------------------------------------------------------------------------------ -void _BStyleBuffer_::RemoveStyleRange(int32 from, int32 to) -{ -} -//------------------------------------------------------------------------------ -int32 _BStyleBuffer_::OffsetToRun(int32 offset) const -{ - if (offset == 0) + 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)) { + break; + } + else { + if (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_() +{ +} +//------------------------------------------------------------------------------ +int32 +_BStyleRecordBuffer_::InsertRecord(const BFont *inFont, + const rgb_color *inColor) +{ + int32 index = 0; - for (int i = 0; i < fRuns.fCount; i++) - { - if (offset < fRuns.fItems[i].fOffset) - return i - 1; + // 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 + index = fItemCount; + STEStyleRecord newRecord; + newRecord.refs = 0; + newRecord.ascent = fh.ascent; + newRecord.descent = fh.descent + fh.leading; + newRecord.style.font = *inFont; + newRecord.style.color = *inColor; + InsertItemsAt(1, index, &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->Size() == fBuffer[i].style.font.Size()) && + (inFont->Shear() == fBuffer[i].style.font.Shear()) && + (inFont->Face() == fBuffer[i].style.font.Face()) && + (inColor->red == fBuffer[i].style.color.red) && + (inColor->green == fBuffer[i].style.color.green) && + (inColor->blue == fBuffer[i].style.color.blue) && + (inColor->alpha == fBuffer[i].style.color.alpha) ) { + *outIndex = i; + return true; + } } - return fRuns.fCount - 2; + return false; } //------------------------------------------------------------------------------ -void _BStyleBuffer_::BumpOffset(int32 run, int32 offset) +_BStyleBuffer_::_BStyleBuffer_(const BFont *inFont, const rgb_color *inColor) { - for (int i = run; i < fRuns.fCount; i++) - fRuns.fItems[i].fOffset += offset; + fValidNullStyle = true; + fNullStyle.font = *inFont; + fNullStyle.color = *inColor; } -//------------------------------------------------------------------------------ -/*void _BStyleBuffer_::Iterate(int32, int32, _BInlineInput_ *, const BFont **, - const rgb_color **, float *, uint32 *) const +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::InvalidateNullStyle() +{ + fValidNullStyle = false; +} +//------------------------------------------------------------------------------ +bool +_BStyleBuffer_::IsValidNullStyle() const +{ + return fValidNullStyle; +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::SyncNullStyle(int32 offset) +{ + if ((fValidNullStyle) || (fStyleRunDesc.ItemCount() < 1)) + return; + + int32 index = OffsetToRun(offset); + fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; + + fValidNullStyle = true; +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::SetNullStyle(uint32 inMode, const BFont *inFont, + const rgb_color *inColor, int32 offset) +{ + if ((fValidNullStyle) || (fStyleRunDesc.ItemCount() < 1)) + SetStyle(inMode, inFont, &fNullStyle.font, inColor, &fNullStyle.color); + else { + int32 index = OffsetToRun(offset - 1); + fNullStyle = fStyleRecord[fStyleRunDesc[index]->index]->style; + SetStyle(inMode, inFont, &fNullStyle.font, inColor, &fNullStyle.color); + } + + fValidNullStyle = true; +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::GetNullStyle(const BFont **font, + const rgb_color **color) const +{ + if (font) + *font = &fNullStyle.font; + if (color) + *color = &fNullStyle.color; +} +//------------------------------------------------------------------------------ +bool +_BStyleBuffer_::IsContinuousStyle(uint32 *ioMode, STEStylePtr outStyle, + int32 fromOffset, int32 toOffset) +{ + if (fStyleRunDesc.ItemCount() < 1) { + SetStyle(*ioMode, &fNullStyle.font, &outStyle->font, + &fNullStyle.color, &outStyle->color); + return true; + } + + bool result = true; + int32 fromIndex = OffsetToRun(fromOffset); + int32 toIndex = OffsetToRun(toOffset - 1); + + if (fromIndex == toIndex) { + int32 styleIndex = fStyleRunDesc[fromIndex]->index; + STEStylePtr style = &fStyleRecord[styleIndex]->style; + + SetStyle(*ioMode, &style->font, &outStyle->font, &style->color, + &outStyle->color); + result = true; + } + else { + int32 styleIndex = fStyleRunDesc[toIndex]->index; + STEStyle theStyle = fStyleRecord[styleIndex]->style; + //STEStylePtr style = NULL; + + /* for (int32 i = fromIndex; i < toIndex; i++) { + styleIndex = fStyleRunDesc[i]->index; + style = &fStyleRecord[styleIndex]->style; + + if (*ioMode & doFont) { + if (strcmp(theStyle.font, style->font) != 0) { + *ioMode &= ~doFont; + result = false; + } + } + + if (*ioMode & doSize) { + if (theStyle.size != style->size) { + *ioMode &= ~doSize; + result = false; + } + } + + if (*ioMode & doShear) { + if (theStyle.shear != style->shear) { + *ioMode &= ~doShear; + result = false; + } + } + + if (*ioMode & doUnderline) { + if (theStyle.underline != style->underline) { + *ioMode &= ~doUnderline; + result = false; + } + } + + if (*ioMode & doColor) { + if ( (theStyle.color.red != style->color.red) || + (theStyle.color.green != style->color.green) || + (theStyle.color.blue != style->color.blue) || + (theStyle.color.alpha != style->color.alpha) ) { + *ioMode &= ~doColor; + result = false; + } + } + + if (*ioMode & doExtra) { + if (theStyle.extra != style->extra) { + *ioMode &= ~doExtra; + result = false; + } + } + }*/ + + SetStyle(*ioMode, &theStyle.font, &outStyle->font, &theStyle.color, + &outStyle->color); + } + + return result; +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::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 styleIndex = 0; + int32 offset = fromOffset; + int32 runIndex = OffsetToRun(offset); + do { + STEStyleRunDesc runDesc = *fStyleRunDesc[runIndex]; + int32 runEnd = textLen; + if (runIndex < (fStyleRunDesc.ItemCount() - 1)) + runEnd = fStyleRunDesc[runIndex + 1]->offset; + + STEStyle style = fStyleRecord[runDesc.index]->style; + SetStyle(inMode, inFont, &style.font, inColor, &style.color); + + styleIndex = fStyleRecord.InsertRecord(inFont, inColor); + + 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 +_BStyleBuffer_::GetStyle(int32 inOffset, BFont *outFont, + rgb_color *outColor) const +{ + if (fStyleRunDesc.ItemCount() < 1) + { + if (outFont) + *outFont = fNullStyle.font; + if (outColor) + *outColor = fNullStyle.color; + return; + } + + int32 runIndex = OffsetToRun(inOffset); + int32 styleIndex = fStyleRunDesc[runIndex]->index; + + if (outFont) + *outFont = fStyleRecord[styleIndex]->style.font; + if (outColor) + *outColor = fStyleRecord[styleIndex]->style.color; +} +//------------------------------------------------------------------------------ +STEStyleRangePtr +_BStyleBuffer_::GetStyleRange(int32 startOffset, + int32 endOffset) const +{ + STEStyleRangePtr result = NULL; + + int32 startIndex = OffsetToRun(startOffset); + int32 endIndex = OffsetToRun(endOffset); + + int32 numStyles = endIndex - startIndex + 1; + numStyles = (numStyles < 1) ? 1 : numStyles; + result = (STEStyleRangePtr)malloc(sizeof(int32) + + (sizeof(STEStyleRun) * numStyles)); + + result->count = numStyles; + STEStyleRunPtr run = &result->runs[0]; + for (int32 index = 0; index < numStyles; index++) { + *run = (*this)[startIndex + index]; + run->offset -= startOffset; + run->offset = (run->offset < 0) ? 0 : run->offset; + run++; + } + + return result; +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::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))) { + STEStyleRunDescPtr runDesc = fStyleRunDesc[toIndex + 1]; + runDesc->offset = fromOffset; + } + + if (fromIndex < (fStyleRunDesc.ItemCount() - 1)) { + STEStyleRunDescPtr runDesc = fStyleRunDesc[fromIndex]; + if (runDesc->offset == (runDesc + 1)->offset) { + RemoveStyles(fromIndex); + fromIndex--; + } + } + + if ((fromIndex >= 0) && (fromIndex < (fStyleRunDesc.ItemCount() - 1))) { + STEStyleRunDescPtr runDesc = fStyleRunDesc[fromIndex]; + if (runDesc->index == (runDesc + 1)->index) + RemoveStyles(fromIndex + 1); + } +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::RemoveStyles(int32 index, int32 count) +{ + for (int32 i = index; i < (index + count); i++) + fStyleRecord.RemoveRecord(fStyleRunDesc[i]->index); + + fStyleRunDesc.RemoveDescs(index, count); +} +//------------------------------------------------------------------------------ +int32 +_BStyleBuffer_::Iterate(int32 fromOffset, int32 length, /*_BInlineInput_ *,*/ + 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); + STEStyleRunDescPtr 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 = (result > nextOffset) ? nextOffset : result; + } + + return result; +} +//------------------------------------------------------------------------------ +int32 +_BStyleBuffer_::OffsetToRun(int32 offset) const +{ + return fStyleRunDesc.OffsetToRun(offset); +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::BumpOffset(int32 delta, int32 index) +{ + fStyleRunDesc.BumpOffset(delta, index); +} +//------------------------------------------------------------------------------ +void +_BStyleBuffer_::SetStyle(uint32 mode, const BFont *fromFont, + BFont *toFont, const rgb_color *fromColor, + rgb_color *toColor) +{ + if (mode & doFont) + toFont->SetFamilyAndStyle(fromFont->FamilyAndStyle()); + + if (mode & doSize) { + if (mode & addSize) + toFont->SetSize(fromFont->Size()); + else + toFont->SetSize(fromFont->Size()); + } + + if (mode & doShear) + toFont->SetShear(fromFont->Shear()); + + if (mode & doUnderline) + toFont->SetFace(fromFont->Face()); + + if (mode & doColor) + *toColor = *fromColor; +} +//------------------------------------------------------------------------------ +STEStyleRun +_BStyleBuffer_::operator[](int32 index) const +{ + STEStyleRun run; + + if (fStyleRunDesc.ItemCount() < 1) { + run.offset = 0; + run.style = fNullStyle; + } else { + STEStyleRunDescPtr runDesc = fStyleRunDesc[index]; + STEStyleRecordPtr record = fStyleRecord[runDesc->index]; + run.offset = runDesc->offset; + run.style = record->style; + } + + return run; +} +//------------------------------------------------------------------------------ + +/*void _BStyleBuffer_::ContinuousGetStyle(BFont *, uint32 *, rgb_color *, bool *, + int32, int32) const { }*/ -//------------------------------------------------------------------------------ -STEStyleRecord *_BStyleBuffer_::operator[](int32 index) -{ - if (index < fRecords.fCount) - return fRecords.fItems + index; - else - return NULL; -} -//------------------------------------------------------------------------------ /* * $Log $ diff --git a/src/kits/interface/BTextView/StyleBuffer.h b/src/kits/interface/BTextView/StyleBuffer.h index 74d80c9b44..1eb9e0376f 100644 --- a/src/kits/interface/BTextView/StyleBuffer.h +++ b/src/kits/interface/BTextView/StyleBuffer.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -29,67 +29,171 @@ // System Includes ------------------------------------------------------------- #include #include +#include "TextViewSupportBuffer.h" #include // Project Includes ------------------------------------------------------------ // Local Includes -------------------------------------------------------------- -#include "TextViewSupportBuffer.h" // Local Defines --------------------------------------------------------------- -struct STEStyleRunDesc { - int32 fOffset; - int32 fStyle; +enum { + doFont = 0x00000001, // set font + doSize = 0x00000002, // set size + doShear = 0x00000004, // set shear + doUnderline = 0x00000008, // set underline + doColor = 0x00000010, // set color + doExtra = 0x00000020, // set the extra field + doAll = 0x0000003F, // set everything + addSize = 0x00010000 // add size value }; -struct STEStyleRecord { - BFont fFont; - rgb_color fColor; -}; +typedef struct STEStyle { + BFont font; // font + rgb_color color; // pen color +} STEStyle, *STEStylePtr; + +typedef struct STEStyleRun { + long offset; // byte offset of first character of run + STEStyle style; // style info +} STEStyleRun, *STEStyleRunPtr; + +typedef struct STEStyleRange { + long count; // number of style runs + STEStyleRun runs[1]; // array of count number of runs +} STEStyleRange, *STEStyleRangePtr; + +typedef struct STEStyleRecord { + long refs; // reference count for this style + float ascent; // ascent for this style + float descent; // descent for this style + STEStyle style; // style info +} STEStyleRecord, *STEStyleRecordPtr; + +typedef struct STEStyleRunDesc { + long offset; // byte offset of first character of run + long index; // index of corresponding style record +} STEStyleRunDesc, *STEStyleRunDescPtr; // Globals --------------------------------------------------------------------- +// _BStyleRunDescBuffer_ class ------------------------------------------------- +class _BStyleRunDescBuffer_ : public _BTextViewSupportBuffer_ { + +public: + _BStyleRunDescBuffer_(); + + void InsertDesc(STEStyleRunDescPtr inDesc, int32 index); + void RemoveDescs(int32 index, int32 count = 1); + + long OffsetToRun(int32 offset) const; + void BumpOffset(int32 delta, int32 index); + + const STEStyleRunDescPtr operator[](int32 index) const; +}; +//------------------------------------------------------------------------------ +inline const +STEStyleRunDescPtr _BStyleRunDescBuffer_::operator[](int32 index) const +{ + return &fBuffer[index]; +} +//------------------------------------------------------------------------------ + +// _BStyleRecordBuffer_ class -------------------------------------------------- +class _BStyleRecordBuffer_ : public _BTextViewSupportBuffer_ { + +public: + _BStyleRecordBuffer_(); + + int32 InsertRecord(const BFont *inFont, const rgb_color *inColor); + void CommitRecord(int32 index); + void RemoveRecord(int32 index); + + bool MatchRecord(const BFont *inFont, const rgb_color *inColor, + int32 *outIndex); + + const STEStyleRecordPtr operator[](int32 index) const; +}; +//------------------------------------------------------------------------------ +inline const +STEStyleRecordPtr _BStyleRecordBuffer_::operator[](int32 index) const +{ + return &fBuffer[index]; +} +//------------------------------------------------------------------------------ + // _BStyleBuffer_ class -------------------------------------------------------- class _BStyleBuffer_ { public: - _BStyleBuffer_(const BFont *, const rgb_color *); -virtual ~_BStyleBuffer_(); + _BStyleBuffer_(const BFont *inFont, + const rgb_color *inColor); + + void InvalidateNullStyle(); + bool IsValidNullStyle() const; + + void SyncNullStyle(int32 offset); + void SetNullStyle(uint32 inMode, const BFont *inFont, + const rgb_color *inColor, int32 offset = 0); + void GetNullStyle(const BFont **font, + const rgb_color **color) const; + + bool IsContinuousStyle(uint32 *ioMode, STEStylePtr outStyle, + int32 fromOffset, int32 toOffset); + void SetStyleRange(int32 fromOffset, int32 toOffset, + int32 textLen, uint32 inMode, + const BFont *inFont, const rgb_color *inColor); + void GetStyle(int32 inOffset, BFont *outFont, + rgb_color *outColor) const; + STEStyleRangePtr GetStyleRange(int32 startOffset, int32 endOffset) const; + + void RemoveStyleRange(int32 fromOffset, int32 toOffset); + void RemoveStyles(int32 index, int32 count = 1); + + int32 Iterate(int32 fromOffset, int32 length, /*_BInlineInput_ *,*/ + const BFont **outFont = NULL, + const rgb_color **outColor = NULL, + float *outAscent = NULL, + float *outDescen = NULL, uint32 * = NULL) const; + + int32 OffsetToRun(int32 offset) const; + void BumpOffset(int32 delta, int32 index); + + void SetStyle(uint32 mode, const BFont *fromFont, + BFont *toFont, const rgb_color *fromColor, + rgb_color *toColor); + + STEStyleRun operator[](int32 index) const; + + int32 NumRuns() const; + const _BStyleRunDescBuffer_& RunBuffer() const; + const _BStyleRecordBuffer_& RecordBuffer() const; - void SetNullStyle(uint32, const BFont *, const rgb_color *, int32); - void GetNullStyle(const BFont **, const rgb_color **) const; - void SyncNullStyle(int32); - void InvalidateNullStyle(); - bool IsValidNullStyle() const; - - void SetStyle(uint32, const BFont *, BFont *, const rgb_color *, - rgb_color *) const; - void GetStyle(uint32, BFont *, rgb_color *) const; - - void SetStyleRange(int32, int32, int32, uint32 mode, const BFont *, - const rgb_color *); - int32 GetStyleRange(int32, int32) const; - - void ContinuousGetStyle(BFont *, uint32 *, rgb_color *, bool *, - int32, int32) const; - - void RemoveStyles(int32 from, int32 to); - void RemoveStyleRange(int32 from, int32 to); - - int32 OffsetToRun(int32 offset) const; - - void BumpOffset(int32 run, int32 offset); -// void Iterate(int32, int32, _BInlineInput_ *, const BFont **, -// const rgb_color **, float *, uint32 *) const; - - STEStyleRecord *operator[](int32); - -private: - _BTextViewSupportBuffer_ fRuns; - _BTextViewSupportBuffer_ fRecords; - STEStyleRecord fNullStyle; +protected: + _BStyleRunDescBuffer_ fStyleRunDesc; + _BStyleRecordBuffer_ fStyleRecord; + bool fValidNullStyle; + STEStyle fNullStyle; }; //------------------------------------------------------------------------------ +inline int32 +_BStyleBuffer_::NumRuns() const +{ + return fStyleRunDesc.ItemCount(); +} +//------------------------------------------------------------------------------ +inline const +_BStyleRunDescBuffer_ &_BStyleBuffer_::RunBuffer() const +{ + return fStyleRunDesc; +} +//------------------------------------------------------------------------------ +inline const +_BStyleRecordBuffer_ &_BStyleBuffer_::RecordBuffer() const +{ + return fStyleRecord; +} +//------------------------------------------------------------------------------ /* * $Log $ diff --git a/src/kits/interface/BTextView/TextGapBuffer.cpp b/src/kits/interface/BTextView/TextGapBuffer.cpp index d4b91e561f..3a8a4746a5 100644 --- a/src/kits/interface/BTextView/TextGapBuffer.cpp +++ b/src/kits/interface/BTextView/TextGapBuffer.cpp @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -25,14 +25,15 @@ //------------------------------------------------------------------------------ // Standard Includes ----------------------------------------------------------- -#include +#include +#include // System Includes ------------------------------------------------------------- +#include "TextGapBuffer.h" // Project Includes ------------------------------------------------------------ // Local Includes -------------------------------------------------------------- -#include "TextGapBuffer.h" // Local Defines --------------------------------------------------------------- @@ -40,94 +41,179 @@ //------------------------------------------------------------------------------ _BTextGapBuffer_::_BTextGapBuffer_() - : fText(NULL), - fLogicalBytes(0), - fPhysicalBytes(2048) + : fExtraCount(2048), + fItemCount(0), + fBuffer(NULL), + fBufferCount(fExtraCount + fItemCount), + fGapIndex(fItemCount), + fGapCount(fBufferCount - fGapIndex), + fScratchBuffer(NULL), + fScratchSize(0) { - fText = new char[fPhysicalBytes]; - *fText = '\0'; + fBuffer = (char *)malloc(fExtraCount + fItemCount); + fScratchBuffer = (char*)malloc(0); } //------------------------------------------------------------------------------ _BTextGapBuffer_::~_BTextGapBuffer_() { - delete[] fText; + free(fBuffer); + free(fScratchBuffer); } //------------------------------------------------------------------------------ -void _BTextGapBuffer_::InsertText(const char *text, int32 length, int32 offset) +void +_BTextGapBuffer_::InsertText(const char *inText, int32 inNumItems, + int32 inAtIndex) { - // If needed, resize buffer - if (fPhysicalBytes < fLogicalBytes + length + 1) - Resize(fLogicalBytes + length + 1); - - // Move text after insertion point - memcpy(fText + offset + length, fText + offset, - fLogicalBytes + 1 - offset); - - // Copy new text - memcpy(fText + offset, text, length); - - // Update used bytes - fLogicalBytes += length; + if (inNumItems < 1) + return; + + inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex; + inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; + + if (inAtIndex != fGapIndex) + MoveGapTo(inAtIndex); + + if (fGapCount < inNumItems) + SizeGapTo(inNumItems + fExtraCount); + + memcpy(fBuffer + fGapIndex, inText, inNumItems); + + fGapCount -= inNumItems; + fGapIndex += inNumItems; + fItemCount += inNumItems; } //------------------------------------------------------------------------------ -void _BTextGapBuffer_::RemoveRange(int32 from, int32 to) +void +_BTextGapBuffer_::RemoveRange(int32 start, int32 end) { - // Move text after deletion point - memcpy(fText + from, fText + to, fLogicalBytes + 1 - to); - - // Update used bytes - fLogicalBytes -= to - from; + long inAtIndex = start; + long inNumItems = end - start; + + if (inNumItems < 1) + return; + + inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex; + inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; + + MoveGapTo(inAtIndex); + + fGapCount += inNumItems; + fItemCount -= inNumItems; + + if (fGapCount > fExtraCount) + SizeGapTo(fExtraCount); } //------------------------------------------------------------------------------ -char *_BTextGapBuffer_::Text() +void +_BTextGapBuffer_::MoveGapTo(int32 toIndex) { - if (fLogicalBytes == 0 || fText[fLogicalBytes - 1] != '\0') - { - if (fPhysicalBytes < fLogicalBytes + 1) - { - char *new_text = new char[fLogicalBytes + 1]; - - if (fText) - { - memcpy(new_text, fText, fLogicalBytes ); - delete fText; - } - - fText = new_text; - } - - fText[fLogicalBytes] = '\0'; + if (toIndex == fGapIndex) + return; + + long gapEndIndex = fGapIndex + fGapCount; + long srcIndex = 0; + long dstIndex = 0; + long count = 0; + if (toIndex > fGapIndex) { + long trailGapCount = fBufferCount - gapEndIndex; + srcIndex = toIndex + (gapEndIndex - toIndex); + dstIndex = fGapIndex; + count = fGapCount + (toIndex - srcIndex); + count = (count > trailGapCount) ? trailGapCount : count; } + else { + srcIndex = toIndex; + dstIndex = toIndex + (gapEndIndex - fGapIndex); + count = gapEndIndex - dstIndex; + } + + if (count > 0) + memmove(fBuffer + dstIndex, fBuffer + srcIndex, count); - return fText; + fGapIndex = toIndex; } //------------------------------------------------------------------------------ -char *_BTextGapBuffer_::RealText() +void +_BTextGapBuffer_::SizeGapTo(long inCount) +{ + if (inCount == fGapCount) + return; + + fBuffer = (char *)realloc(fBuffer, fItemCount + inCount); + memmove(fBuffer + fGapIndex + inCount, + fBuffer + fGapIndex + fGapCount, + fBufferCount - (fGapIndex + fGapCount)); + + fGapCount = inCount; + fBufferCount = fItemCount + fGapCount; +} +//------------------------------------------------------------------------------ +const char * +_BTextGapBuffer_::GetString(int32 fromOffset, int32 numChars) +{ + char *result = ""; + + if (numChars < 1) + return (result); + + bool isStartBeforeGap = (fromOffset < fGapIndex); + bool isEndBeforeGap = ((fromOffset + numChars - 1) < fGapIndex); + + if (isStartBeforeGap == isEndBeforeGap) { + result = fBuffer + fromOffset; + if (!isStartBeforeGap) + result += fGapCount; + } + else { + if (fScratchSize < numChars) { + fScratchBuffer = (char *)realloc(fScratchBuffer, numChars); + fScratchSize = numChars; + } + + for (long i = 0; i < numChars; i++) + fScratchBuffer[i] = (*this)[fromOffset + i]; + + result = fScratchBuffer; + } + + return result; +} +//------------------------------------------------------------------------------ +bool +_BTextGapBuffer_::FindChar(char inChar, long fromIndex, long *ioDelta) +{ + long numChars = *ioDelta; + for (long i = 0; i < numChars; i++) { + if (((*this)[fromIndex + i] & 0xc0) == 0x80) + continue; + if ((*this)[fromIndex + i] == inChar) { + *ioDelta = i; + return (true); + } + } + + return false; +} +//------------------------------------------------------------------------------ +const char * +_BTextGapBuffer_::Text() +{ + MoveGapTo(fItemCount); + fBuffer[fItemCount] = '\0'; + + return fBuffer; +} +//------------------------------------------------------------------------------ +/*char *_BTextGapBuffer_::RealText() { return fText; -} +}*/ //------------------------------------------------------------------------------ -char _BTextGapBuffer_::RealCharAt(int32 offset) const +/*char +_BTextGapBuffer_::RealCharAt(int32 offset) const { - return *(fText + offset); -} -//------------------------------------------------------------------------------ -void _BTextGapBuffer_::Resize(int32 size) -{ - if (fPhysicalBytes < size) - { - char *text = new char[size]; - - if (fText) - { - memcpy(text, fText, fLogicalBytes); - delete fText; - } - - fText = text; - fPhysicalBytes = size; - } -} + return *(fBuffer + offset); +}*/ //------------------------------------------------------------------------------ /* diff --git a/src/kits/interface/BTextView/TextGapBuffer.h b/src/kits/interface/BTextView/TextGapBuffer.h index 32fc3b5a46..afb19f38f4 100644 --- a/src/kits/interface/BTextView/TextGapBuffer.h +++ b/src/kits/interface/BTextView/TextGapBuffer.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -41,34 +41,57 @@ class _BTextGapBuffer_ { public: - _BTextGapBuffer_(); -virtual ~_BTextGapBuffer_(); + _BTextGapBuffer_(); +virtual ~_BTextGapBuffer_(); - void InsertText(const char *text, int32 length, int32 offset); - void RemoveRange(int32 from, int32 to); - - char *Text(); - char *RealText(); - void GetString(int32 offset, int32 length, char *buffer); - void GetString(int32, int32 *); + void InsertText(const char *inText, int32 inNumItems, int32 inAtIndex); + void RemoveRange(int32 start, int32 end); - char RealCharAt(int32 offset) const; - bool FindChar(char c, int32 inOffset, int32 *outOffset); + void MoveGapTo(int32 toIndex); + void SizeGapTo(int32 inCount); + + const char *GetString(int32 fromOffset, int32 numChars); + bool FindChar(char inChar, int32 fromIndex, int32 *ioDelta); + + const char *Text(); + int32 Length() const; + char operator[](int32 index) const; + + +// char *RealText(); +// void GetString(int32 offset, int32 length, char *buffer); +// void GetString(int32, int32 *); + +// char RealCharAt(int32 offset) const; - void SizeGapTo(int32); - void MoveGapTo(int32); - void InsertText(BFile *, int32, int32, int32); - bool PasswordMode() const; - void SetPasswordMode(bool); +// void InsertText(BFile *, int32, int32, int32); +// bool PasswordMode() const; +// void SetPasswordMode(bool); - void Resize(int32 size); +// void Resize(int32 size); - char *fText; - int32 fLogicalBytes; - int32 fPhysicalBytes; +protected: + int32 fExtraCount; // when realloc()-ing + int32 fItemCount; // logical count + char *fBuffer; // allocated memory + int32 fBufferCount; // physical count + int32 fGapIndex; // gap position + int32 fGapCount; // gap count + char *fScratchBuffer; // for GetString + int32 fScratchSize; // scratch size }; //------------------------------------------------------------------------------ - +inline int32 +_BTextGapBuffer_::Length() const +{ + return fItemCount; +} +//------------------------------------------------------------------------------ +inline char +_BTextGapBuffer_::operator[](long index) const +{ + return (index < fGapIndex) ? fBuffer[index] : fBuffer[index + fGapCount]; +} /* * $Log $ * diff --git a/src/kits/interface/BTextView/TextView.cpp b/src/kits/interface/BTextView/TextView.cpp index 32e3e56a35..583b439892 100644 --- a/src/kits/interface/BTextView/TextView.cpp +++ b/src/kits/interface/BTextView/TextView.cpp @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -25,31 +25,53 @@ //------------------------------------------------------------------------------ // Standard Includes ----------------------------------------------------------- -#include +#include // System Includes ------------------------------------------------------------- #include -#include -#include #include -#include +#include #include #include -#include -#include -#include +#include +#include +#include +#include + + +#include "TextGapBuffer.h" +#include "LineBuffer.h" +#include "StyleBuffer.h" + +//#include // Project Includes ------------------------------------------------------------ // Local Includes -------------------------------------------------------------- -#include "TextGapBuffer.h" -#include "LineBuffer.h" -#include "StyleBuffer.h" -#include "moreUTF8.h" + // Local Defines --------------------------------------------------------------- -using std::min; -using std::max; +struct flattened_text_run { + int32 offset; + char family[64]; + char style[64]; + float size; + float shear; /* typically 90.0 */ + uint16 face; /* typically 0 */ + uint8 red; + uint8 green; + uint8 blue; + uint8 alpha; /* 255 == opaque */ + uint16 _reserved_; /* 0 */ +}; + +struct flattened_text_run_array { + uchar magic[4]; /* 41 6c 69 21 */ + uchar version[4]; /* 00 00 00 00 */ + int32 count; + flattened_text_run styles[1]; +}; + // _BTextTrackState_ class ----------------------------------------------------- class _BTextTrackState_ { @@ -65,7 +87,8 @@ public: //------------------------------------------------------------------------------ // Globals --------------------------------------------------------------------- -static property_info prop_list[] = +static property_info +prop_list[] = { { "Selection", @@ -126,17 +149,17 @@ BTextView::BTextView(BRect frame, const char *name, BRect textRect, fText(NULL), fLines(NULL), fStyles(NULL), - //fTextRect + fTextRect(textRect), fSelStart(0), fSelEnd(0), - fCaretVisible(true), - //fCaretTime - //fClickOffset - //fClickCount - //fClickTime - //fDragOffset + fCaretVisible(false), + fCaretTime(0), + fClickOffset(-1), + fClickCount(0), + fClickTime(0), + fDragOffset(-1), //fCursor - //fActive + fActive(false), fStylable(false), fTabWidth(28.0f), fSelectable(true), @@ -168,17 +191,17 @@ BTextView::BTextView(BRect frame, const char *name, BRect textRect, fText(NULL), fLines(NULL), fStyles(NULL), - //fTextRect + fTextRect(textRect), fSelStart(0), fSelEnd(0), - fCaretVisible(true), - //fCaretTime - //fClickOffset - //fClickCount - //fClickTime - //fDragOffset + fCaretVisible(false), + fCaretTime(0), + fClickOffset(-1), + fClickCount(0), + fClickTime(0), + fDragOffset(-1), //fCursor - //fActive + fActive(false), fStylable(false), fTabWidth(28.0f), fSelectable(true), @@ -206,30 +229,62 @@ BTextView::BTextView(BRect frame, const char *name, BRect textRect, BTextView::BTextView(BMessage *archive) : BView(archive) { - archive->AddString("_text", Text()); - archive->AddInt32("_align", fAlignment); - archive->AddFloat("_tab", fTabWidth); - archive->AddInt32("_col_sp", fColorSpace); - archive->AddRect("_trect", fTextRect); - archive->AddInt32("_max", fMaxBytes); - archive->AddInt32("_sel", fSelStart); - archive->AddInt32("_sel", fSelEnd); - //"_dis_ch" (array) B_RAW_TYPE Disallowed characters. - //"_runs" B_RAW_TYPE Flattened run array. - archive->AddBool("_stylable", fStylable); - archive->AddBool("_auto_in", fAutoindent); - archive->AddBool("_wrap", fWrap); - archive->AddBool("_nsel", !fSelectable); - archive->AddBool("_nedit", !fEditable); + const char *text; + int32 flag, flag2; + float value; + BRect rect; + bool toggle; + + if (archive->FindString("_text", &text) == B_OK) + SetText(text); + + if (archive->FindInt32("_align", &flag) == B_OK) + SetAlignment((alignment)flag); + + if (archive->FindFloat("_tab", &value) == B_OK) + SetTabWidth(value); + + if (archive->FindInt32("_col_sp", &flag) == B_OK) + SetColorSpace((color_space)flag); + + if (archive->FindRect("_trect", &rect) == B_OK) + SetTextRect(rect); + + if (archive->FindInt32("_max", &flag) == B_OK) + SetMaxBytes(value); + + if (archive->FindInt32("_sel", &flag) == B_OK && + archive->FindInt32("_sel", &flag2) == B_OK) + Select(flag, flag2); + + //"_dis_ch" (array) B_RAW_TYPE Disallowed characters. + //"_runs" B_RAW_TYPE Flattened run array. + + if (archive->FindBool("_stylable", &toggle) == B_OK) + SetStylable(toggle); + + if (archive->FindBool("_auto_in", &toggle) == B_OK) + SetAutoindent(toggle); + + if (archive->FindBool("_wrap", &toggle) == B_OK) + SetWordWrap(toggle); + + if (archive->FindBool("_nsel", &toggle) == B_OK) + MakeSelectable(!toggle); + + if (archive->FindBool("_nedit", &toggle) == B_OK) + MakeEditable(!toggle); } //------------------------------------------------------------------------------ BTextView::~BTextView() { delete fText; delete fLines; + delete fStyles; } //------------------------------------------------------------------------------ -BArchivable *BTextView::Instantiate(BMessage *archive) +BArchivable * +BTextView::Instantiate(BMessage *archive) { if (validate_instantiation(archive, "BTextView")) return new BTextView(archive); @@ -237,246 +292,442 @@ BArchivable *BTextView::Instantiate(BMessage *archive) return NULL; } //------------------------------------------------------------------------------ -status_t BTextView::Archive(BMessage *data, bool deep) const +status_t +BTextView::Archive(BMessage *data, bool deep) const { - // TODO: write archive - return BView::Archive(data, deep); + status_t err = BView::Archive(data, deep); + + data->AddString("_text", Text()); + data->AddInt32("_align", fAlignment); + data->AddFloat("_tab", fTabWidth); + data->AddInt32("_col_sp", fColorSpace); + data->AddRect("_trect", fTextRect); + data->AddInt32("_max", fMaxBytes); + data->AddInt32("_sel", fSelStart); + data->AddInt32("_sel", fSelEnd); + //"_dis_ch" (array) B_RAW_TYPE Disallowed characters. + //"_runs" B_RAW_TYPE Flattened run array. + data->AddBool("_stylable", fStylable); + data->AddBool("_auto_in", fAutoindent); + data->AddBool("_wrap", fWrap); + data->AddBool("_nsel", !fSelectable); + data->AddBool("_nedit", !fEditable); + + return err; } //------------------------------------------------------------------------------ -void BTextView::AttachedToWindow() +void +BTextView::AttachedToWindow() { - // TODO: set drawing mode to B_OP_COPY, adjust scrollbars, enable and set - // pulse + BView::AttachedToWindow(); + + fCaretVisible = false; + fCaretTime = 0; + fClickCount = 0; + fClickTime = 0; + fDragOffset = -1; + fActive = false; + + Refresh(0, LONG_MAX, true, false); } //------------------------------------------------------------------------------ -void BTextView::DetachedFromWindow() +void +BTextView::DetachedFromWindow() { //TODO: if cursor in bounds, reset it to B_HAND_CURSOR } //------------------------------------------------------------------------------ -void BTextView::Draw(BRect updateRect) +void +BTextView::Draw(BRect updateRect) { - int32 from = 0, to = 0; + // what lines need to be drawn? + long startLine = LineAt(BPoint(0.0f, updateRect.top)); + long endLine = LineAt(BPoint(0.0f, updateRect.bottom)); - while (fLines->fObjectList[from].fHeight + fLines->fObjectList[from].fLineHeight - < updateRect.top && from < CountLines()) - from++; - - while (to < fLines->fItemCount - 2 && - fLines->fObjectList[to].fHeight + fLines->fObjectList[to].fLineHeight - < updateRect.bottom && to < CountLines()) - to++; - - if (from > 0) - from--; - - const BFont *font; - const rgb_color *color; - - fStyles->GetNullStyle(&font, &color); - - SetFont(font); - SetHighColor(*color); - - DrawLines(from, to, 0, false); - - if (IsFocus()) - { - if (fSelStart == fSelEnd) - { - /*float caretHeight; - BPoint caret = PointAt(fSelStart, &caretHeight); - StrokeLine(BPoint(caret.x, caret.y), - BPoint(caret.x, caret.y + caretHeight));*/ + DrawLines(startLine, endLine); + + // draw the caret/hilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { if (fCaretVisible) DrawCaret(fSelStart); } - else - Highlight(fSelStart, fSelEnd); } } //------------------------------------------------------------------------------ -void BTextView::MouseDown(BPoint where) +void +BTextView::MouseDown(BPoint where) { - MakeFocus(); - SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); - - fClickOffset = OffsetAt(where); - fTrackingMouse = new _BTextTrackState_(fSelStart < fClickOffset && - fClickOffset < fSelEnd); - - if (!fTrackingMouse->fInSelection) - Select (fClickOffset, fClickOffset); - - SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS); -} -//------------------------------------------------------------------------------ -void BTextView::MouseUp(BPoint where) -{ - if (fTrackingMouse) - { - if (!fTrackingMouse->fMoved) - Select(fClickOffset, fClickOffset); + // should we even bother? + if ((!fEditable) && (!fSelectable)) + return; - delete fTrackingMouse; - fTrackingMouse = NULL; + if (!IsFocus()) { + MakeFocus(); + return; } -} -//------------------------------------------------------------------------------ -void BTextView::MouseMoved(BPoint where, uint32 code, const BMessage *message) -{ - // TODO: cursor changing, and message tracking - switch (code) - { - case B_EXITED_VIEW: - SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); - break; - default: - SetViewCursor(B_CURSOR_I_BEAM); - break; - } - - if (message == NULL) - { - if (fTrackingMouse) - { - if (fTrackingMouse->fInSelection) - { - BMessage msg(B_SIMPLE_DATA); + + // hide the caret if it's visible + if (fCaretVisible) + InvertCaret(); + + int32 mouseOffset = OffsetAt(where); + bool shiftDown = modifiers() & B_SHIFT_KEY; - msg.AddData("text/plain", B_MIME_TYPE, fText->Text() + fSelStart, - fSelEnd - fSelStart); - - delete fTrackingMouse; - fTrackingMouse = NULL; - - BBitmap *bitmap = NULL; - BPoint point; - BHandler *handler = this; - - GetDragParameters(&msg, &bitmap, &point, &handler); - - DragMessage(&msg, bitmap, point, handler); - } - else - { - int32 offset = OffsetAt(where); - - if (offset < fClickOffset) - Select(offset, fClickOffset); - else - Select(fClickOffset, offset); - - fTrackingMouse->fMoved = true; + // should we initiate a drag? + if ((fSelStart != fSelEnd) && (!shiftDown)) { + BPoint loc; + ulong buttons; + GetMouse(&loc, &buttons); + // was the secondary button clicked? + if (buttons == B_SECONDARY_MOUSE_BUTTON) { + // was the click within the selection range? + if ((mouseOffset >= fSelStart) && (mouseOffset <= fSelEnd)) { + InitiateDrag(); + return; } } } - else - { - int32 offset = OffsetAt(where); - - Select(offset, offset); + + // get the system-wide click speed + bigtime_t clickSpeed = 0; + get_click_speed(&clickSpeed); + + // is this a double/triple click, or is it a new click? + if ( (clickSpeed > (system_time() - fClickTime)) && + (mouseOffset == fClickOffset) ) { + if (fClickCount > 1) { + // triple click + fClickCount = 0; + fClickTime = 0; + } + else { + // double click + fClickCount = 2; + fClickTime = system_time(); + } + } + else { + // new click + fClickOffset = mouseOffset; + fClickCount = 1; + fClickTime = system_time(); + + if (!shiftDown) { + Select(mouseOffset, mouseOffset); + if (fEditable) + InvertCaret(); + } + } + + // no need to track the mouse if we can't select + if (!fSelectable) + return; + + // track the mouse while it's down + long start = 0; + long end = 0; + long anchor = (mouseOffset > fSelStart) ? fSelStart : fSelEnd; + BPoint curMouse = where; + ulong buttons = 0; + do { + if (mouseOffset > anchor) { + start = anchor; + end = mouseOffset; + } + else { + start = mouseOffset; + end = anchor; + } + + switch (fClickCount) { + case 0: + // triple click, select line by line + start = (*fLines)[LineAt(start)]->offset; + end = (*fLines)[LineAt(end) + 1]->offset; + break; + + case 2: + { + // double click, select word by word + FindWord(mouseOffset, &start, &end); + break; + } + + default: + // new click, select char by char + break; + } + if (shiftDown) { + if (mouseOffset > anchor) + start = anchor; + else + end = anchor; + } + Select(start, end); + + // Should we scroll the view? + if (!Bounds().Contains(curMouse)) { + float hDelta = 0; + float vDelta = 0; + if (ScrollBar(B_HORIZONTAL) != NULL) { + if (curMouse.x < Bounds().left) + hDelta = curMouse.x - Bounds().left; + else { + if (curMouse.x > Bounds().right) + hDelta = curMouse.x - Bounds().right; + } + if (hDelta != 0) + ScrollBar(B_HORIZONTAL)->SetValue(ScrollBar(B_HORIZONTAL)->Value() + hDelta); + } + if (ScrollBar(B_VERTICAL) != NULL) { + if (curMouse.y < Bounds().top) + vDelta = curMouse.y - Bounds().top; + else if (curMouse.y > Bounds().bottom) + vDelta = curMouse.y - Bounds().bottom; + if (vDelta != 0) + ScrollBar(B_VERTICAL)->SetValue(ScrollBar(B_VERTICAL)->Value() + vDelta); + } + if ((hDelta != 0) || (vDelta != 0)) + Window()->UpdateIfNeeded(); + } + + // Zzzz... + snooze(30000); + + GetMouse(&curMouse, &buttons); + mouseOffset = OffsetAt(curMouse); + } while (buttons != 0); +} +//------------------------------------------------------------------------------ +void +BTextView::MouseUp(BPoint where) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::MouseMoved(BPoint where, uint32 code, const BMessage *message) +{ + switch (code) { + case B_ENTERED_VIEW: + if ((fActive) && (message == NULL)) + be_app->SetCursor(B_I_BEAM_CURSOR); + break; + + case B_INSIDE_VIEW: + if (message != NULL) { + if (AcceptsDrop(message)) + DragCaret(OffsetAt(where)); + } + break; + + case B_EXITED_VIEW: + DragCaret(-1); + if (fActive) + be_app->SetCursor(B_HAND_CURSOR); + break; + + default: + BView::MouseMoved(where, code, message); + break; } } //------------------------------------------------------------------------------ -void BTextView::WindowActivated(bool state) +void +BTextView::WindowActivated(bool state) { - if (!state) - fCaretVisible = false; + BView::WindowActivated(state); - Invalidate(); - // TODO: handle highlighting when state changes + if (state && IsFocus()) { + if (!fActive) + Activate(); + } + else { + if (fActive) + Deactivate(); + } } //------------------------------------------------------------------------------ -void BTextView::KeyDown(const char *bytes, int32 numBytes) +void +BTextView::KeyDown(const char *bytes, int32 numBytes) { - char key = bytes[0]; + if (!fEditable) + return; + + // hide the cursor and caret + be_app->ObscureCursor(); + if (fCaretVisible) + InvertCaret(); - switch (key) - { + switch (bytes[0]) { case B_BACKSPACE: HandleBackspace(); break; + case B_LEFT_ARROW: case B_RIGHT_ARROW: - HandleArrowKey(key); + case B_UP_ARROW: + case B_DOWN_ARROW: + HandleArrowKey(bytes[0]); break; + case B_DELETE: HandleDelete(); break; + + case B_HOME: + case B_END: case B_PAGE_UP: case B_PAGE_DOWN: - HandlePageKey(key); + HandlePageKey(bytes[0]); break; + + case B_ESCAPE: + case B_INSERT: + case B_FUNCTION_KEY: + // ignore, pass it up to superclass + BView::KeyDown(bytes, numBytes); + break; + default: HandleAlphaKey(bytes, numBytes); + break; } -} -//------------------------------------------------------------------------------ -void BTextView::Pulse() -{ - // TODO: blink caret - if (Window()->IsActive() && IsFocus()) - { - fCaretVisible = !fCaretVisible; - - if (fSelStart == fSelEnd) + + // draw the caret + if (fSelStart == fSelEnd) { + if (!fCaretVisible) InvertCaret(); } } //------------------------------------------------------------------------------ -void BTextView::FrameResized(float width, float height) +void +BTextView::Pulse() { - // TODO: update scrollbars + if ((fActive) && (fEditable) && (fSelStart == fSelEnd)) { + if (system_time() > (fCaretTime + 500000.0)) + InvertCaret(); + } } //------------------------------------------------------------------------------ -void BTextView::MakeFocus(bool focusState) +void +BTextView::FrameResized(float width, float height) { - // TODO: change highlight according to focusState - BView::MakeFocus(focusState); + BView::FrameResized(width, height); - Invalidate(); + UpdateScrollbars(); } //------------------------------------------------------------------------------ -void BTextView::MessageReceived(BMessage *message) +void +BTextView::MakeFocus(bool focusState) { - // TODO: Handle scripting - switch (message->what) - { + BView::MakeFocus(focusState); + + if (focusState && Window()->IsActive()) { + if (!fActive) + Activate(); + } + else { + if (fActive) + Deactivate(); + } +} +//------------------------------------------------------------------------------ +void +BTextView::MessageReceived(BMessage *message) +{ + // was this message dropped? + if (message->WasDropped()) { + BPoint dropLoc; + BPoint offset; + + dropLoc = message->DropPoint(&offset); + ConvertFromScreen(&dropLoc); + ConvertFromScreen(&offset); + if (!MessageDropped(message, dropLoc, offset)) + BView::MessageReceived(message); + + return; + } + + switch (message->what) { case B_CUT: Cut(be_clipboard); break; + case B_COPY: Copy(be_clipboard); break; + case B_PASTE: Paste(be_clipboard); break; - case B_SELECT_ALL: - SelectAll (); - break; - case B_MIME_DATA: + + case B_SET_PROPERTY: + case B_GET_PROPERTY: + case B_COUNT_PROPERTIES: { - if (!AcceptsDrop(message)) - break; + BPropertyInfo propInfo(prop_list); + BMessage specifier; + const char *property; - const char *text; - ssize_t len; + if (message->GetCurrentSpecifier(NULL, &specifier) != B_OK || + specifier.FindString("property", &property) != B_OK) + return; - if (message->FindData("text/plain", B_MIME_TYPE, (const void**)&text, &len) == B_OK) + if (propInfo.FindMatch(message, 0, &specifier, specifier.what, + property) == B_ERROR) { - InsertText(text, len, fSelStart, NULL); - Select(fSelStart, fSelStart + len); + BView::MessageReceived(message); + break; + } + + switch(message->what) + { + case B_GET_PROPERTY: + { + BMessage reply; + + GetProperty(&specifier, specifier.what, property, &reply); + + message->SendReply(&reply); + + break; + } + case B_SET_PROPERTY: + { + BMessage reply; + + SetProperty(&specifier, specifier.what, property, &reply); + + message->SendReply(&reply); + + break; + } + case B_COUNT_PROPERTIES: + { + BMessage reply; + + CountProperties(&specifier, specifier.what, property, &reply); + + message->SendReply(&reply); + + break; + } } break; } default: BView::MessageReceived(message); + break; } } //------------------------------------------------------------------------------ -BHandler *BTextView::ResolveSpecifier(BMessage *message, int32 index, +BHandler * +BTextView::ResolveSpecifier(BMessage *message, int32 index, BMessage *specifier, int32 form, const char *property) { @@ -506,7 +757,8 @@ BHandler *BTextView::ResolveSpecifier(BMessage *message, int32 index, return target; } //------------------------------------------------------------------------------ -status_t BTextView::GetSupportedSuites(BMessage *data) +status_t +BTextView::GetSupportedSuites(BMessage *data) { // TODO: check if the suite name is ok, according to the documentation it // is unnamed @@ -529,106 +781,265 @@ status_t BTextView::GetSupportedSuites(BMessage *data) return BView::GetSupportedSuites(data); } //------------------------------------------------------------------------------ -status_t BTextView::Perform(perform_code d, void *arg) +status_t +BTextView::Perform(perform_code d, void *arg) { return B_ERROR; } //------------------------------------------------------------------------------ -void BTextView::SetText(const char *inText, const text_run_array *inRuns) +void +BTextView::SetText(const char *inText, const text_run_array *inRuns) { - DeleteText(0, fText->fLogicalBytes); + // hide the caret/unhilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // remove data from buffer + if (fText->Length() > 0) + DeleteText(0, fText->Length()); // TODO: was fText->Length() - 1 - if (inText) - InsertText(inText, strlen(inText), 0, NULL); + int len = (inText) ? strlen(inText) : 0; + + if (inText != NULL && len > 0) + InsertText(inText, len, 0, inRuns); + + fSelStart = fSelEnd = 0; + + // recalc line breaks and draw the text + Refresh(0, len, true, true); + + // draw the caret + if (fActive) { + if (!fCaretVisible) + InvertCaret(); + } } //------------------------------------------------------------------------------ -void BTextView::SetText(const char *inText, int32 inLength, +void +BTextView::SetText(const char *inText, int32 inLength, const text_run_array *inRuns) { - DeleteText(0, fText->fLogicalBytes); + // hide the caret/unhilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // remove data from buffer + if (fText->Length() > 0) + DeleteText(0, fText->Length()); // TODO: was fText->Length() - 1 + + if (inText != NULL && inLength > 0) + InsertText(inText, inLength, 0, inRuns); + + fSelStart = fSelEnd = 0; - if (inText) - InsertText(inText, inLength, 0, NULL); + // recalc line breaks and draw the text + Refresh(0, inLength, true, true); + + // draw the caret + if (fActive) { + if (!fCaretVisible) + InvertCaret(); + } } //------------------------------------------------------------------------------ -void BTextView::SetText (BFile *inFile, int32 startOffset, int32 inLength, +void +BTextView::SetText (BFile *inFile, int32 startOffset, int32 inLength, const text_run_array *inRuns) { // TODO: } //------------------------------------------------------------------------------ -void BTextView::Insert(const char *inText, const text_run_array *inRuns) +void +BTextView::Insert(const char *inText, const text_run_array *inRuns) { Insert(fSelStart, inText, strlen(inText), inRuns); } //------------------------------------------------------------------------------ -void BTextView::Insert(const char *inText, int32 inLength, +void +BTextView::Insert(const char *inText, int32 inLength, const text_run_array *inRuns) { Insert(fSelStart, inText, inLength, inRuns); } //------------------------------------------------------------------------------ -void BTextView::Insert(int32 startOffset, const char *inText, int32 inLength, +void +BTextView::Insert(int32 startOffset, const char *inText, int32 inLength, const text_run_array *inRuns) { - InsertText(inText, inLength, startOffset, inRuns); + // do we really need to do anything? + if (inLength < 1) + return; + + // hide the caret/unhilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // copy data into buffer + InsertText(inText, inLength, fSelStart, inRuns); + + // offset the caret/selection + long saveStart = fSelStart; + fSelStart += inLength; + fSelEnd += inLength; + + // recalc line breaks and draw the text + Refresh(saveStart, fSelStart, true, true); + + // draw the caret/hilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (!fCaretVisible) + InvertCaret(); + } + } } //------------------------------------------------------------------------------ -void BTextView::Delete() +void +BTextView::Delete() { - if (fSelStart != fSelEnd) - Delete(fSelStart, fSelEnd); + // anything to delete? + if (fSelStart == fSelEnd) + return; + + // hide the caret/unhilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // remove data from buffer + DeleteText(fSelStart, fSelEnd); + + // collapse the selection + fSelEnd = fSelStart; + + // recalc line breaks and draw what's left + Refresh(fSelStart, fSelEnd, true, true); + + // draw the caret + if (fActive) { + if (!fCaretVisible) + InvertCaret(); + } } //------------------------------------------------------------------------------ -void BTextView::Delete(int32 startOffset, int32 endOffset) +void +BTextView::Delete(int32 startOffset, int32 endOffset) { - if (startOffset != endOffset) - DeleteText(startOffset, endOffset); + // anything to delete? + if (startOffset == endOffset) + return; + + // hide the caret/unhilite the selection + if (fActive) { + if (startOffset != endOffset) + Highlight(startOffset, endOffset); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // remove data from buffer + DeleteText(startOffset, endOffset); + + // recalc line breaks and draw what's left + Refresh(startOffset, endOffset, true, true); + + // draw the caret + if (fActive) { + if (!fCaretVisible) + InvertCaret(); + } } //------------------------------------------------------------------------------ -const char *BTextView::Text() const +const char * +BTextView::Text() const { return fText->Text(); } //------------------------------------------------------------------------------ -int32 BTextView::TextLength() const +int32 +BTextView::TextLength() const { - return fText->fLogicalBytes; + return fText->Length(); } //------------------------------------------------------------------------------ -void BTextView::GetText(int32 offset, int32 length, char *buffer) const +void +BTextView::GetText(int32 offset, int32 length, char *buffer) const { - memcpy(buffer, fText->RealText() + offset, length); + int32 textLen = fText->Length(); + if ((offset < 0) || (offset > (textLen - 1))) { + buffer[0] = '\0'; + return; + } + + length = ((offset + length) > textLen) ? textLen - offset : length; + for (int32 i = 0; i < length; i++) + buffer[i] = (*fText)[i + offset]; buffer[length] = '\0'; } //------------------------------------------------------------------------------ -uchar BTextView::ByteAt(int32 offset) const +uchar +BTextView::ByteAt(int32 offset) const { - return fText->RealCharAt(offset); + if ((offset < 0) || (offset > (fText->Length() - 1))) + return ('\0'); + + return (*fText)[offset]; } //------------------------------------------------------------------------------ -int32 BTextView::CountLines() const +int32 +BTextView::CountLines() const { - return fLines->fItemCount - 1; + return fLines->NumLines(); } //------------------------------------------------------------------------------ -int32 BTextView::CurrentLine() const +int32 +BTextView::CurrentLine() const { return LineAt(fSelStart); } //------------------------------------------------------------------------------ -void BTextView::GoToLine(int32 index) +void +BTextView::GoToLine(int32 index) { fSelStart = fSelEnd = OffsetAt(index); } //------------------------------------------------------------------------------ -void BTextView::Cut(BClipboard *clipboard) +void +BTextView::Cut(BClipboard *clipboard) { Copy(clipboard); DeleteText(fSelStart, fSelEnd); } //------------------------------------------------------------------------------ -void BTextView::Copy(BClipboard *clipboard) +void +BTextView::Copy(BClipboard *clipboard) { BMessage *clip = NULL; @@ -636,9 +1047,7 @@ void BTextView::Copy(BClipboard *clipboard) { clipboard->Clear(); - clip = clipboard->Data(); - - if (clip != NULL) + if ((clip = clipboard->Data()) != NULL) { clip->AddData("text/plain", B_MIME_TYPE, Text() + fSelStart, fSelEnd - fSelStart); @@ -649,403 +1058,839 @@ void BTextView::Copy(BClipboard *clipboard) clipboard->Unlock(); } //------------------------------------------------------------------------------ -void BTextView::Paste(BClipboard *clipboard) +void +BTextView::Paste(BClipboard *clipboard) { BMessage *clip = NULL; if (clipboard->Lock()) - { - clip = clipboard->Data(); - if (clip != NULL) + { + if ((clip = clipboard->Data()) != NULL) { const char *text; ssize_t len; - clip->FindData("text/plain", B_MIME_TYPE, (const void **)&text, &len); - - DeleteText(fSelStart, fSelEnd); - InsertText(text, len, fSelStart, NULL); + if (clip->FindData("text/plain", B_MIME_TYPE, + (const void **)&text, &len) == B_OK) + { + DeleteText(fSelStart, fSelEnd); + InsertText(text, len, fSelStart, NULL); + } } clipboard->Unlock(); } } //------------------------------------------------------------------------------ -void BTextView::Clear() +void +BTextView::Clear() { - if (fSelStart != fSelEnd) - DeleteText(fSelStart, fSelEnd); + Delete(); } //------------------------------------------------------------------------------ -bool BTextView::AcceptsPaste(BClipboard *clipboard) +bool +BTextView::AcceptsPaste(BClipboard *clipboard) { - // TODO: check if mime on clipboard is text/plain - return (fEditable); + if (!fEditable) + return false; + + bool result = false; + + be_clipboard->Lock(); + + //result = (be_clipboard->CountEntries(B_ASCII_TYPE) > 0); + result = (be_clipboard->Data()->GetInfo((type_code)B_ASCII_TYPE, 0, NULL, + NULL) == B_OK); + + be_clipboard->Unlock(); + + return (result); } //------------------------------------------------------------------------------ -bool BTextView::AcceptsDrop(const BMessage *inMessage) +bool +BTextView::AcceptsDrop(const BMessage *inMessage) { - const void *data; + if (inMessage->HasData("text", B_ASCII_TYPE) || + inMessage->HasData("char", B_INT32_TYPE)) + return true; + + return false; +} +//------------------------------------------------------------------------------ +void +BTextView::Select(int32 startOffset, int32 endOffset) +{ + // a negative selection? + if (startOffset > endOffset) + return; + + // is the new selection any different from the current selection? + if ((startOffset == fSelStart) && (endOffset == fSelEnd)) + return; + + fStyles->InvalidateNullStyle(); + + // pin offsets at reasonable values + startOffset = (startOffset < 0) ? 0 : startOffset; + endOffset = (endOffset < 0) ? 0 : endOffset; + endOffset = (endOffset > fText->Length()) ? fText->Length() : endOffset; - return (fEditable && - inMessage->FindData("text/plain", B_MIME_TYPE, &data, NULL) == B_OK); + // hide the caret + if (fCaretVisible) + InvertCaret(); + + if (startOffset == endOffset) { + if (fSelStart != fSelEnd) { + // unhilite the selection + if (fActive) + Highlight(fSelStart, fSelEnd); + } + fSelStart = fSelEnd = startOffset; + } + else { + if (fActive) { + // draw only those ranges that are different + long start, end; + if (startOffset != fSelStart) { + // start of selection has changed + if (startOffset > fSelStart) { + start = fSelStart; + end = startOffset; + } + else { + start = startOffset; + end = fSelStart; + } + Highlight(start, end); + } + + if (endOffset != fSelEnd) { + // end of selection has changed + if (endOffset > fSelEnd) { + start = fSelEnd; + end = endOffset; + } + else { + start = endOffset; + end = fSelEnd; + } + Highlight(start, end); + } + } + fSelStart = startOffset; + fSelEnd = endOffset; + } } //------------------------------------------------------------------------------ -void BTextView::Select(int32 startOffset, int32 endOffset) +void +BTextView::SelectAll() { - fSelStart = min(max((int32)0, startOffset), fText->fLogicalBytes); - fSelEnd = min(max(fSelStart, endOffset), fText->fLogicalBytes); - - Invalidate(); + Select(0, fText->Length()); } //------------------------------------------------------------------------------ -void BTextView::SelectAll() -{ - Select(0, fText->fLogicalBytes); -} -//------------------------------------------------------------------------------ -void BTextView::GetSelection(int32 *outStart, int32 *outEnd) const +void +BTextView::GetSelection(int32 *outStart, int32 *outEnd) const { *outStart = fSelStart; *outEnd = fSelEnd; } //------------------------------------------------------------------------------ -void BTextView::SetFontAndColor(const BFont *inFont, uint32 inMode, +void +BTextView::SetFontAndColor(const BFont *inFont, uint32 inMode, const rgb_color *inColor) { - fStyles->SetNullStyle(inMode, inFont, inColor, 0); + // hide the caret/unhilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // add the style to the style buffer + fStyles->SetStyleRange(fSelStart, fSelEnd, fText->Length(), + inMode, inFont, inColor); + + if ((inMode & doFont) || (inMode & doSize)) + // recalc the line breaks and redraw with new style + Refresh(fSelStart, fSelEnd, fSelStart != fSelEnd, false); + else + // the line breaks wont change, simply redraw + DrawLines(LineAt(fSelStart), LineAt(fSelEnd), fSelStart, true); + + // draw the caret/hilite the selection + if (fActive) { + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (!fCaretVisible) + InvertCaret(); + } + } } //------------------------------------------------------------------------------ -void BTextView::SetFontAndColor(int32 startOffset, int32 endOffset, +void +BTextView::SetFontAndColor(int32 startOffset, int32 endOffset, const BFont *inFont, uint32 inMode, const rgb_color *inColor) { - fStyles->SetNullStyle(inMode, inFont, inColor, 0); + // hide the caret/unhilite the selection + if (fActive) { + if (startOffset != endOffset) + Highlight(startOffset, endOffset); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + // add the style to the style buffer + fStyles->SetStyleRange(startOffset, endOffset, fText->Length(), + inMode, inFont, inColor); + + if ((inMode & doFont) || (inMode & doSize)) + // recalc the line breaks and redraw with new style + Refresh(startOffset, endOffset, startOffset != endOffset, false); + else + // the line breaks wont change, simply redraw + DrawLines(LineAt(startOffset), LineAt(endOffset), startOffset, true); + + // draw the caret/hilite the selection + if (fActive) { + if (startOffset != endOffset) + Highlight(startOffset, endOffset); + else { + if (!fCaretVisible) + InvertCaret(); + } + } } //------------------------------------------------------------------------------ -void BTextView::GetFontAndColor(int32 inOffset, BFont*outFont, +void +BTextView::GetFontAndColor(int32 inOffset, BFont*outFont, rgb_color *outColor) const { - const BFont *font; - const rgb_color *color; - - fStyles->GetNullStyle(&font, &color); - - if (outFont) - *outFont = *font; - - if (outColor) - *outColor = *color; + fStyles->GetStyle(inOffset, outFont, outColor); } //------------------------------------------------------------------------------ -void BTextView::GetFontAndColor(BFont *outFont, uint32 *outMode, +void +BTextView::GetFontAndColor(BFont *outFont, uint32 *outMode, rgb_color *outColor, bool *outEqColor) const { - const BFont *font; - const rgb_color *color; - - fStyles->GetNullStyle(&font, &color); - - if (outFont) - *outFont = *font; - - if (outColor) - *outColor = *color; + // TODO fill in outMode and outEqColor + fStyles->GetStyle(fSelStart, outFont, outColor); } //------------------------------------------------------------------------------ -void BTextView::SetRunArray(int32 startOffset, int32 endOffset, +void +BTextView::SetRunArray(int32 startOffset, int32 endOffset, const text_run_array *inRuns) { + // pin offsets at reasonable values + startOffset = (startOffset < 0) ? 0 : startOffset; + endOffset = (endOffset < 0) ? 0 : endOffset; + endOffset = (endOffset > fText->Length()) ? fText->Length() : endOffset; + + long numStyles = inRuns->count; + if (numStyles > 0) { + long textLength = fText->Length(); + const text_run *theRun = &inRuns->runs[0]; + for (long index = 0; index < numStyles; index++) { + long fromOffset = theRun->offset + startOffset; + long toOffset = endOffset; + if ((index + 1) < numStyles) { + toOffset = (theRun + 1)->offset + startOffset; + toOffset = (toOffset > endOffset) ? endOffset : toOffset; + } + + fStyles->SetStyleRange(fromOffset, toOffset, textLength, + doAll, &theRun->font, &theRun->color); + + theRun++; + } + fStyles->InvalidateNullStyle(); + + Refresh(startOffset, endOffset, true, false); + } } //------------------------------------------------------------------------------ -text_run_array *BTextView::RunArray(int32 startOffset, int32 endOffset, +text_run_array * +BTextView::RunArray(int32 startOffset, int32 endOffset, int32 *outSize) const { -/* text_run_array *textRunArray = new text_run_array; + STEStyleRangePtr result = fStyles->GetStyleRange(startOffset, endOffset - 1); - textRunArray->count = 1; - textRunArray->runs[0].offset = 0; - textRunArray->runs[0].font = NULL; - //textRunArray->runs[0].color; + if (result == NULL) + return NULL; - return textRunArray;*/ + text_run_array *res = (text_run_array*)malloc(sizeof(int32) + + (sizeof(text_run) * result->count)); - return NULL; + res->count = result->count; + + for (int32 i = 0; i < res->count; i++) + { + res->runs[i].offset = result->runs[i].offset; + res->runs[i].font = &result->runs[i].style.font; + res->runs[i].color = result->runs[i].style.color; + } + + if (outSize != NULL) + *outSize = sizeof(int32) + (sizeof(text_run) * res->count); + + return res; } //------------------------------------------------------------------------------ -int32 BTextView::LineAt(int32 offset) const +int32 +BTextView::LineAt(int32 offset) const { return fLines->OffsetToLine(offset); } //------------------------------------------------------------------------------ -int32 BTextView::LineAt(BPoint point) const +int32 +BTextView::LineAt(BPoint point) const { return fLines->PixelToLine(point.y - fTextRect.top); } //------------------------------------------------------------------------------ -BPoint BTextView::PointAt(int32 inOffset, float *outHeight) const +BPoint +BTextView::PointAt(int32 inOffset, float *outHeight) const { - int32 line = LineAt(inOffset); + BPoint result; + int32 textLength = fText->Length(); + int32 lineNum = LineAt(inOffset); + STELinePtr line = (*fLines)[lineNum]; + float height = (line + 1)->origin - line->origin; + + result.x = 0.0; + result.y = line->origin + fTextRect.top; + + // special case: go down one line if inOffset is a newline + if ((inOffset == textLength) && ((*fText)[textLength - 1] == '\n')) { + float ascent, descent; + StyledWidth(inOffset, 1, &ascent, &descent); + + result.y += height; + height = ascent + descent; + } + else { + int32 offset = line->offset; + int32 length = inOffset - line->offset; + int32 numChars = length; + bool foundTab = false; + do { + foundTab = fText->FindChar('\t', offset, &numChars); + + result.x += StyledWidth(offset, numChars); + + if (foundTab) { + result.x += fTabWidth - fmod(result.x, fTabWidth); + numChars++; + } + + offset += numChars; + length -= numChars; + numChars = length; + } while ((foundTab) && (length > 0)); + } - if (outHeight) - *outHeight = LineHeight(line); + // convert from text rect coordinates + result.x += fTextRect.left - 1.0; - return BPoint(fTextRect.left + - StringWidth(fText->RealText() + fLines->fObjectList[line].fOffset, - inOffset - fLines->fObjectList[line].fOffset), - fTextRect.top + fLines->fObjectList[line].fHeight); + // round up + result.x = ceil(result.x); + result.y = ceil(result.y); + if (outHeight != NULL) + *outHeight = height; + + return (result); } //------------------------------------------------------------------------------ -int32 BTextView::OffsetAt(BPoint point) const +int32 +BTextView::OffsetAt(BPoint point) const { - // Find the line we clicked on - int32 line = LineAt(point); - int32 offset = fLines->fObjectList[line].fOffset; + // should we even bother? + if (point.y >= fTextRect.bottom) + return (fText->Length()); + else { + if (point.y < fTextRect.top) + return (0); + } + int32 lineNum = LineAt(point); + STELinePtr line = (*fLines)[lineNum]; + + // special case: if point is within the text rect and PixelToLine() + // tells us that it's on the last line, but if point is actually + // lower than the bottom of the last line, return the last offset + // (can happen for newlines) + if (lineNum == (fLines->NumLines() - 1)) { + if (point.y >= ((line + 1)->origin + fTextRect.top)) + return (fText->Length()); + } + + // convert to text rect coordinates point.x -= fTextRect.left; + point.x = (point.x < 0.0) ? 0.0 : point.x; + + // do a pseudo-binary search of the character widths on the line + // that PixelToLine() gave us + // note: the right half of a character returns its offset + 1 + int32 offset = line->offset; + int32 saveOffset = offset; + int32 delta = 0; + int32 limit = (line + 1)->offset; + int32 length = limit - line->offset; + float sigmaWidth = 0.0; + float tabWidth = 0.0; + int32 numChars = length; + bool done = false; + bool foundTab = false; + do { + saveOffset = offset; + + if (foundTab) { + // is the point in the right-half of the tab? + if ((point.x >= (sigmaWidth - (tabWidth / 2))) && + (point.x < sigmaWidth)) + break; + } + + // any more tabs? + foundTab = fText->FindChar('\t', offset, &numChars); + + delta = numChars / 2; + delta = (delta < 1) ? 1 : delta; + + if (numChars > 1) { + do { + float deltaWidth = StyledWidth(offset, delta); + float leftWidth = StyledWidth(offset + delta - 1, 1); + sigmaWidth += deltaWidth; + + if (point.x >= (sigmaWidth - (leftWidth / 2))) { + // we're to the left of the point + float rightWidth = StyledWidth(offset + delta, 1); + if (point.x < (sigmaWidth + (rightWidth / 2))) { + // the next character is to the right, we're done! + offset += delta; + done = true; + break; + } + else { + // still too far to the left, measure some more + offset += delta; + delta /= 2; + delta = (delta < 1) ? 1 : delta; + } + } + else { + // oops, we overshot the point, go back some + sigmaWidth -= deltaWidth; + + if (delta == 1) { + done = true; + break; + } + + delta /= 2; + delta = (delta < 1) ? 1 : delta; + + } + } while (offset < (numChars + saveOffset)); + } + + if (done || (offset >= limit)) + break; + + if (foundTab) { + tabWidth = fTabWidth - fmod(sigmaWidth, fTabWidth); + + // is the point in the left-half of the tab? + if (point.x < (sigmaWidth + (tabWidth / 2))) + break; + + sigmaWidth += tabWidth; + numChars++; + } - // If we clicked past the end of the line return last index - if (point.x > fLines->fObjectList[line].fWidth) - { - if (fText->RealCharAt(fLines->fObjectList[line + 1].fOffset - 1) == '\n') - return fLines->fObjectList[line + 1].fOffset - 1; - else - return fLines->fObjectList[line + 1].fOffset; + offset = saveOffset + numChars; + length -= numChars; + numChars = length; + } while ((foundTab) && (length > 0)); + + if (offset == (line + 1)->offset) { + // special case: newlines aren't visible + // return the offset of the character preceding the newline + if ((*fText)[offset - 1] == '\n') + return (--offset); + + // special case: return the offset preceding any spaces that + // aren't at the end of the buffer + if ((offset != fText->Length()) && ((*fText)[offset - 1] == ' ')) + return (--offset); } - - // Search offset - float width = 0.0f, nextWidth; - int32 startOffset = offset, nextOffset; - - while (point.x > width && offset < fLines->fObjectList[line + 1].fOffset - 1) - { - nextOffset = next_utf8(fText->RealText() + offset) - fText->RealText(); - nextWidth = StringWidth(fText->RealText(), nextOffset - startOffset); - - if (point.x < width + (nextWidth - width) * 0.5f) - return offset; - - width = nextWidth; - offset = nextOffset; - } - - return offset; + + return (offset); } //------------------------------------------------------------------------------ -int32 BTextView::OffsetAt(int32 line) const +int32 +BTextView::OffsetAt(int32 line) const { - if (line > fLines->fItemCount) - return fText->fLogicalBytes; + if (line > fLines->NumLines()) + return fText->Length(); - return fLines->fObjectList[line].fOffset; + return (*fLines)[line]->offset; } //------------------------------------------------------------------------------ -void BTextView::FindWord(int32 inOffset, int32 *outFromOffset, +void +BTextView::FindWord(int32 inOffset, int32 *outFromOffset, int32 *outToOffset) { -} -//------------------------------------------------------------------------------ -bool BTextView::CanEndLine(int32 offset) -{ - switch(fText->RealCharAt(offset)) - { - case B_SPACE: - case B_TAB: - case B_ENTER: - case '=': - case '+': - case '-': - case '<': - case '>': - case '^': - case '/': - case '|': - case '&': - case '*': - case '\0': - return true; + long offset; + + // check to the left + for (offset = inOffset; offset > 0; offset--) { + //TODO: Should use CharClassification() instead + if (CharClassification(offset - 1) == 1) + break; } - return false; + *outFromOffset = offset; + + // check to the right + long textLen = TextLength(); + for (offset = inOffset; offset < textLen; offset++) { + if (CharClassification(offset) == 1) + break; + } + + *outToOffset = offset; +} + +//------------------------------------------------------------------------------ +bool +BTextView::CanEndLine(int32 offset) +{ + switch ((*fText)[offset]) { + case '\0': + case '\t': + case '\n': + case ' ': + case '&': + case '*': + case '+': + case '-': + case '/': + case '<': + case '=': + case '>': + case '\\': + case '^': + case '|': + return true; + + default: + return false; + } + + return true; } //------------------------------------------------------------------------------ -float BTextView::LineWidth(int32 lineNum) const +float +BTextView::LineWidth(int32 lineNum) const { if (lineNum < 0) - return fLines->fObjectList[0].fWidth; + return (*fLines)[0]->width; else if (lineNum > fLines->fItemCount - 2) - return fLines->fObjectList[fLines->fItemCount - 2].fWidth; + return (*fLines)[fLines->fItemCount - 2]->width; else - return fLines->fObjectList[lineNum].fWidth; + return (*fLines)[lineNum]->width; } //------------------------------------------------------------------------------ -float BTextView::LineHeight(int32 lineNum) const +float +BTextView::LineHeight(int32 lineNum) const { if (lineNum < 0) - return fLines->fObjectList[0].fLineHeight; + return (*fLines)[0]->ascent; else if (lineNum > fLines->fItemCount - 2) - return fLines->fObjectList[fLines->fItemCount - 2].fLineHeight; + return (*fLines)[fLines->fItemCount - 2]->ascent; else - return fLines->fObjectList[lineNum].fLineHeight; + return (*fLines)[lineNum]->ascent; } //------------------------------------------------------------------------------ -float BTextView::TextHeight(int32 startLine, int32 endLine) const +float +BTextView::TextHeight(int32 startLine, int32 endLine) const { - if (startLine < 0) - startLine = 0; - - if (endLine > fLines->fItemCount - 1) - endLine = fLines->fItemCount - 1; - - float height = 0.0f; - - for (int32 i = startLine; i < endLine + 1; i ++) - height += fLines->fObjectList[i].fLineHeight + 3.0f; - + int32 numLines = fLines->NumLines(); + startLine = (startLine < 0) ? 0 : startLine; + endLine = (endLine > numLines - 1) ? numLines - 1 : endLine; + + float height = (*fLines)[endLine + 1]->origin - + (*fLines)[startLine]->origin; + + if ((endLine == numLines - 1) && ((*fText)[fText->Length() - 1] == '\n')) + height += (*fLines)[endLine + 1]->origin - (*fLines)[endLine]->origin; + return height; } //------------------------------------------------------------------------------ -void BTextView::GetTextRegion(int32 startOffset, int32 endOffset, +void +BTextView::GetTextRegion(int32 startOffset, int32 endOffset, BRegion *outRegion) const { -} -//------------------------------------------------------------------------------ -void BTextView::ScrollToOffset(int32 inOffset) -{ - int32 line = LineAt(inOffset); - BRect rect; + outRegion->MakeEmpty(); - rect.left = 0; - rect.top = fLines->fObjectList[line].fHeight; - rect.right = LineWidth(line); - rect.bottom = rect.top + LineHeight(line); - - if (Bounds().Intersects(rect.InsetByCopy(0, 2))) + // return an empty region if the range is invalid + if (startOffset >= endOffset) return; - if (rect.top < Bounds().top ) - ScrollTo(0, rect.top); - else - ScrollTo(0, rect.bottom - Bounds().Height()); + float startLineHeight = 0.0; + float endLineHeight = 0.0; + BPoint startPt = PointAt(startOffset, &startLineHeight); + BPoint endPt = PointAt(endOffset, &endLineHeight); + BRect selRect; - UpdateScrollbars(); + if (startPt.y == endPt.y) { + // this is a one-line region + selRect.left = (startPt.x < fTextRect.left) ? fTextRect.left : startPt.x; + selRect.top = startPt.y; + selRect.right = endPt.x - 1.0; + selRect.bottom = endPt.y + endLineHeight - 1.0; + outRegion->Include(selRect); + } + else { + // more than one line in the specified offset range + selRect.left = (startPt.x < fTextRect.left) ? fTextRect.left : startPt.x; + selRect.top = startPt.y; + selRect.right = fTextRect.right; + selRect.bottom = startPt.y + startLineHeight - 1.0; + outRegion->Include(selRect); + + if ((startPt.y + startLineHeight) < endPt.y) { + // more than two lines in the range + selRect.left = fTextRect.left; + selRect.top = startPt.y + startLineHeight; + selRect.right = fTextRect.right; + selRect.bottom = endPt.y - 1.0; + outRegion->Include(selRect); + } + + selRect.left = fTextRect.left; + selRect.top = endPt.y; + selRect.right = endPt.x - 1.0; + selRect.bottom = endPt.y + endLineHeight - 1.0; + outRegion->Include(selRect); + } } //------------------------------------------------------------------------------ -void BTextView::ScrollToSelection() +void +BTextView::ScrollToOffset(int32 inOffset) +{ + BRect bounds = Bounds(); + //STELinePtr line = (*fLines)[LineAt(inOffset)]; + float lineHeight = 0.0; + BPoint point = PointAt(inOffset, &lineHeight); + + if (ScrollBar(B_HORIZONTAL) != NULL) { + if ((point.x < bounds.left) || (point.x >= bounds.right)) + ScrollBar(B_HORIZONTAL)->SetValue(point.x - (bounds.IntegerWidth() / 2)); + } + + if (ScrollBar(B_VERTICAL) != NULL) { + if ((point.y < bounds.top) || ((point.y + lineHeight) >= bounds.bottom)) + ScrollBar(B_VERTICAL)->SetValue(point.y - (bounds.IntegerHeight() / 2)); + } +} +//------------------------------------------------------------------------------ +void +BTextView::ScrollToSelection() { ScrollToOffset(fSelStart); } //------------------------------------------------------------------------------ -void BTextView::Highlight(int32 startOffset, int32 endOffset) +void +BTextView::Highlight(int32 startOffset, int32 endOffset) { - rgb_color color = HighColor(); - SetHighColor(0, 0, 0, 128); - - int32 lineStart = LineAt(startOffset); - int32 lineEnd = LineAt(endOffset); - - float selHeight; - BPoint selStart, selEnd; - - for (int32 line = lineStart; line <= lineEnd; line++) - { - if (line == lineStart) - selStart = PointAt(startOffset); - else - selStart = PointAt(OffsetAt(line)); - - if (line == lineEnd) - selEnd = PointAt(endOffset, &selHeight); - else - { - selEnd = PointAt(OffsetAt(line), &selHeight); - selEnd.x = LineWidth(line); - } - - InvertRect(BRect(selStart.x, selStart.y, selEnd.x, selEnd.y + selHeight)); - } + // get real + if (startOffset >= endOffset) + return; + + BRegion selRegion; + GetTextRegion(startOffset, endOffset, &selRegion); - SetHighColor(color); + ConstrainClippingRegion(&selRegion); + InvertRect(selRegion.Frame()); + ConstrainClippingRegion(NULL); + + Flush(); //// } //------------------------------------------------------------------------------ -void BTextView::SetTextRect(BRect rect) +void +BTextView::SetTextRect(BRect rect) { + if (rect == fTextRect) + return; + + fTextRect = rect; + + if (Window() != NULL) + Refresh(0, LONG_MAX, true, false); } //------------------------------------------------------------------------------ -BRect BTextView::TextRect() const +BRect +BTextView::TextRect() const { - return BRect(); + return fTextRect; } //------------------------------------------------------------------------------ -void BTextView::SetStylable(bool stylable) +void +BTextView::SetStylable(bool stylable) { fStylable = stylable; } //------------------------------------------------------------------------------ -bool BTextView::IsStylable() const +bool +BTextView::IsStylable() const { return fStylable; } //------------------------------------------------------------------------------ -void BTextView::SetTabWidth(float width) +void +BTextView::SetTabWidth(float width) { + if (width == fTabWidth) + return; + fTabWidth = width; + + if (Window() != NULL) + Refresh(0, LONG_MAX, true, false); } //------------------------------------------------------------------------------ -float BTextView::TabWidth() const +float +BTextView::TabWidth() const { return fTabWidth; } //------------------------------------------------------------------------------ -void BTextView::MakeSelectable(bool selectable) +void +BTextView::MakeSelectable(bool selectable) { + if (selectable == fSelectable) + return; + fSelectable = selectable; + + if (Window() != NULL) { + if (fActive) { + // show/hide the caret, hilite/unhilite the selection + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else + InvertCaret(); + } + } } //------------------------------------------------------------------------------ -bool BTextView::IsSelectable() const +bool +BTextView::IsSelectable() const { return fSelectable; } //------------------------------------------------------------------------------ -void BTextView::MakeEditable(bool editable) +void +BTextView::MakeEditable(bool editable) { + if (editable == fEditable) + return; + fEditable = editable; + + if (Window() != NULL) { + if (fActive) { + if ((!fEditable) && (fCaretVisible)) + InvertCaret(); + } + } } //------------------------------------------------------------------------------ -bool BTextView::IsEditable() const +bool +BTextView::IsEditable() const { return fEditable; } //------------------------------------------------------------------------------ -void BTextView::SetWordWrap(bool wrap) +void +BTextView::SetWordWrap(bool wrap) { + if (wrap == fWrap) + return; + fWrap = wrap; + + if (Window() != NULL) { + if (fActive) { + // hide the caret, unhilite the selection + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (fCaretVisible) + InvertCaret(); + } + } + + Refresh(0, LONG_MAX, true, false); + + if (fActive) { + // show the caret, hilite the selection + if (fSelStart != fSelEnd) + Highlight(fSelStart, fSelEnd); + else { + if (!fCaretVisible) + InvertCaret(); + } + } + } } //------------------------------------------------------------------------------ -bool BTextView::DoesWordWrap() const +bool +BTextView::DoesWordWrap() const { return fWrap; } //------------------------------------------------------------------------------ -void BTextView::SetMaxBytes(int32 max) +void +BTextView::SetMaxBytes(int32 max) { fMaxBytes = max; } //------------------------------------------------------------------------------ -int32 BTextView::MaxBytes() const +int32 +BTextView::MaxBytes() const { return fMaxBytes; } //------------------------------------------------------------------------------ -void BTextView::DisallowChar(uint32 aChar) +void +BTextView::DisallowChar(uint32 aChar) { + if (fDisallowedChars == NULL) + fDisallowedChars = new BList; + if (!fDisallowedChars->HasItem(&aChar)) + fDisallowedChars->AddItem(&aChar); } //------------------------------------------------------------------------------ -void BTextView::AllowChar(uint32 aChar) +void +BTextView::AllowChar(uint32 aChar) { + if (fDisallowedChars != NULL) + fDisallowedChars->RemoveItem(&aChar); } //------------------------------------------------------------------------------ -void BTextView::SetAlignment(alignment flag) +void +BTextView::SetAlignment(alignment flag) { fAlignment = flag; } @@ -1055,572 +1900,245 @@ alignment BTextView::Alignment() const return fAlignment; } //------------------------------------------------------------------------------ -void BTextView::SetAutoindent(bool state) +void +BTextView::SetAutoindent(bool state) { fAutoindent = state; } //------------------------------------------------------------------------------ -bool BTextView::DoesAutoindent() const +bool +BTextView::DoesAutoindent() const { return fAutoindent; } //------------------------------------------------------------------------------ -void BTextView::SetColorSpace(color_space colors) +void +BTextView::SetColorSpace(color_space colors) { fColorSpace = colors; } //------------------------------------------------------------------------------ -color_space BTextView::ColorSpace() const +color_space +BTextView::ColorSpace() const { return fColorSpace; } //------------------------------------------------------------------------------ -void BTextView::MakeResizable(bool resize, BView *resizeView) +void +BTextView::MakeResizable(bool resize, BView *resizeView) { fResizable = resize; fContainerView = resizeView; } //------------------------------------------------------------------------------ -bool BTextView::IsResizable () const +bool +BTextView::IsResizable () const { return fResizable; } //------------------------------------------------------------------------------ -void BTextView::SetDoesUndo(bool undo) +void +BTextView::SetDoesUndo(bool undo) { } //------------------------------------------------------------------------------ -bool BTextView::DoesUndo() const +bool +BTextView::DoesUndo() const { return false; } //------------------------------------------------------------------------------ -void BTextView::HideTyping(bool enabled) +void +BTextView::HideTyping(bool enabled) { } //------------------------------------------------------------------------------ -bool BTextView::IsTypingHidden() const +bool +BTextView::IsTypingHidden() const { return false; } //------------------------------------------------------------------------------ -void BTextView::ResizeToPreferred() +void +BTextView::ResizeToPreferred() { float widht, height; GetPreferredSize(&widht, &height); BView::ResizeTo(widht,height); } //------------------------------------------------------------------------------ -void BTextView::GetPreferredSize(float *width, float *height) +void +BTextView::GetPreferredSize(float *width, float *height) { } //------------------------------------------------------------------------------ -void BTextView::AllAttached() +void +BTextView::AllAttached() { } //------------------------------------------------------------------------------ -void BTextView::AllDetached() +void +BTextView::AllDetached() { } //------------------------------------------------------------------------------ -void *BTextView::FlattenRunArray(const text_run_array *inArray, - int32 *outSize) +void * +BTextView::FlattenRunArray(const text_run_array *inArray, int32 *outSize) { - // TODO: see TranslatorFormats for info - return NULL; + int32 size = sizeof(flattened_text_run_array) + (inArray->count - 1) * + sizeof(flattened_text_run_array); + + flattened_text_run_array *array = (flattened_text_run_array*)malloc(size); + + array->magic[0] = 0x41; + array->magic[1] = 0x6c; + array->magic[2] = 0x69; + array->magic[3] = 0x21; + array->version[0] = 0x00; + array->version[1] = 0x00; + array->version[2] = 0x00; + array->version[3] = 0x00; + array->count = inArray->count; + + for (int32 i = 0; i < inArray->count; i++) + { + array->styles[i].offset = inArray->runs[i].offset; + inArray->runs[i].font.GetFamilyAndStyle(&array->styles[i].family, + &array->styles[i].style); + array->styles[i].size = inArray->runs[i].font.Size(); + array->styles[i].shear = inArray->runs[i].font.Shear(); + array->styles[i].face = inArray->runs[i].font.Face(); + array->styles[i].red = inArray->runs[i].color.red; + array->styles[i].green = inArray->runs[i].color.green; + array->styles[i].blue = inArray->runs[i].color.blue; + array->styles[i].alpha = 255; + array->styles[i]._reserved_ = 0; + } + + if (outSize) + *outSize = size; + + return array; } //------------------------------------------------------------------------------ -text_run_array *BTextView::UnflattenRunArray(const void *data, int32 *outSize) +text_run_array * +BTextView::UnflattenRunArray(const void *data, int32 *outSize) { - // TODO: see TranslatorFormats for info - return NULL; + flattened_text_run_array *array = (flattened_text_run_array*)data; + + if (array->magic[0] != 0x41 || array->magic[1] != 0x6c || + array->magic[2] != 0x69 || array->magic[3] != 0x21 || + array->version[0] != 0x00 || array->version[1] != 0x00 || + array->version[2] != 0x00 || array->version[3] != 0x00) + { + if (outSize) + *outSize = 0; + + return NULL; + } + + int32 size = sizeof(text_run_array) + (array->count - 1) * sizeof(text_run); + + text_run_array *run_array = (text_run_array*)malloc(size); + + run_array->count = array->count; + + for (int32 i = 0; i < array->count; i++) + { + run_array->runs[i].font = new BFont; + run_array->runs[i].font.SetFamilyAndStyle(array->styles[i].family, + array->styles[i].style); + run_array->runs[i].font.SetSize(array->styles[i].size); + run_array->runs[i].font.SetShear(array->styles[i].shear); + run_array->runs[i].font.SetFace(array->styles[i].face); + run_array->runs[i].color.red = array->styles[i].red; + run_array->runs[i].color.green = array->styles[i].green; + run_array->runs[i].color.blue = array->styles[i].blue; + run_array->runs[i].color.alpha = array->styles[i].alpha; + } + + if (outSize) + *outSize = size; + + return run_array; } //------------------------------------------------------------------------------ -void BTextView::InsertText(const char *inText, int32 inLength, int32 inOffset, +void +BTextView::InsertText(const char *inText, int32 inLength, int32 inOffset, const text_run_array *inRuns) { //_ASSERT ( _CrtCheckMemory () ); + // why add nothing? + if (inLength < 1) + return; + + // add the text to the buffer fText->InsertText(inText, inLength, inOffset); - - int32 line = fLines->OffsetToLine(inOffset); - fLines->BumpOffset(line + 1, inLength); - - // Move selection if needed - if (inOffset <= fSelStart) - { - fSelStart += inLength; - fSelEnd += inLength; + + // update the start offsets of each line below offset + fLines->BumpOffset(inLength, LineAt(inOffset) + 1); + + // update the style runs + fStyles->BumpOffset(inLength, fStyles->OffsetToRun(inOffset - 1) + 1); + + if (inRuns != NULL) + //SetStyleRange(inOffset, inOffset + inLength, inStyles, false); + SetRunArray(inOffset, inOffset + inLength, inRuns); + else { + // apply nullStyle to inserted text + fStyles->SyncNullStyle(inOffset); + fStyles->SetStyleRange(inOffset, inOffset + inLength, + fText->Length(), doAll, NULL, NULL); } //_ASSERT ( _CrtCheckMemory () ); - - // Update line buffer - Refresh(inOffset, inOffset + inLength, true, false); - - //_ASSERT ( _CrtCheckMemory () ); } //------------------------------------------------------------------------------ -void BTextView::DeleteText(int32 fromOffset, int32 toOffset) +void +BTextView::DeleteText(int32 fromOffset, int32 toOffset) { //_ASSERT ( _CrtCheckMemory () ); + // sanity checking + if ((fromOffset >= toOffset) || (fromOffset < 0) || (toOffset < 0)) + return; + + // set nullStyle to style at beginning of range + fStyles->InvalidateNullStyle(); + fStyles->SyncNullStyle(fromOffset); + + // remove from the text buffer fText->RemoveRange(fromOffset, toOffset); + + // remove any lines that have been obliterated fLines->RemoveLineRange(fromOffset, toOffset); - - // Move selection if needed - if (fromOffset <= fSelStart) - { - if (toOffset <= fSelStart) - { - fSelStart -= toOffset - fromOffset; - fSelEnd -= toOffset - fromOffset; - } - else - { - fSelStart = fromOffset; - fSelEnd = fromOffset; - } - } - - //_ASSERT ( _CrtCheckMemory () ); - - // Update line buffer - Refresh(fromOffset, fromOffset, true, false); + + // remove any style runs that have been obliterated + fStyles->RemoveStyleRange(fromOffset, toOffset); //_ASSERT ( _CrtCheckMemory () ); } //------------------------------------------------------------------------------ -void BTextView::Undo(BClipboard *clipboard) +void +BTextView::Undo(BClipboard *clipboard) { } //------------------------------------------------------------------------------ -undo_state BTextView::UndoState(bool *isRedo) const +undo_state +BTextView::UndoState(bool *isRedo) const { return B_UNDO_UNAVAILABLE; } //------------------------------------------------------------------------------ -void BTextView::GetDragParameters(BMessage *drag, BBitmap **bitmap, +void +BTextView::GetDragParameters(BMessage *drag, BBitmap **bitmap, BPoint *point, BHandler **handler) { } //------------------------------------------------------------------------------ - -//Private or Reserved -//------------------------------------------------------------------------------ -void BTextView::InitObject(BRect textRect, const BFont *initialFont, - const rgb_color *initialColor) -{ - fTextRect = textRect; - - fText = new _BTextGapBuffer_; - fLines = new _BLineBuffer_; - fStyles = new _BStyleBuffer_(initialFont, initialColor); - - font_height fh; - GetFontHeight(&fh); - fLines->fObjectList[0].fLineHeight = (float)ceil(fh.ascent + fh.descent); -} -//------------------------------------------------------------------------------ -void BTextView::HandleBackspace() -{ - if (!fEditable) - return; - - if (fSelStart < fSelEnd) - { - DeleteText(fSelStart, fSelEnd); - } - else if ( fSelStart > 0 ) - { - int32 selStart = previous_utf8(fText->RealText(), - fText->RealText() + fSelStart) - fText->RealText(); - DeleteText(selStart, fSelStart); - } -} -//------------------------------------------------------------------------------ -void BTextView::HandleArrowKey(uint32 inArrowKey) -{ - switch(inArrowKey) - { - case B_LEFT_ARROW: - if (fSelStart > 0) - { - int32 sel = previous_utf8(fText->RealText(), - fText->RealText() + fSelStart) - fText->RealText(); - - if (modifiers() & B_SHIFT_KEY) - Select(sel, fSelEnd); - else - Select(sel, sel); - } - break; - case B_RIGHT_ARROW: - { - int32 sel; - - if (fSelEnd < fText->fLogicalBytes - 1) - sel= next_utf8(fText->RealText() + fSelEnd) - fText->RealText(); - else - sel = fText->fLogicalBytes; - - if (modifiers() & B_SHIFT_KEY) - Select(fSelStart, sel); - else - Select(sel, sel); - - break; - } - case B_DOWN_ARROW: - { - //TODO: Implement - break; - } - case B_UP_ARROW: - { - //TODO: Implement - break; - } - } -} -//------------------------------------------------------------------------------ -void BTextView::HandleDelete() -{ - if (!fEditable) - return; - - if (fSelStart < fSelEnd) - { - DeleteText(fSelStart, fSelEnd); - } - else if (fSelStart < fText->fLogicalBytes) - { - int32 selEnd = next_utf8(fText->RealText() + fSelStart) - - fText->RealText(); - DeleteText(fSelStart, selEnd); - } -} -//------------------------------------------------------------------------------ -void BTextView::HandlePageKey(uint32 inPageKey) -{ - if (inPageKey == B_PAGE_DOWN) - { - GoToLine(CountLines() - 1); - } - else if (inPageKey == B_PAGE_UP) - { - GoToLine(0); - } - Invalidate(); -} -//------------------------------------------------------------------------------ -void BTextView::HandleAlphaKey(const char *bytes, int32 numBytes) -{ - if (!fEditable) - return; - - if (fSelStart != fSelEnd) - DeleteText(fSelStart, fSelEnd); - - InsertText(bytes, numBytes, fSelStart, NULL); -} -//------------------------------------------------------------------------------ -void BTextView::Refresh(int32 fromOffset, int32 toOffset, bool erase, - bool scroll) -{ - float ascent, descent, width; - - // Find the first line that changed - int32 line = LineAt(fromOffset); - int32 offset = FindLineBreak(OffsetAt(line), &ascent, &descent, &width); - - // Update the lineheight and width of this line - fLines->fObjectList[line].fLineHeight = (float)ceil(ascent + descent); - fLines->fObjectList[line].fWidth = width; - line++; - - // Check if we have to insert new lines - while (offset < toOffset) - { - STELine l; - - l.fOffset = offset + 1; - l.fHeight = fLines->fObjectList[line - 1].fHeight + - fLines->fObjectList[line - 1].fLineHeight + 3; - l.fLineHeight = (float)ceil(ascent + descent); - l.fWidth = width; - - fLines->InsertLine(&l, line); - - offset++; - line++; - offset = FindLineBreak(offset, &ascent, &descent, &width); - } - - // Update the height of the remaining lines - for (int i = line; i < fLines->fItemCount; i++) - fLines->fObjectList[i].fHeight = fLines->fObjectList[i - 1].fHeight + - fLines->fObjectList[i - 1].fLineHeight + 3; - - // Update the screen - UpdateScrollbars(); - - /*Invalidate(BRect(0.0f, PointAt(fromOffset).y, Bounds().right, - PointAt(toOffset).y + LineHeight(LineAt(toOffset))));*/ - Invalidate(); -} -//------------------------------------------------------------------------------ -void BTextView::RecalLineBreaks(int32 *startLine, int32 *endLine) -{ -} -//------------------------------------------------------------------------------ -int32 BTextView::FindLineBreak(int32 fromOffset, float *outAscent, - float *outDescent, float *ioWidth) -{ - if (fromOffset >= fText->fLogicalBytes) - { - // Fill in font height if needed - if (outAscent && outDescent) - { - *outAscent = 0.0f; - *outDescent = 0.0f; - } - - return fromOffset; - } - - // Find the actual linebreak - int32 offset = fromOffset; - char *ptr = fText->RealText(); - - while(offset < fText->fLogicalBytes) - { - if (ptr[offset] == '\n' && ptr[offset + 1] != '\0') - break; - - if (ptr[offset] == '\0') - break; - - offset++; - } - - // Fill in font height if needed - if (outAscent && outDescent) - { - font_height fh; - GetFontHeight(&fh); - - *outAscent = fh.ascent; - *outDescent = fh.descent; - } - - // Calculate width if needed - if (ioWidth) - *ioWidth = StringWidth(ptr + fromOffset, offset - fromOffset); - - return offset; -} -//------------------------------------------------------------------------------ -float BTextView::StyledWidth(int32 fromOffset, int32 length, float *outAscent, - float *outDescent) const -{ - return 0.0f; -} -//------------------------------------------------------------------------------ -float BTextView::ActualTabWidth(float location) const -{ - return 0.0f; -} -//------------------------------------------------------------------------------ -void BTextView::DoInsertText(const char *inText, int32 inLength, int32 inOffset, - const text_run_array *inRuns, - _BTextChangeResult_ *outResult) -{ -} -//------------------------------------------------------------------------------ -void BTextView::DoDeleteText(int32 fromOffset, int32 toOffset, - _BTextChangeResult_ *outResult) -{ -} -//------------------------------------------------------------------------------ -void BTextView::DrawLines(int32 startLine, int32 endLine, int32 startOffset, - bool erase) -{ - char *string; - int32 length; - - font_height fh; - GetFontHeight(&fh); - - for (int32 i = startLine; i < endLine + 1; i++) - { - string = fText->RealText() + fLines->fObjectList[i].fOffset; - length = fLines->fObjectList[i + 1].fOffset - - fLines->fObjectList[i].fOffset; - - if (length > 0) - { - if (fAlignment == B_ALIGN_LEFT) - DrawString(string, length, BPoint(fTextRect.left, - fTextRect.top + fLines->fObjectList[i].fHeight + - fh.ascent)); - else if (fAlignment == B_ALIGN_CENTER) - DrawString(string, length, BPoint(fTextRect.left + - fTextRect.Width() / 2 - fLines->fObjectList[i].fWidth / 2, - fTextRect.top + fLines->fObjectList[i].fHeight + fh.ascent)); - else - DrawString(string, length, BPoint(fTextRect.right - - fLines->fObjectList[i].fWidth, fTextRect.top + - fLines->fObjectList[i].fHeight + fh.ascent)); - } - } -} -//------------------------------------------------------------------------------ -void BTextView::DrawCaret(int32 offset) -{ - Highlight(offset, offset); -} -//------------------------------------------------------------------------------ -void BTextView::InvertCaret() -{ - Highlight(fSelStart, fSelStart); -} -//------------------------------------------------------------------------------ -void BTextView::DragCaret(int32 offset) -{ -} -//------------------------------------------------------------------------------ -void BTextView::StopMouseTracking() -{ -} -//------------------------------------------------------------------------------ -bool BTextView::PerformMouseUp(BPoint where) -{ - return false; -} -//------------------------------------------------------------------------------ -bool BTextView::PerformMouseMoved(BPoint where, uint32 code) -{ - return false; -} -//------------------------------------------------------------------------------ -void BTextView::TrackMouse(BPoint where, const BMessage *message, bool force) -{ -} -//------------------------------------------------------------------------------ -void BTextView::TrackDrag(BPoint where) -{ -} -//------------------------------------------------------------------------------ -void BTextView::InitiateDrag() -{ -} -//------------------------------------------------------------------------------ -bool BTextView::MessageDropped(BMessage *inMessage, BPoint where, BPoint offset) -{ - return false; -} -//------------------------------------------------------------------------------ -void BTextView::UpdateScrollbars() -{ - BRect bounds(Bounds()); - BScrollBar *vertScroller = ScrollBar(B_VERTICAL); - - if (vertScroller) - { - float height = TextHeight(0, CountLines()); - - if (bounds.Height() > height) - vertScroller->SetRange(0.0f, 0.0f); - else - { - vertScroller->SetRange(0.0f, height - bounds.Height()); - vertScroller->SetProportion(bounds.Height() / height); - } - } -} -//------------------------------------------------------------------------------ -void BTextView::AutoResize(bool doredraw) -{ -} -//------------------------------------------------------------------------------ -void BTextView::NewOffscreen(float padding) -{ -} -//------------------------------------------------------------------------------ -void BTextView::DeleteOffscreen() -{ -} -//------------------------------------------------------------------------------ -void BTextView::Activate() -{ -} -//------------------------------------------------------------------------------ -void BTextView::Deactivate() -{ -} -//------------------------------------------------------------------------------ -void BTextView::NormalizeFont(BFont *font) -{ -} -//------------------------------------------------------------------------------ -uint32 BTextView::CharClassification(int32 offset) const -{ - return 0; -} -//------------------------------------------------------------------------------ -int32 BTextView::NextInitialByte(int32 offset) const -{ - return 0; -} -//------------------------------------------------------------------------------ -int32 BTextView::PreviousInitialByte(int32 offset) const -{ - return 0; -} -//------------------------------------------------------------------------------ -bool BTextView::GetProperty(BMessage *specifier, int32 form, - const char *property, BMessage *reply) -{ - return false; -} -//------------------------------------------------------------------------------ -bool BTextView::SetProperty(BMessage *specifier, int32 form, - const char *property, BMessage *reply) -{ - return false; -} -//------------------------------------------------------------------------------ -bool BTextView::CountProperties(BMessage *specifier, int32 form, - const char *property, BMessage *reply) -{ - return false; -} -//------------------------------------------------------------------------------ -void BTextView::HandleInputMethodChanged(BMessage *message) -{ -} -//------------------------------------------------------------------------------ -void BTextView::HandleInputMethodLocationRequest() -{ -} -//------------------------------------------------------------------------------ -void BTextView::CancelInputMethod() -{ -} -//------------------------------------------------------------------------------ -void BTextView::LockWidthBuffer() -{ -} -//------------------------------------------------------------------------------ -void BTextView::UnlockWidthBuffer() -{ -} -//------------------------------------------------------------------------------ - -//FBC void BTextView::_ReservedTextView3() {} void BTextView::_ReservedTextView4() {} void BTextView::_ReservedTextView5() {} @@ -1631,6 +2149,1118 @@ void BTextView::_ReservedTextView9() {} void BTextView::_ReservedTextView10() {} void BTextView::_ReservedTextView11() {} void BTextView::_ReservedTextView12() {} +//------------------------------------------------------------------------------ +void +BTextView::InitObject(BRect textRect, const BFont *initialFont, + const rgb_color *initialColor) +{ + fTextRect = textRect; + + BFont font; + if (initialFont == NULL) + { + GetFont(&font); + initialFont = &font; + } + + rgb_color black = {0, 0, 0, 255}; + if (initialColor == NULL) + initialColor = &black; + + fText = new _BTextGapBuffer_; + fLines = new _BLineBuffer_; + fStyles = new _BStyleBuffer_(initialFont, initialColor); +} +//------------------------------------------------------------------------------ +void +BTextView::HandleBackspace() +{ + if (fSelStart == fSelEnd) { + if (fSelStart == 0) + return; + else + fSelStart--; + } + else + Highlight(fSelStart, fSelEnd); + + DeleteText(fSelStart, fSelEnd); + fSelEnd = fSelStart; + + Refresh(fSelStart, fSelEnd, true, false); +} +//------------------------------------------------------------------------------ +void +BTextView::HandleArrowKey(uint32 inArrowKey) +{ + // return if there's nowhere to go + if (fText->Length() == 0) + return; + + int32 selStart = fSelStart; + int32 selEnd = fSelEnd; + //int32 delta = 0; + int32 scrollToOffset = 0; + bool shiftDown = modifiers() & B_SHIFT_KEY; + + switch (inArrowKey) { + case B_LEFT_ARROW: + if (shiftDown) { + if (selStart > 0) + selStart = PreviousInitialByte(selStart); + } + else { + if (selStart == selEnd) { + if (selStart > 0) + selEnd = selStart = PreviousInitialByte(selStart); + } + else + selEnd = selStart; + } + scrollToOffset = selStart; + break; + + case B_RIGHT_ARROW: + if (shiftDown) { + if (selEnd < fText->Length()) + selEnd = NextInitialByte(selEnd); + } + else { + if (selStart == selEnd) { + if (selStart < fText->Length()) + selStart = selEnd = NextInitialByte(selEnd); + } + else + selStart = selEnd; + } + scrollToOffset = selEnd; + break; + + case B_UP_ARROW: + { + BPoint point = PointAt(selStart); + point.y--; + selStart = OffsetAt(point); + if (!shiftDown) + selEnd = selStart; + scrollToOffset = selStart; + break; + } + + case B_DOWN_ARROW: + { + float height; + BPoint point = PointAt(selEnd, &height); + point.y += height; + selEnd = OffsetAt(point); + if (!shiftDown) + selStart = selEnd; + scrollToOffset = selEnd; + break; + } + } + + // invalidate the null style + fStyles->InvalidateNullStyle(); + + Select(selStart, selEnd); + + // scroll if needed + ScrollToOffset(scrollToOffset); +} +//------------------------------------------------------------------------------ +void +BTextView::HandleDelete() +{ + if (fSelStart == fSelEnd) { + if (fSelEnd == fText->Length()) + return; + else + fSelEnd = NextInitialByte(fSelEnd); + } + else + Highlight(fSelStart, fSelEnd); + + DeleteText(fSelStart, fSelEnd); + + fSelEnd = fSelStart; + + Refresh(fSelStart, fSelEnd, true, true); +} +//------------------------------------------------------------------------------ +void +BTextView::HandlePageKey(uint32 inPageKey) +{ + switch (inPageKey) { + case B_HOME: + case B_END: + ScrollToOffset((inPageKey == B_HOME) ? 0 : fText->Length()); + break; + + case B_PAGE_UP: + case B_PAGE_DOWN: + { + if (ScrollBar(B_VERTICAL) != NULL) { + float delta = Bounds().Height(); + delta = (inPageKey == B_PAGE_UP) ? -delta : delta; + ScrollBar(B_VERTICAL)->SetValue(ScrollBar(B_VERTICAL)->Value() + delta); + } + break; + } + } +} +//------------------------------------------------------------------------------ +void +BTextView::HandleAlphaKey(const char *bytes, int32 numBytes) +{ + bool refresh = fSelStart != fText->Length(); + + if (fSelStart != fSelEnd) { + Highlight(fSelStart, fSelEnd); + DeleteText(fSelStart, fSelEnd); + refresh = true; + } + +/* if (fAutoindent && numBytes == 1 && *bytes == B_ENTER) + { + int32 start, offset; + start = offset = OffsetAt(LineAt(fSelStart)); + const char *text = Text(); + + while (*(text + offset) != '\0' && + *(text + offset) == '\t' || *(text + offset) == ' ') + offset++; + + if (start != offset) + InsertText(text + start, offset - start, fSelStart, NULL); + + InsertText(bytes, numBytes, fSelStart, NULL); + numBytes += offset - start; + } + else*/ + InsertText(bytes, numBytes, fSelStart, NULL); + + fSelEnd = fSelStart = fSelStart + numBytes; + + Refresh(fSelStart, fSelEnd, refresh, true); +} +//------------------------------------------------------------------------------ +void +BTextView::Refresh(int32 fromOffset, int32 toOffset, bool erase, + bool scroll) +{ + float saveHeight = fTextRect.Height(); + int32 fromLine = LineAt(fromOffset); + int32 toLine = LineAt(toOffset); + int32 saveFromLine = fromLine; + int32 saveToLine = toLine; + float saveLineHeight = LineHeight(fromLine); + BRect bounds = Bounds(); + + RecalLineBreaks(&fromLine, &toLine); + + float newHeight = fTextRect.Height(); + + // if the line breaks have changed, force an erase + if ( (fromLine != saveFromLine) || (toLine != saveToLine) || + (newHeight != saveHeight) ) + erase = true; + + if (newHeight != saveHeight) { + // the text area has changed + if (newHeight < saveHeight) + toLine = LineAt(BPoint(0.0f, saveHeight + fTextRect.top)); + else + toLine = LineAt(BPoint(0.0f, newHeight + fTextRect.top)); + } + + // draw only those lines that are visible + int32 fromVisible = LineAt(BPoint(0.0f, bounds.top)); + int32 toVisible = LineAt(BPoint(0.0f, bounds.bottom)); + fromLine = (fromVisible > fromLine) ? fromVisible : fromLine; + toLine = (toLine > toVisible) ? toVisible : toLine; + + int32 drawOffset = fromOffset; + if ( (LineHeight(fromLine) != saveLineHeight) || + (newHeight < saveHeight) || (fromLine < saveFromLine) ) + drawOffset = (*fLines)[fromLine]->offset; + + DrawLines(fromLine, toLine, drawOffset, erase); + + // erase the area below the text + BRect eraseRect = bounds; + eraseRect.top = fTextRect.top + (*fLines)[fLines->NumLines()]->origin; + eraseRect.bottom = fTextRect.top + saveHeight; + if ((eraseRect.bottom > eraseRect.top) && (eraseRect.Intersects(bounds))) { + SetLowColor(ViewColor()); + FillRect(eraseRect, B_SOLID_LOW); + } + + // update the scroll bars if the text area has changed + if (newHeight != saveHeight) + UpdateScrollbars(); + + if (scroll) + ScrollToOffset(fSelEnd); + + Flush(); //// +} +//------------------------------------------------------------------------------ +void +BTextView::RecalLineBreaks(int32 *startLine, int32 *endLine) +{ + // are we insane? + *startLine = (*startLine < 0) ? 0 : *startLine; + *endLine = (*endLine > fLines->NumLines() - 1) ? fLines->NumLines() - 1 : *endLine; + + int32 textLength = fText->Length(); + int32 lineIndex = (*startLine > 0) ? *startLine - 1 : 0; + int32 recalThreshold = (*fLines)[*endLine + 1]->offset; + float width = fTextRect.Width(); + STELinePtr curLine = (*fLines)[lineIndex]; + STELinePtr nextLine = curLine + 1; + + do { + float ascent, descent; + int32 fromOffset = curLine->offset; + int32 toOffset = FindLineBreak(fromOffset, &ascent, + &descent, &width); + + // we want to advance at least by one character + if ((toOffset == fromOffset) && (fromOffset < textLength)) + toOffset = NextInitialByte(toOffset); + + // set the ascent of this line + curLine->ascent = ascent; + + lineIndex++; + STELine saveLine = *nextLine; + if ( (lineIndex > fLines->NumLines()) || + (toOffset < nextLine->offset) ) { + // the new line comes before the old line start, add a line + STELine newLine; + newLine.offset = toOffset; + newLine.origin = curLine->origin + ascent + descent; + newLine.ascent = 0; + fLines->InsertLine(&newLine, lineIndex); + } + else { + // update the exising line + nextLine->offset = toOffset; + nextLine->origin = curLine->origin + ascent + descent; + + // remove any lines that start before the current line + while ( (lineIndex < fLines->NumLines()) && + (toOffset >= ((*fLines)[lineIndex] + 1)->offset) ) + fLines->RemoveLines(lineIndex + 1); + + nextLine = (*fLines)[lineIndex]; + if (nextLine->offset == saveLine.offset) { + if (nextLine->offset >= recalThreshold) { + if (nextLine->origin != saveLine.origin) + fLines->BumpOrigin(nextLine->origin - saveLine.origin, + lineIndex + 1); + break; + } + } + else { + if ((lineIndex > 0) && (lineIndex == *startLine)) + *startLine = lineIndex - 1; + } + } + + curLine = (*fLines)[lineIndex]; + nextLine = curLine + 1; + } while (curLine->offset < textLength); + + // update the text rect + float newHeight = TextHeight(0, fLines->NumLines() - 1); + fTextRect.bottom = fTextRect.top + newHeight; + + *endLine = lineIndex - 1; + *startLine = (*startLine > *endLine) ? *endLine : *startLine; +} +//------------------------------------------------------------------------------ +int32 +BTextView::FindLineBreak(int32 fromOffset, float *outAscent, + float *outDescent, float *ioWidth) +{ + *outAscent = 0.0; + *outDescent = 0.0; + + const int32 limit = fText->Length(); + + // is fromOffset at the end? + if (fromOffset >= limit) { + // try to return valid height info anyway + if (fStyles->NumRuns() > 0) + fStyles->Iterate(fromOffset, 1, NULL, NULL, outAscent, outDescent); + else { + if (fStyles->IsValidNullStyle()) { + const BFont *font = NULL; + fStyles->GetNullStyle(&font, NULL); + + font_height fh; + font->GetHeight(&fh); + *outAscent = fh.ascent; + *outDescent = fh.descent + fh.leading; + } + } + + return (limit); + } + + bool done = false; + float ascent = 0.0; + float descent = 0.0; + int32 offset = fromOffset; + int32 delta = 0; + float deltaWidth = 0.0; + float tabWidth = 0.0; + float strWidth = 0.0; + + // do we need to wrap the text? + if (!fWrap) { + offset = limit - fromOffset; + fText->FindChar('\n', fromOffset, &offset); + offset += fromOffset; + offset = (offset < limit) ? offset + 1 : limit; + + // iterate through the style runs + int32 length = offset; + int32 startOffset = fromOffset; + while (int32 numChars = fStyles->Iterate(startOffset, length, NULL, NULL, &ascent, &descent)) { + *outAscent = (ascent > *outAscent) ? ascent : *outAscent; + *outDescent = (descent > *outDescent) ? descent : *outDescent; + + startOffset += numChars; + length -= numChars; + } + + return (offset); + } + + // wrap the text + do { + bool foundTab = false; + + // find the next line break candidate + for ( ; (offset + delta) < limit ; delta++) { + if (CanEndLine(offset + delta)) + break; + } + for ( ; (offset + delta) < limit; delta++) { + uchar theChar = (*fText)[offset + delta]; + if (!CanEndLine(offset + delta)) + break; + + if (theChar == '\n') { + // found a newline, we're done! + done = true; + delta++; + break; + } + else { + // include all trailing spaces and tabs, + // but not spaces after tabs + if ((theChar != ' ') && (theChar != '\t')) + break; + else { + if ((theChar == ' ') && (foundTab)) + break; + else { + if (theChar == '\t') + foundTab = true; + } + } + } + } + delta = (delta < 1) ? 1 : delta; + + deltaWidth = StyledWidth(offset, delta, &ascent, &descent); + strWidth += deltaWidth; + + if (!foundTab) + tabWidth = 0.0; + else { + int32 tabCount = 0; + for (int32 i = delta - 1; (*fText)[offset + i] == '\t'; i--) + tabCount++; + + tabWidth = fTabWidth - fmod(strWidth, fTabWidth); + if (tabCount > 1) + tabWidth += ((tabCount - 1) * fTabWidth); + strWidth += tabWidth; + } + + if (strWidth >= *ioWidth) { + // we've found where the line will wrap + bool foundNewline = done; + done = true; + int32 pos = delta - 1; + if (((*fText)[offset + pos] != ' ') && + ((*fText)[offset + pos] != '\t') && + ((*fText)[offset + pos] != '\n')) + break; + + strWidth -= (deltaWidth + tabWidth); + + for ( ; ((offset + pos) > offset); pos--) { + uchar theChar = (*fText)[offset + pos]; + if ((theChar != ' ') && + (theChar != '\t') && + (theChar != '\n')) + break; + } + + strWidth += StyledWidth(offset, pos + 1, &ascent, &descent); + if (strWidth >= *ioWidth) + break; + + if (!foundNewline) { + for ( ; (offset + delta) < limit; delta++) { + if (((*fText)[offset + delta] != ' ') && + ((*fText)[offset + delta] != '\t')) + break; + } + if ( ((offset + delta) < limit) && + ((*fText)[offset + delta] == '\n') ) + delta++; + } + // get the ascent and descent of the spaces/tabs + StyledWidth(offset, delta, &ascent, &descent); + } + + *outAscent = (ascent > *outAscent) ? ascent : *outAscent; + *outDescent = (descent > *outDescent) ? descent : *outDescent; + + offset += delta; + delta = 0; + } while ((offset < limit) && (!done)); + + if ((offset - fromOffset) < 1) { + // there weren't any words that fit entirely in this line + // force a break in the middle of a word + *outAscent = 0.0; + *outDescent = 0.0; + strWidth = 0.0; + + for (offset = fromOffset; offset < limit; offset++) { + strWidth += StyledWidth(offset, 1, &ascent, &descent); + + if (strWidth >= *ioWidth) + break; + + *outAscent = (ascent > *outAscent) ? ascent : *outAscent; + *outDescent = (descent > *outDescent) ? descent : *outDescent; + } + } + + offset = (offset < limit) ? offset : limit; + + return (offset); +} +//------------------------------------------------------------------------------ +float +BTextView::StyledWidth(int32 fromOffset, int32 length, float *outAscent, + float *outDescent) const +{ + float result = 0.0; + float ascent = 0.0; + float descent = 0.0; + float maxAscent = 0.0; + float maxDescent = 0.0; + + // iterate through the style runs + const BFont *font = NULL; + while (long numChars = fStyles->Iterate(fromOffset, length, &font, NULL, &ascent, &descent)) { + maxAscent = (ascent > maxAscent) ? ascent : maxAscent; + maxDescent = (descent > maxDescent) ? descent : maxDescent; + + result += font->StringWidth(fText->Text() + fromOffset, numChars); + + fromOffset += numChars; + length -= numChars; + } + + if (outAscent != NULL) + *outAscent = maxAscent; + if (outDescent != NULL) + *outDescent = maxDescent; + + return (result); +} +//------------------------------------------------------------------------------ +float +BTextView::ActualTabWidth(float location) const +{ + return 0.0f; +} +//------------------------------------------------------------------------------ +void +BTextView::DoInsertText(const char *inText, int32 inLength, int32 inOffset, + const text_run_array *inRuns, + _BTextChangeResult_ *outResult) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::DoDeleteText(int32 fromOffset, int32 toOffset, + _BTextChangeResult_ *outResult) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::DrawLines(int32 startLine, int32 endLine, int32 startOffset, + bool erase) +{ + // clip the text + BRect clipRect = Bounds() & fTextRect; + clipRect.InsetBy(-1, -1); + BRegion newClip; + newClip.Set(clipRect); + ConstrainClippingRegion(&newClip); + + // set the low color to the view color so that + // drawing to a non-white background will work + SetLowColor(ViewColor()); + + long maxLine = fLines->NumLines() - 1; + startLine = (startLine < 0) ? 0 : startLine; + endLine = (endLine > maxLine) ? maxLine : endLine; + + BRect eraseRect = clipRect; + long startEraseLine = startLine; + STELinePtr line = (*fLines)[startLine]; + + if ((erase) && (startOffset != -1)) { + // erase only to the right of startOffset + startEraseLine++; + long startErase = startOffset; + if (startErase > line->offset) { + for ( ; ((*fText)[startErase] != ' ') && ((*fText)[startErase] != '\t'); startErase--) { + if (startErase <= line->offset) + break; + } + if (startErase > line->offset) + startErase--; + } + + eraseRect.left = PointAt(startErase).x; + eraseRect.top = line->origin + fTextRect.top; + eraseRect.bottom = (line + 1)->origin + fTextRect.top; + + FillRect(eraseRect, B_SOLID_LOW); + + eraseRect = clipRect; + } + + for (long i = startLine; i <= endLine; i++) { + long length = (line + 1)->offset - line->offset; + // DrawString() chokes if you draw a newline + if ((*fText)[(line + 1)->offset - 1] == '\n') + length--; + + MovePenTo(fTextRect.left, line->origin + line->ascent + fTextRect.top); + + if ((erase) && (i >= startEraseLine)) { + eraseRect.top = line->origin + fTextRect.top; + eraseRect.bottom = (line + 1)->origin + fTextRect.top; + + FillRect(eraseRect, B_SOLID_LOW); + } + + // do we have any text to draw? + if (length > 0) { + // iterate through each style on this line + //BPoint startPenLoc; + bool foundTab = false; + long tabChars = 0; + long numTabs = 0; + long offset = line->offset; + const BFont *font = NULL; + const rgb_color *color = NULL; + while (long numChars = fStyles->Iterate(offset, length, &font, &color)) { + SetFont(font); + SetHighColor(*color); + + tabChars = numChars; + do { + //if (style->underline) + // startPenLoc = PenLocation(); + + foundTab = fText->FindChar('\t', offset, &tabChars); + if (foundTab) { + for (numTabs = 0; (tabChars + numTabs) < numChars; numTabs++) { + if ((*fText)[offset + tabChars + numTabs] != '\t') + break; + } + } + + DrawString(fText->GetString(offset, tabChars), tabChars); + + if (foundTab) { + float penPos = PenLocation().x - fTextRect.left; + float tabWidth = fTabWidth - fmod(penPos, fTabWidth); + if (numTabs > 1) + tabWidth += ((numTabs - 1) * fTabWidth); + + MovePenBy(tabWidth, 0.0); + + tabChars += numTabs; + } + + /*if (style->underline) { + BPoint savePenLoc = PenLocation(); + BPoint curPenLoc = savePenLoc; + startPenLoc.y += 1.0; + curPenLoc.y += 1.0; + + StrokeLine(startPenLoc, curPenLoc); + + MovePenTo(savePenLoc); + }*/ + + offset += tabChars; + length -= tabChars; + numChars -= tabChars; + tabChars = numChars; + numTabs = 0; + } while ((foundTab) && (tabChars > 0)); + } + } + line++; + } + + ConstrainClippingRegion(NULL); +} +//------------------------------------------------------------------------------ +void +BTextView::DrawCaret(int32 offset) +{ + //long lineNum = LineAt(offset); + //STELinePtr line = (*fLines)[lineNum]; + float lineHeight; + BPoint caretPoint = PointAt(offset, &lineHeight); + caretPoint.x = (caretPoint.x > fTextRect.right) ? fTextRect.right : caretPoint.x; + + BRect caretRect; + caretRect.left = caretRect.right = caretPoint.x; + caretRect.top = caretPoint.y; + caretRect.bottom = caretPoint.y + lineHeight; + + InvertRect(caretRect); + + Flush(); //// +} +//------------------------------------------------------------------------------ +void +BTextView::InvertCaret() +{ + DrawCaret(fSelStart); + fCaretVisible = !fCaretVisible; + fCaretTime = system_time(); +} +//------------------------------------------------------------------------------ +void +BTextView::DragCaret(int32 offset) +{ + // does the caret need to move? + if (offset == fDragOffset) + return; + + // hide the previous drag caret + if (fDragOffset != -1) + DrawCaret(fDragOffset); + + // do we have a new location? + if (offset != -1) { + if (fActive) { + // ignore if offset is within active selection + if ((offset >= fSelStart) && (offset <= fSelEnd)) { + fDragOffset = -1; + return; + } + } + + DrawCaret(offset); + } + + fDragOffset = offset; +} +//------------------------------------------------------------------------------ +void +BTextView::StopMouseTracking() +{ +} +//------------------------------------------------------------------------------ +bool +BTextView::PerformMouseUp(BPoint where) +{ + return false; +} +//------------------------------------------------------------------------------ +bool BTextView::PerformMouseMoved(BPoint where, uint32 code) +{ + return false; +} +//------------------------------------------------------------------------------ +void +BTextView::TrackMouse(BPoint where, const BMessage *message, bool force) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::TrackDrag(BPoint where) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::InitiateDrag() +{ + BMessage *drag = new BMessage(); + + // add the text + drag->AddData("text", B_ASCII_TYPE, fText->Text() + fSelStart, + fSelEnd - fSelStart); + + // add the corresponding styles + int32 size = 0; + text_run_array *styles = RunArray(fSelStart, fSelEnd, &size); + + drag->AddData("application/x-vnd.Be-text_run_array", B_MIME_TYPE, + styles, size); + + BRegion hiliteRgn; + GetTextRegion(fSelStart, fSelEnd, &hiliteRgn); + BRect bounds = Bounds(); + BRect dragRect = hiliteRgn.Frame(); + if (!bounds.Contains(dragRect)) + dragRect = bounds & dragRect; + + be_app->SetCursor(B_HAND_CURSOR); + DragMessage(drag, dragRect); +} +//------------------------------------------------------------------------------ +bool +BTextView::MessageDropped(BMessage *inMessage, BPoint where, BPoint offset) +{ + // make sure the drag caret is erased + DragCaret(-1); + + if (fActive) + be_app->SetCursor(B_I_BEAM_CURSOR); + + // are we sure we like this message? + if (!AcceptsDrop(inMessage)) + return false; + + long dropOffset = OffsetAt(where); + + // if this view initiated the drag, move instead of copy + if ((fActive) && (fSelStart != fSelEnd)) { + // dropping onto itself? + if ((dropOffset >= fSelStart) && (dropOffset <= fSelEnd)) + return true; + + if (dropOffset > fSelEnd) + dropOffset -= fSelEnd - fSelStart; + + Delete(); + } + + Select(dropOffset, dropOffset); + + ssize_t dataLen = 0; + const char *text; + inMessage->FindData("text", B_ASCII_TYPE, (const void**)&text, &dataLen); + /*if (text != NULL) { + long styleLen = 0; + STEStyleRangePtr styles = NULL; + styles = (STEStyleRangePtr)inMessage->FindData("style", STE_STYLE_TYPE, &styleLen); + + Insert(text, dataLen, styles); + } + else { + char theChar = inMessage->FindLong("char"); + if (inMessage->Error() == B_NO_ERROR) + Insert(&theChar, 1); + }*/ + + return true; +} +//------------------------------------------------------------------------------ +void +BTextView::UpdateScrollbars() +{ + //BRect bounds(Bounds()); + BScrollBar *hsb = ScrollBar(B_HORIZONTAL); + BScrollBar *vsb = ScrollBar(B_VERTICAL); + + // do we have a horizontal scroll bar? + if (hsb != NULL) { + long viewWidth = Bounds().IntegerWidth(); + long dataWidth = fTextRect.IntegerWidth(); + dataWidth += (long)ceil(fTextRect.left) + 1; + + long maxRange = dataWidth - viewWidth; + maxRange = (maxRange < 0) ? 0 : maxRange; + + hsb->SetRange(0, (float)maxRange); + hsb->SetProportion((float)viewWidth / (float)dataWidth); + hsb->SetSteps(10, dataWidth / 10); + } + + // how about a vertical scroll bar? + if (vsb != NULL) { + long viewHeight = Bounds().IntegerHeight(); + long dataHeight = fTextRect.IntegerHeight(); + dataHeight += (long)ceil(fTextRect.top) + 1; + + long maxRange = dataHeight - viewHeight; + maxRange = (maxRange < 0) ? 0 : maxRange; + + vsb->SetRange(0, maxRange); + vsb->SetProportion((float)viewHeight / (float)dataHeight); + vsb->SetSteps(12, viewHeight); + } +} +//------------------------------------------------------------------------------ +void +BTextView::AutoResize(bool doredraw) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::NewOffscreen(float padding) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::DeleteOffscreen() +{ +} +//------------------------------------------------------------------------------ +void +BTextView::Activate() +{ + fActive = true; + + if (fSelStart != fSelEnd) { + if (fSelectable) + Highlight(fSelStart, fSelEnd); + } + else { + if (fEditable) + InvertCaret(); + } + + BPoint where; + ulong buttons; + GetMouse(&where, &buttons); + if (Bounds().Contains(where)) + be_app->SetCursor(B_I_BEAM_CURSOR); +} +//------------------------------------------------------------------------------ +void +BTextView::Deactivate() +{ + fActive = false; + + if (fSelStart != fSelEnd) { + if (fSelectable) + Highlight(fSelStart, fSelEnd); + } + else { + if (fCaretVisible) + InvertCaret(); + } + + BPoint where; + ulong buttons; + GetMouse(&where, &buttons); + if (Bounds().Contains(where)) + be_app->SetCursor(B_HAND_CURSOR); +} +//------------------------------------------------------------------------------ +void +BTextView::NormalizeFont(BFont *font) +{ + font->SetRotation(0.0f); + font->SetFlags(font->Flags() & ~B_DISABLE_ANTIALIASING); + font->SetSpacing(B_BITMAP_SPACING); + font->SetEncoding(B_UNICODE_UTF8); +} +//------------------------------------------------------------------------------ +uint32 +BTextView::CharClassification(int32 offset) const +{ + //char c = fText->RealCharAt(offset); + char c = *(Text() + offset); + + // Should check against a list of character containing also + // japanese word breakers + if (isspace(c) || ispunct(c)) + return 1; + return 0; +} +//------------------------------------------------------------------------------ +int32 +BTextView::NextInitialByte(int32 offset) const +{ + const char *text = Text(); + + if (text == NULL) + return 0; + + for (++offset; (*(text + offset) & 0xc0) == 0x80; ++offset) + ; + + return offset; +} +//------------------------------------------------------------------------------ +int32 +BTextView::PreviousInitialByte(int32 offset) const +{ + const char *text = Text(); + int count = 6; + + for (--offset; (text + offset) > text && count; --offset, --count) + { + if ((*(text + offset) & 0xc0 ) != 0x80) + break; + } + + return count ? offset : 0; +} +//------------------------------------------------------------------------------ +bool +BTextView::GetProperty(BMessage *specifier, int32 form, + const char *property, BMessage *reply) +{ + if (strcmp(property, "Selection") == 0) + { + reply->what = B_REPLY; + reply->AddInt32("result", fSelStart); + reply->AddInt32("result", fSelEnd); + reply->AddInt32("error", B_OK); + return true; + } + else if (strcmp(property, "Text") == 0) + { + int32 index, range; + char *buffer; + + specifier->FindInt32("index", &index); + specifier->FindInt32("range", &range); + + buffer = new char[range + 1]; + GetText(index, range, buffer); + + reply->what = B_REPLY; + reply->AddString("result", buffer); + delete buffer; + reply->AddInt32("error", B_OK); + return true; + } + else if (strcmp(property, "text_run_array") == 0) + { + return false; + } + else + return false; +} +//------------------------------------------------------------------------------ +bool +BTextView::SetProperty(BMessage *specifier, int32 form, + const char *property, BMessage *reply) +{ + if (strcmp(property, "Selection") == 0) + { + int32 index, range; + + specifier->FindInt32("index", &index); + specifier->FindInt32("range", &range); + + Select(index, index + range); + + reply->what = B_REPLY; + reply->AddInt32("error", B_OK); + + return true; + } + else if (strcmp(property, "Text") == 0) + { + int32 index, range; + const char *buffer; + + specifier->FindInt32("index", &index); + specifier->FindInt32("range", &range); + + if (specifier->FindString("data", &buffer) == B_OK) + InsertText(buffer, range, index, NULL); + else + DeleteText(index, range); + + reply->what = B_REPLY; + reply->AddInt32("error", B_OK); + + return true; + } + else if (strcmp(property, "text_run_array") == 0) + { + return false; + } + else + return false; +} +//------------------------------------------------------------------------------ +bool +BTextView::CountProperties(BMessage *specifier, int32 form, + const char *property, BMessage *reply) +{ + if (strcmp(property, "Text") == 0) + { + reply->what = B_REPLY; + reply->AddInt32("result", TextLength()); + reply->AddInt32("error", B_OK); + + return true; + } + else + return false; +} +//------------------------------------------------------------------------------ +void +BTextView::HandleInputMethodChanged(BMessage *message) +{ +} +//------------------------------------------------------------------------------ +void +BTextView::HandleInputMethodLocationRequest() +{ +} +//------------------------------------------------------------------------------ +void +BTextView::CancelInputMethod() +{ +} +//------------------------------------------------------------------------------ +void +BTextView::LockWidthBuffer() +{ + if (atomic_add(&sWidthAtom, -1) <= 0) + acquire_sem(sWidthSem); +} +//------------------------------------------------------------------------------ +void +BTextView::UnlockWidthBuffer() +{ + if (atomic_add(&sWidthAtom, 1) < 0) + release_sem(sWidthSem); +} +//------------------------------------------------------------------------------ + /* * $Log $ * diff --git a/src/kits/interface/BTextView/TextViewSupportBuffer.h b/src/kits/interface/BTextView/TextViewSupportBuffer.h index ea447fd84c..f7b7eda658 100644 --- a/src/kits/interface/BTextView/TextViewSupportBuffer.h +++ b/src/kits/interface/BTextView/TextViewSupportBuffer.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS +// Copyright (c) 2001-2003, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -25,8 +25,12 @@ // buffer classes. //------------------------------------------------------------------------------ +#ifndef __TEXT_VIEW_SUPPORT_BUFFER__H__ +#define __TEXT_VIEW_SUPPORT_BUFFER__H__ + // Standard Includes ----------------------------------------------------------- -#include +#include +#include // System Includes ------------------------------------------------------------- #include "SupportDefs.h" @@ -44,57 +48,99 @@ template class _BTextViewSupportBuffer_ { public: - _BTextViewSupportBuffer_(int32 count, int32 blockSize); + _BTextViewSupportBuffer_(long inExtraCount = 0, + long inCount = 0); virtual ~_BTextViewSupportBuffer_(); - void InsertItemsAt(int32 offset, int32 count, const T *items); - void RemoveItemsAt(int32 offset, int32 count); + void InsertItemsAt(int32 inNumItems, int32 inAtIndex, const T *inItem); + void RemoveItemsAt(int32 inNumItems, int32 inAtIndex); - int32 fBlockSize; - int32 fCount; - int32 fPhysicalSize; - T *fItems; - int32 fReserved; + int32 ItemCount() const; + +//protected: + int32 fExtraCount; + int32 fItemCount; + T* fBuffer; + int32 fBufferCount; }; //------------------------------------------------------------------------------ template -_BTextViewSupportBuffer_::_BTextViewSupportBuffer_(int32 count, int32 blockSize) - : - fBlockSize(blockSize), - fCount(count), - fPhysicalSize(count + blockSize), - fItems(NULL) +_BTextViewSupportBuffer_::_BTextViewSupportBuffer_(long inExtraCount, + long inCount) + : fExtraCount(inExtraCount), + fItemCount(inCount), + fBuffer(NULL), + fBufferCount(fExtraCount + fItemCount) { - fItems = (T*)malloc(fPhysicalSize * sizeof(T)); + fBuffer = (T *)calloc(fExtraCount + fItemCount, sizeof(T)); } //------------------------------------------------------------------------------ template _BTextViewSupportBuffer_::~_BTextViewSupportBuffer_() { - free(fItems); + free(fBuffer); } //------------------------------------------------------------------------------ template -void _BTextViewSupportBuffer_::InsertItemsAt(int32 offset, int32 count, - const T *items) +void _BTextViewSupportBuffer_::InsertItemsAt(int32 inNumItems, + int32 inAtIndex, + const T *inItem) { - if (fPhysicalSize < fCount + count) - { - int32 new_size = (((fCount + count) / fBlockSize) + 1) * fBlockSize; + if (inNumItems < 1) + return; + + inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex; + inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; - fItems = realloc(fItems, new_size); + long delta = inNumItems * sizeof(T); + long logSize = fItemCount * sizeof(T); + if ((logSize + delta) >= fBufferCount) { + fBufferCount = logSize + delta + (fExtraCount * sizeof(T)); + fBuffer = (T *)realloc(fBuffer, fBufferCount); } - - memcpy(fItems + offset, items, count); - fCount += count; + + T *loc = fBuffer + inAtIndex; + memmove(loc + inNumItems, loc, (fItemCount - inAtIndex) * sizeof(T)); + memcpy(loc, inItem, delta); + + fItemCount += inNumItems; } //------------------------------------------------------------------------------ template -void _BTextViewSupportBuffer_::RemoveItemsAt(int32 offset, int32 count) +void +_BTextViewSupportBuffer_::RemoveItemsAt(int32 inNumItems, + int32 inAtIndex) { - + if (inNumItems < 1) + return; + + inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex; + inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; + + T *loc = fBuffer + inAtIndex; + memmove(loc, loc + inNumItems, + (fItemCount - (inNumItems + inAtIndex)) * sizeof(T)); + + long delta = inNumItems * sizeof(T); + long logSize = fItemCount * sizeof(T); + long extraSize = fBufferCount - (logSize - delta); + if (extraSize > (fExtraCount * sizeof(T))) { + fBufferCount = (logSize - delta) + (fExtraCount * sizeof(T)); + fBuffer = (T *)realloc(fBuffer, fBufferCount); + } + + fItemCount -= inNumItems; } //------------------------------------------------------------------------------ +template +inline int32 +_BTextViewSupportBuffer_::ItemCount() const +{ + return fItemCount; +} +//------------------------------------------------------------------------------ + +#endif // __TEXT_VIEW_SUPPORT_BUFFER__H__ /* * $Log $