/** @file Decoration.cxx ** Visual elements added over text. **/ // Copyright 1998-2007 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #include #include #include #include #include #include "ScintillaTypes.h" #include "Debugging.h" #include "Position.h" #include "SplitVector.h" #include "Partitioning.h" #include "RunStyles.h" #include "Decoration.h" using namespace Scintilla::Internal; namespace { template class Decoration : public IDecoration { int indicator; public: RunStyles rs; explicit Decoration(int indicator_) : indicator(indicator_) { } bool Empty() const noexcept override { return (rs.Runs() == 1) && (rs.AllSameAs(0)); } int Indicator() const noexcept override { return indicator; } Sci::Position Length() const noexcept override { return rs.Length(); } int ValueAt(Sci::Position position) const noexcept override { return rs.ValueAt(static_cast(position)); } Sci::Position StartRun(Sci::Position position) const noexcept override { return rs.StartRun(static_cast(position)); } Sci::Position EndRun(Sci::Position position) const noexcept override { return rs.EndRun(static_cast(position)); } void SetValueAt(Sci::Position position, int value) override { rs.SetValueAt(static_cast(position), value); } void InsertSpace(Sci::Position position, Sci::Position insertLength) override { rs.InsertSpace(static_cast(position), static_cast(insertLength)); } Sci::Position Runs() const noexcept override { return rs.Runs(); } }; template class DecorationList : public IDecorationList { int currentIndicator; int currentValue; Decoration *current; // Non-owning. Cached so FillRange doesn't have to search for each call. Sci::Position lengthDocument; // Ordered by indicator std::vector>> decorationList; std::vector decorationView; // Read-only view of decorationList bool clickNotified; Decoration *DecorationFromIndicator(int indicator) noexcept; Decoration *Create(int indicator, Sci::Position length); void Delete(int indicator); void DeleteAnyEmpty(); void SetView(); public: DecorationList(); const std::vector &View() const noexcept override { return decorationView; } void SetCurrentIndicator(int indicator) override; int GetCurrentIndicator() const noexcept override { return currentIndicator; } void SetCurrentValue(int value) override; int GetCurrentValue() const noexcept override { return currentValue; } // Returns changed=true if some values may have changed FillResult FillRange(Sci::Position position, int value, Sci::Position fillLength) override; void InsertSpace(Sci::Position position, Sci::Position insertLength) override; void DeleteRange(Sci::Position position, Sci::Position deleteLength) override; void DeleteLexerDecorations() override; int AllOnFor(Sci::Position position) const noexcept override; int ValueAt(int indicator, Sci::Position position) noexcept override; Sci::Position Start(int indicator, Sci::Position position) noexcept override; Sci::Position End(int indicator, Sci::Position position) noexcept override; bool ClickNotified() const noexcept override { return clickNotified; } void SetClickNotified(bool notified) noexcept override { clickNotified = notified; } }; template DecorationList::DecorationList() : currentIndicator(0), currentValue(1), current(nullptr), lengthDocument(0), clickNotified(false) { } template Decoration *DecorationList::DecorationFromIndicator(int indicator) noexcept { for (const std::unique_ptr> &deco : decorationList) { if (deco->Indicator() == indicator) { return deco.get(); } } return nullptr; } template Decoration *DecorationList::Create(int indicator, Sci::Position length) { currentIndicator = indicator; std::unique_ptr> decoNew = std::make_unique>(indicator); decoNew->rs.InsertSpace(0, static_cast(length)); typename std::vector>>::iterator it = std::lower_bound( decorationList.begin(), decorationList.end(), decoNew, [](const std::unique_ptr> &a, const std::unique_ptr> &b) noexcept { return a->Indicator() < b->Indicator(); }); typename std::vector>>::iterator itAdded = decorationList.insert(it, std::move(decoNew)); SetView(); return itAdded->get(); } template void DecorationList::Delete(int indicator) { decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(), [indicator](const std::unique_ptr> &deco) noexcept { return deco->Indicator() == indicator; }), decorationList.end()); current = nullptr; SetView(); } template void DecorationList::SetCurrentIndicator(int indicator) { currentIndicator = indicator; current = DecorationFromIndicator(indicator); currentValue = 1; } template void DecorationList::SetCurrentValue(int value) { currentValue = value ? value : 1; } template FillResult DecorationList::FillRange(Sci::Position position, int value, Sci::Position fillLength) { if (!current) { current = DecorationFromIndicator(currentIndicator); if (!current) { current = Create(currentIndicator, lengthDocument); } } // Converting result from POS to Sci::Position as callers not polymorphic. const FillResult frInPOS = current->rs.FillRange(static_cast(position), value, static_cast(fillLength)); const FillResult fr { frInPOS.changed, frInPOS.position, frInPOS.fillLength }; if (current->Empty()) { Delete(currentIndicator); } return fr; } template void DecorationList::InsertSpace(Sci::Position position, Sci::Position insertLength) { const bool atEnd = position == lengthDocument; lengthDocument += insertLength; for (const std::unique_ptr> &deco : decorationList) { deco->rs.InsertSpace(static_cast(position), static_cast(insertLength)); if (atEnd) { deco->rs.FillRange(static_cast(position), 0, static_cast(insertLength)); } } } template void DecorationList::DeleteRange(Sci::Position position, Sci::Position deleteLength) { lengthDocument -= deleteLength; for (const std::unique_ptr> &deco : decorationList) { deco->rs.DeleteRange(static_cast(position), static_cast(deleteLength)); } DeleteAnyEmpty(); if (decorationList.size() != decorationView.size()) { // One or more empty decorations deleted so update view. current = nullptr; SetView(); } } template void DecorationList::DeleteLexerDecorations() { decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(), [](const std::unique_ptr> &deco) noexcept { return deco->Indicator() < static_cast(Scintilla::IndicatorNumbers::Container); }), decorationList.end()); current = nullptr; SetView(); } template void DecorationList::DeleteAnyEmpty() { if (lengthDocument == 0) { decorationList.clear(); } else { decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(), [](const std::unique_ptr> &deco) noexcept { return deco->Empty(); }), decorationList.end()); } } template void DecorationList::SetView() { decorationView.clear(); for (const std::unique_ptr> &deco : decorationList) { decorationView.push_back(deco.get()); } } template int DecorationList::AllOnFor(Sci::Position position) const noexcept { int mask = 0; for (const std::unique_ptr> &deco : decorationList) { if (deco->rs.ValueAt(static_cast(position))) { if (deco->Indicator() < static_cast(Scintilla::IndicatorNumbers::Ime)) { mask |= 1u << deco->Indicator(); } } } return mask; } template int DecorationList::ValueAt(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { return deco->rs.ValueAt(static_cast(position)); } return 0; } template Sci::Position DecorationList::Start(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { return deco->rs.StartRun(static_cast(position)); } return 0; } template Sci::Position DecorationList::End(int indicator, Sci::Position position) noexcept { const Decoration *deco = DecorationFromIndicator(indicator); if (deco) { return deco->rs.EndRun(static_cast(position)); } return 0; } } namespace Scintilla::Internal { std::unique_ptr DecorationCreate(bool largeDocument, int indicator) { if (largeDocument) return std::make_unique>(indicator); else return std::make_unique>(indicator); } std::unique_ptr DecorationListCreate(bool largeDocument) { if (largeDocument) return std::make_unique>(); else return std::make_unique>(); } }