summaryrefslogtreecommitdiff
path: root/editeng
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2023-12-24 16:33:46 +0900
committerTomaž Vajngerl <quikee@gmail.com>2023-12-29 12:38:04 +0100
commit62aee46df1f8e4b863e2927e0b9d3b6bcd270008 (patch)
tree1c7bcd8bcb5023c2b0eab310d48117008ee285e9 /editeng
parentd9fc8e10aa24d5b34f0d86f84286ba13cee8c563 (diff)
editeng: move source of ContentNode & co. to own sourcefile
Move ContentNode, CharAttribList, ContentAttribs methods to own source class. Change-Id: Idd1371430ecaf36431de763dc4e3e3978ba60641 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161355 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'editeng')
-rw-r--r--editeng/Library_editeng.mk1
-rw-r--r--editeng/source/editeng/ContentNode.cxx1015
-rw-r--r--editeng/source/editeng/editdoc.cxx984
3 files changed, 1016 insertions, 984 deletions
diff --git a/editeng/Library_editeng.mk b/editeng/Library_editeng.mk
index 7f7f3581b62f..e0834b96f983 100644
--- a/editeng/Library_editeng.mk
+++ b/editeng/Library_editeng.mk
@@ -50,6 +50,7 @@ $(eval $(call gb_Library_add_exception_objects,editeng,\
endif
$(eval $(call gb_Library_add_exception_objects,editeng,\
+ editeng/source/editeng/ContentNode \
editeng/source/editeng/editattr \
editeng/source/editeng/editdata \
editeng/source/editeng/editdbg \
diff --git a/editeng/source/editeng/ContentNode.cxx b/editeng/source/editeng/ContentNode.cxx
new file mode 100644
index 000000000000..16d6a1ed808b
--- /dev/null
+++ b/editeng/source/editeng/ContentNode.cxx
@@ -0,0 +1,1015 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <ContentNode.hxx>
+#include <editeng/tstpitem.hxx>
+#include "impedit.hxx"
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <libxml/xmlwriter.h>
+
+#include <memory>
+#include <set>
+
+ContentNode::ContentNode( SfxItemPool& rPool )
+ : maContentAttribs( rPool )
+{
+}
+
+ContentNode::ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs )
+ : maString(rStr)
+ , maContentAttribs(rContentAttribs)
+{
+}
+
+ContentNode::~ContentNode()
+{
+}
+
+void ContentNode::ExpandAttribs( sal_Int32 nIndex, sal_Int32 nNew )
+{
+ if ( !nNew )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+#endif
+
+ // Since features are treated differently than normal character attributes,
+ // but can also affect the order of the start list. // In every if ..., in the next (n) opportunities due to bFeature or
+ // an existing special case, must (n-1) opportunities be provided with
+ // bResort. The most likely possibility receives no bResort, so that is
+ // not sorted anew when all attributes are the same.
+ bool bResort = false;
+ bool bExpandedEmptyAtIndexNull = false;
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
+ while ( pAttrib )
+ {
+ if ( pAttrib->GetEnd() >= nIndex )
+ {
+ // Move all attributes behind the insertion point...
+ if ( pAttrib->GetStart() > nIndex )
+ {
+ pAttrib->MoveForward( nNew );
+ }
+ // 0: Expand empty attribute, if at insertion point
+ else if ( pAttrib->IsEmpty() )
+ {
+ // Do not check Index, an empty one could only be there
+ // When later checking it anyhow:
+ // Special case: Start == 0; AbsLen == 1, nNew = 1
+ // => Expand, because of paragraph break!
+ // Start <= nIndex, End >= nIndex => Start=End=nIndex!
+// if ( pAttrib->GetStart() == nIndex )
+ pAttrib->Expand( nNew );
+ bResort = true;
+ if ( pAttrib->GetStart() == 0 )
+ bExpandedEmptyAtIndexNull = true;
+ }
+ // 1: Attribute starts before, goes to index ...
+ else if ( pAttrib->GetEnd() == nIndex ) // Start must be before
+ {
+ // Only expand when there is no feature
+ // and if not in exclude list!
+ // Otherwise, a UL will go on until a new ULDB, expanding both
+// if ( !pAttrib->IsFeature() && !rExclList.FindAttrib( pAttrib->Which() ) )
+ if ( !pAttrib->IsFeature() && !maCharAttribList.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
+ {
+ if ( !pAttrib->IsEdge() )
+ pAttrib->Expand( nNew );
+ }
+ else
+ bResort = true;
+ }
+ // 2: Attribute starts before, goes past the Index...
+ else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
+ {
+ DBG_ASSERT( !pAttrib->IsFeature(), "Large Feature?!" );
+ pAttrib->Expand( nNew );
+ }
+ // 3: Attribute starts on index...
+ else if ( pAttrib->GetStart() == nIndex )
+ {
+ if ( pAttrib->IsFeature() )
+ {
+ pAttrib->MoveForward( nNew );
+ bResort = true;
+ }
+ else
+ {
+ bool bExpand = false;
+ if ( nIndex == 0 )
+ {
+ bExpand = true;
+ if( bExpandedEmptyAtIndexNull )
+ {
+ // Check if this kind of attribute was empty and expanded here...
+ sal_uInt16 nW = pAttrib->GetItem()->Which();
+ for ( std::size_t nA = 0; nA < nAttr; nA++ )
+ {
+ const EditCharAttrib& r = *maCharAttribList.GetAttribs()[nA];
+ if ( ( r.GetStart() == 0 ) && ( r.GetItem()->Which() == nW ) )
+ {
+ bExpand = false;
+ break;
+ }
+ }
+
+ }
+ }
+ if ( bExpand )
+ {
+ pAttrib->Expand( nNew );
+ bResort = true;
+ }
+ else
+ {
+ pAttrib->MoveForward( nNew );
+ }
+ }
+ }
+ }
+
+ if ( pAttrib->IsEdge() )
+ pAttrib->SetEdge(false);
+
+ DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
+
+ DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribute distorted!" );
+ DBG_ASSERT( ( pAttrib->GetEnd() <= Len() ), "Expand: Attribute larger than paragraph!" );
+ if ( pAttrib->IsEmpty() )
+ {
+ OSL_FAIL( "Empty Attribute after ExpandAttribs?" );
+ bResort = true;
+ rAttribs.erase(rAttribs.begin()+nAttr);
+ }
+ else
+ {
+ ++nAttr;
+ }
+ pAttrib = GetAttrib(rAttribs, nAttr);
+ }
+
+ if ( bResort )
+ maCharAttribList.ResortAttribs();
+
+ if (mpWrongList)
+ {
+ bool bSep = ( maString[ nIndex ] == ' ' ) || IsFeature( nIndex );
+ mpWrongList->TextInserted( nIndex, nNew, bSep );
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+#endif
+}
+
+void ContentNode::CollapseAttribs( sal_Int32 nIndex, sal_Int32 nDeleted )
+{
+ if ( !nDeleted )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+#endif
+
+ // Since features are treated differently than normal character attributes,
+ // but can also affect the order of the start list
+ bool bResort = false;
+ sal_Int32 nEndChanges = nIndex+nDeleted;
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
+ while ( pAttrib )
+ {
+ bool bDelAttr = false;
+ if ( pAttrib->GetEnd() >= nIndex )
+ {
+ // Move all Attribute behind the insert point...
+ if ( pAttrib->GetStart() >= nEndChanges )
+ {
+ pAttrib->MoveBackward( nDeleted );
+ }
+ // 1. Delete Internal attributes...
+ else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
+ {
+ // Special case: Attribute covers the area exactly
+ // => keep as empty Attribute.
+ if ( !pAttrib->IsFeature() && ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
+ {
+ pAttrib->GetEnd() = nIndex; // empty
+ bResort = true;
+ }
+ else
+ bDelAttr = true;
+ }
+ // 2. Attribute starts earlier, ends inside or behind it ...
+ else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
+ {
+ DBG_ASSERT( !pAttrib->IsFeature(), "Collapsing Feature!" );
+ if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
+ pAttrib->GetEnd() = nIndex;
+ else
+ pAttrib->Collaps( nDeleted ); // ends behind
+ }
+ // 3. Attribute starts inside, ending behind ...
+ else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
+ {
+ // Features not allowed to expand!
+ if ( pAttrib->IsFeature() )
+ {
+ pAttrib->MoveBackward( nDeleted );
+ bResort = true;
+ }
+ else
+ {
+ pAttrib->GetStart() = nEndChanges;
+ pAttrib->MoveBackward( nDeleted );
+ }
+ }
+ }
+ DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
+
+ DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collapse: Attribute distorted!" );
+ DBG_ASSERT( ( pAttrib->GetEnd() <= Len()) || bDelAttr, "Collapse: Attribute larger than paragraph!" );
+ if ( bDelAttr )
+ {
+ bResort = true;
+ rAttribs.erase(rAttribs.begin()+nAttr);
+ }
+ else
+ {
+ if ( pAttrib->IsEmpty() )
+ maCharAttribList.SetHasEmptyAttribs(true);
+ nAttr++;
+ }
+
+ pAttrib = GetAttrib(rAttribs, nAttr);
+ }
+
+ if ( bResort )
+ maCharAttribList.ResortAttribs();
+
+ if (mpWrongList)
+ mpWrongList->TextDeleted(nIndex, nDeleted);
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+#endif
+}
+
+void ContentNode::CopyAndCutAttribs( ContentNode* pPrevNode, SfxItemPool& rPool, bool bKeepEndingAttribs )
+{
+ assert(pPrevNode);
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+ CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
+#endif
+
+ sal_Int32 nCut = pPrevNode->Len();
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rPrevAttribs = pPrevNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rPrevAttribs, nAttr);
+ while ( pAttrib )
+ {
+ if ( pAttrib->GetEnd() < nCut )
+ {
+ // remain unchanged...
+ nAttr++;
+ }
+ else if ( pAttrib->GetEnd() == nCut )
+ {
+ // must be copied as an empty attributes.
+ if ( bKeepEndingAttribs && !pAttrib->IsFeature() && !maCharAttribList.FindAttrib( pAttrib->GetItem()->Which(), 0 ) )
+ {
+ EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, 0 );
+ assert(pNewAttrib);
+ maCharAttribList.InsertAttrib( pNewAttrib );
+ }
+ nAttr++;
+ }
+ else if ( pAttrib->IsInside( nCut ) || ( !nCut && !pAttrib->GetStart() && !pAttrib->IsFeature() ) )
+ {
+ // If cut is done right at the front then the attribute must be
+ // kept! Has to be copied and changed.
+ EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, pAttrib->GetEnd()-nCut );
+ assert(pNewAttrib);
+ maCharAttribList.InsertAttrib( pNewAttrib );
+ pAttrib->GetEnd() = nCut;
+ nAttr++;
+ }
+ else
+ {
+ // Move all attributes in the current node (this)
+ CharAttribList::AttribsType::iterator it = rPrevAttribs.begin() + nAttr;
+ maCharAttribList.InsertAttrib(it->release());
+ rPrevAttribs.erase(it);
+ pAttrib->MoveBackward( nCut );
+ }
+ pAttrib = GetAttrib(rPrevAttribs, nAttr);
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+ CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
+#endif
+}
+
+void ContentNode::AppendAttribs( ContentNode* pNextNode )
+{
+ assert(pNextNode);
+
+ sal_Int32 nNewStart = maString.getLength();
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+ CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
+#endif
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rNextAttribs = pNextNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rNextAttribs, nAttr);
+ while ( pAttrib )
+ {
+ // Move all attributes in the current node (this)
+ bool bMelted = false;
+ if ( ( pAttrib->GetStart() == 0 ) && ( !pAttrib->IsFeature() ) )
+ {
+ // Attributes can possibly be summarized as:
+ std::size_t nTmpAttr = 0;
+ EditCharAttrib* pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
+ while ( !bMelted && pTmpAttrib )
+ {
+ ++nTmpAttr;
+ if ( pTmpAttrib->GetEnd() == nNewStart )
+ {
+ if (pTmpAttrib->Which() == pAttrib->Which())
+ {
+ // prevent adding 2 0-length attributes at same position
+ if ((*(pTmpAttrib->GetItem()) == *(pAttrib->GetItem()))
+ || (0 == pAttrib->GetLen()))
+ {
+ pTmpAttrib->GetEnd() =
+ pTmpAttrib->GetEnd() + pAttrib->GetLen();
+ rNextAttribs.erase(rNextAttribs.begin()+nAttr);
+ // Unsubscribe from the pool?!
+ bMelted = true;
+ }
+ else if (0 == pTmpAttrib->GetLen())
+ {
+ --nTmpAttr; // to cancel earlier increment...
+ maCharAttribList.Remove(nTmpAttr);
+ }
+ }
+ }
+ pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
+ }
+ }
+
+ if ( !bMelted )
+ {
+ pAttrib->GetStart() = pAttrib->GetStart() + nNewStart;
+ pAttrib->GetEnd() = pAttrib->GetEnd() + nNewStart;
+ CharAttribList::AttribsType::iterator it = rNextAttribs.begin() + nAttr;
+ maCharAttribList.InsertAttrib(it->release());
+ rNextAttribs.erase(it);
+ }
+ pAttrib = GetAttrib(rNextAttribs, nAttr);
+ }
+ // For the Attributes that just moved over:
+ rNextAttribs.clear();
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(maCharAttribList);
+ CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
+#endif
+}
+
+void ContentNode::CreateDefFont()
+{
+ // First use the information from the style ...
+ SfxStyleSheet* pS = maContentAttribs.GetStyleSheet();
+ if ( pS )
+ CreateFont( GetCharAttribs().GetDefFont(), pS->GetItemSet() );
+
+ // ... then iron out the hard paragraph formatting...
+ CreateFont( GetCharAttribs().GetDefFont(),
+ GetContentAttribs().GetItems(), pS == nullptr );
+}
+
+void ContentNode::SetStyleSheet( SfxStyleSheet* pS, const SvxFont& rFontFromStyle )
+{
+ maContentAttribs.SetStyleSheet( pS );
+
+
+ // First use the information from the style ...
+ GetCharAttribs().GetDefFont() = rFontFromStyle;
+ // ... then iron out the hard paragraph formatting...
+ CreateFont( GetCharAttribs().GetDefFont(),
+ GetContentAttribs().GetItems(), pS == nullptr );
+}
+
+void ContentNode::SetStyleSheet( SfxStyleSheet* pS, bool bRecalcFont )
+{
+ maContentAttribs.SetStyleSheet( pS );
+ if ( bRecalcFont )
+ CreateDefFont();
+}
+
+bool ContentNode::IsFeature( sal_Int32 nPos ) const
+{
+ return maString[nPos] == CH_FEATURE;
+}
+
+sal_Int32 ContentNode::Len() const
+{
+ return maString.getLength();
+}
+
+sal_Int32 ContentNode::GetExpandedLen() const
+{
+ sal_Int32 nLen = maString.getLength();
+
+ // Fields can be longer than the placeholder in the Node
+ const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
+ for (sal_Int32 nAttr = rAttrs.size(); nAttr; )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[--nAttr];
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
+ --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0
+ }
+ }
+
+ return nLen;
+}
+
+OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos) const
+{
+ if ( nEndPos < 0 || nEndPos > Len() )
+ nEndPos = Len();
+
+ DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );
+
+ sal_Int32 nIndex = nStartPos;
+ OUStringBuffer aStr(256);
+ const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex );
+ while ( nIndex < nEndPos )
+ {
+ sal_Int32 nEnd = nEndPos;
+ if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
+ nEnd = pNextFeature->GetStart();
+ else
+ pNextFeature = nullptr; // Feature does not interest the below
+
+ DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
+ //!! beware of sub string length of -1
+ if (nEnd > nIndex)
+ aStr.append( GetString().subView(nIndex, nEnd - nIndex) );
+
+ if ( pNextFeature )
+ {
+ switch ( pNextFeature->GetItem()->Which() )
+ {
+ case EE_FEATURE_TAB: aStr.append( "\t" );
+ break;
+ case EE_FEATURE_LINEBR: aStr.append( "\x0A" );
+ break;
+ case EE_FEATURE_FIELD:
+ aStr.append( static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue() );
+ break;
+ default: OSL_FAIL( "What feature?" );
+ }
+ pNextFeature = GetCharAttribs().FindFeature( ++nEnd );
+ }
+ nIndex = nEnd;
+ }
+ return aStr.makeStringAndClear();
+}
+
+void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart )
+{
+ sal_Int32 nOffset = 0;
+
+ const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
+ for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[nAttr];
+ assert (!(nAttr < rAttrs.size() - 1) ||
+ rAttrs[nAttr]->GetStart() <= rAttrs[nAttr + 1]->GetStart());
+
+ nOffset = rAttr.GetStart();
+
+ if (nOffset >= rPos) // happens after the position
+ return;
+
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ sal_Int32 nChunk = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
+ nChunk--; // Character representing the field in the string
+
+ if (nOffset + nChunk >= rPos) // we're inside the field
+ {
+ if (bBiasStart)
+ rPos = rAttr.GetStart();
+ else
+ rPos = rAttr.GetEnd();
+ return;
+ }
+ // Adjust for the position
+ rPos -= nChunk;
+ }
+ }
+ assert (rPos <= Len());
+}
+
+/*
+ * Fields are represented by a single character in the underlying string
+ * and/or selection, however, they can be expanded to the full value of
+ * the field. When we're dealing with selection / offsets however we need
+ * to deal in character positions inside the real (unexpanded) string.
+ * This method maps us back to character offsets.
+ */
+void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos )
+{
+ UnExpandPosition( rStartPos, true );
+ UnExpandPosition( rEndPos, false );
+}
+
+void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c)
+{
+ maString = maString.replaceAt(nPos, 1, rtl::OUStringChar(c));
+}
+
+void ContentNode::Insert(std::u16string_view rStr, sal_Int32 nPos)
+{
+ maString = maString.replaceAt(nPos, 0, rStr);
+}
+
+void ContentNode::Append(std::u16string_view rStr)
+{
+ maString += rStr;
+}
+
+void ContentNode::Erase(sal_Int32 nPos)
+{
+ maString = maString.copy(0, nPos);
+}
+
+void ContentNode::Erase(sal_Int32 nPos, sal_Int32 nCount)
+{
+ maString = maString.replaceAt(nPos, nCount, u"");
+}
+
+OUString ContentNode::Copy(sal_Int32 nPos) const
+{
+ return maString.copy(nPos);
+}
+
+OUString ContentNode::Copy(sal_Int32 nPos, sal_Int32 nCount) const
+{
+ return maString.copy(nPos, nCount);
+}
+
+sal_Unicode ContentNode::GetChar(sal_Int32 nPos) const
+{
+ return maString[nPos];
+}
+
+void ContentNode::EnsureWrongList()
+{
+ if (!mpWrongList)
+ CreateWrongList();
+}
+
+WrongList* ContentNode::GetWrongList()
+{
+ return mpWrongList.get();
+}
+
+const WrongList* ContentNode::GetWrongList() const
+{
+ return mpWrongList.get();
+}
+
+void ContentNode::SetWrongList( WrongList* p )
+{
+ mpWrongList.reset(p);
+}
+
+void ContentNode::CreateWrongList()
+{
+ SAL_WARN_IF( mpWrongList && !mpWrongList->empty(), "editeng", "WrongList already exist!");
+ if (!mpWrongList || !mpWrongList->empty())
+ mpWrongList.reset(new WrongList);
+}
+
+void ContentNode::DestroyWrongList()
+{
+ mpWrongList.reset();
+}
+
+void ContentNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentNode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maString"), BAD_CAST(maString.toUtf8().getStr()));
+ maContentAttribs.dumpAsXml(pWriter);
+ maCharAttribList.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void ContentNode::checkAndDeleteEmptyAttribs() const
+{
+ // Delete empty attributes, but only if paragraph is not empty!
+ if (GetCharAttribs().HasEmptyAttribs() && Len())
+ {
+ const_cast<ContentNode*>(this)->GetCharAttribs().DeleteEmptyAttribs();
+ }
+}
+
+ContentAttribs::ContentAttribs( SfxItemPool& rPool )
+: mpStyle(nullptr)
+, maAttribSet( rPool )
+{
+}
+
+
+SvxTabStop ContentAttribs::FindTabStop( sal_Int32 nCurPos, sal_uInt16 nDefTab )
+{
+ const SvxTabStopItem& rTabs = GetItem( EE_PARA_TABS );
+ for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
+ {
+ const SvxTabStop& rTab = rTabs[i];
+ if ( rTab.GetTabPos() > nCurPos )
+ return rTab;
+ }
+
+ // if there's a default tab size defined for this item use that instead
+ if (rTabs.GetDefaultDistance())
+ nDefTab = rTabs.GetDefaultDistance();
+
+ // Determine DefTab ...
+ SvxTabStop aTabStop;
+ const sal_Int32 x = nCurPos / nDefTab + 1;
+ aTabStop.GetTabPos() = nDefTab * x;
+ return aTabStop;
+}
+
+void ContentAttribs::SetStyleSheet( SfxStyleSheet* pS )
+{
+ bool bStyleChanged = ( mpStyle != pS );
+ mpStyle = pS;
+ // Only when other style sheet, not when current style sheet modified
+ if ( !(mpStyle && bStyleChanged) )
+ return;
+
+ // Selectively remove the attributes from the paragraph formatting
+ // which are specified in the style, so that the attributes of the
+ // style can have an affect.
+ const SfxItemSet& rStyleAttribs = mpStyle->GetItemSet();
+ for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ // Don't change bullet on/off
+ if ( ( nWhich != EE_PARA_BULLETSTATE ) && ( rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET ) )
+ maAttribSet.ClearItem( nWhich );
+ }
+}
+
+const SfxPoolItem& ContentAttribs::GetItem( sal_uInt16 nWhich ) const
+{
+ // Hard paragraph attributes take precedence!
+ const SfxItemSet* pTakeFrom = &maAttribSet;
+ if ( mpStyle && ( maAttribSet.GetItemState( nWhich, false ) != SfxItemState::SET ) )
+ pTakeFrom = &mpStyle->GetItemSet();
+
+ return pTakeFrom->Get( nWhich );
+}
+
+bool ContentAttribs::HasItem( sal_uInt16 nWhich ) const
+{
+ bool bHasItem = false;
+ if ( maAttribSet.GetItemState( nWhich, false ) == SfxItemState::SET )
+ bHasItem = true;
+ else if ( mpStyle && mpStyle->GetItemSet().GetItemState( nWhich ) == SfxItemState::SET )
+ bHasItem = true;
+
+ return bHasItem;
+}
+
+void ContentAttribs::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentAttribs"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("style"), "%s", mpStyle->GetName().toUtf8().getStr());
+ maAttribSet.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+namespace {
+
+struct LessByStart
+{
+ bool operator() (const std::unique_ptr<EditCharAttrib>& left, const std::unique_ptr<EditCharAttrib>& right) const
+ {
+ return left->GetStart() < right->GetStart();
+ }
+};
+
+}
+
+CharAttribList::CharAttribList()
+: mbHasEmptyAttribs(false)
+{
+}
+
+CharAttribList::~CharAttribList()
+{
+}
+
+void CharAttribList::InsertAttrib( EditCharAttrib* pAttrib )
+{
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// optimize: binary search? !
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ // Maybe just simply iterate backwards:
+ // The most common and critical case: Attributes are already sorted
+ // (InsertTextObject!) binary search would not be optimal here.
+ // => Would bring something!
+
+ const sal_Int32 nStart = pAttrib->GetStart(); // may be better for Comp.Opt.
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+
+ if ( pAttrib->IsEmpty() )
+ mbHasEmptyAttribs = true;
+
+ bool bInsert(true);
+ for (sal_Int32 i = 0, n = maAttribs.size(); i < n; ++i)
+ {
+ const EditCharAttrib& rCurAttrib = *maAttribs[i];
+ if (rCurAttrib.GetStart() > nStart)
+ {
+ maAttribs.insert(maAttribs.begin()+i, std::unique_ptr<EditCharAttrib>(pAttrib));
+ bInsert = false;
+ break;
+ }
+ }
+
+ if (bInsert) maAttribs.push_back(std::unique_ptr<EditCharAttrib>(pAttrib));
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+void CharAttribList::ResortAttribs()
+{
+ std::sort(maAttribs.begin(), maAttribs.end(), LessByStart());
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+void CharAttribList::OptimizeRanges()
+{
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+ for (sal_Int32 i = 0; i < static_cast<sal_Int32>(maAttribs.size()); ++i)
+ {
+ EditCharAttrib& rAttr = *maAttribs[i];
+ for (sal_Int32 nNext = i+1; nNext < static_cast<sal_Int32>(maAttribs.size()); ++nNext)
+ {
+ EditCharAttrib& rNext = *maAttribs[nNext];
+ if (!rAttr.IsFeature() && rNext.GetStart() == rAttr.GetEnd() && rNext.Which() == rAttr.Which())
+ {
+ if (*rNext.GetItem() == *rAttr.GetItem())
+ {
+ rAttr.GetEnd() = rNext.GetEnd();
+ maAttribs.erase(maAttribs.begin()+nNext);
+ }
+ break; // only 1 attr with same which can start here.
+ }
+ else if (rNext.GetStart() > rAttr.GetEnd())
+ {
+ break;
+ }
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+sal_Int32 CharAttribList::Count() const
+{
+ return maAttribs.size();
+}
+
+const EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) const
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::const_reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
+ [&nWhich, &nPos](const AttribsType::value_type& rxAttr) {
+ return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
+ if (it != maAttribs.rend())
+ {
+ const EditCharAttrib& rAttr = **it;
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
+ [&nWhich, &nPos](AttribsType::value_type& rxAttr) {
+ return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
+ if (it != maAttribs.rend())
+ {
+ EditCharAttrib& rAttr = **it;
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+const EditCharAttrib* CharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_Int32 nFromPos ) const
+{
+ assert(nWhich);
+ for (auto const& attrib : maAttribs)
+ {
+ const EditCharAttrib& rAttr = *attrib;
+ if (rAttr.GetStart() >= nFromPos && rAttr.Which() == nWhich)
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+bool CharAttribList::HasAttrib( sal_Int32 nStartPos, sal_Int32 nEndPos ) const
+{
+ return std::any_of(maAttribs.rbegin(), maAttribs.rend(),
+ [&nStartPos, &nEndPos](const AttribsType::value_type& rxAttr) {
+ return rxAttr->GetStart() < nEndPos && rxAttr->GetEnd() > nStartPos; });
+}
+
+
+namespace {
+
+class FindByAddress
+{
+ const EditCharAttrib* mpAttr;
+public:
+ explicit FindByAddress(const EditCharAttrib* p) : mpAttr(p) {}
+ bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
+ {
+ return r.get() == mpAttr;
+ }
+};
+
+}
+
+void CharAttribList::Remove(const EditCharAttrib* p)
+{
+ AttribsType::iterator it = std::find_if(maAttribs.begin(), maAttribs.end(), FindByAddress(p));
+ if (it != maAttribs.end())
+ maAttribs.erase(it);
+}
+
+void CharAttribList::Remove(sal_Int32 nPos)
+{
+ if (nPos >= static_cast<sal_Int32>(maAttribs.size()))
+ return;
+
+ maAttribs.erase(maAttribs.begin()+nPos);
+}
+
+void CharAttribList::SetHasEmptyAttribs(bool b)
+{
+ mbHasEmptyAttribs = b;
+}
+
+bool CharAttribList::HasBoundingAttrib( sal_Int32 nBound ) const
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::const_reverse_iterator it = maAttribs.rbegin(), itEnd = maAttribs.rend();
+ for (; it != itEnd; ++it)
+ {
+ const EditCharAttrib& rAttr = **it;
+ if (rAttr.GetEnd() < nBound)
+ return false;
+
+ if (rAttr.GetStart() == nBound || rAttr.GetEnd() == nBound)
+ return true;
+ }
+ return false;
+}
+
+EditCharAttrib* CharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
+{
+ if ( !mbHasEmptyAttribs )
+ return nullptr;
+
+ for (const std::unique_ptr<EditCharAttrib>& rAttr : maAttribs)
+ {
+ if (rAttr->GetStart() == nPos && rAttr->GetEnd() == nPos && rAttr->Which() == nWhich)
+ return rAttr.get();
+ }
+ return nullptr;
+}
+
+namespace {
+
+class FindByStartPos
+{
+ sal_Int32 mnPos;
+public:
+ explicit FindByStartPos(sal_Int32 nPos) : mnPos(nPos) {}
+ bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
+ {
+ return r->GetStart() >= mnPos;
+ }
+};
+
+}
+
+const EditCharAttrib* CharAttribList::FindFeature( sal_Int32 nPos ) const
+{
+ // First, find the first attribute that starts at or after specified position.
+ AttribsType::const_iterator it =
+ std::find_if(maAttribs.begin(), maAttribs.end(), FindByStartPos(nPos));
+
+ if (it == maAttribs.end())
+ // All attributes are before the specified position.
+ return nullptr;
+
+ // And find the first attribute with feature.
+ it = std::find_if(it, maAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsFeature(); } );
+ return it == maAttribs.end() ? nullptr : it->get();
+}
+
+void CharAttribList::DeleteEmptyAttribs()
+{
+ std::erase_if(maAttribs, [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsEmpty(); } );
+ mbHasEmptyAttribs = false;
+}
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void CharAttribList::DbgCheckAttribs(CharAttribList const& rAttribs)
+{
+ std::set<std::pair<sal_Int32, sal_uInt16>> zero_set;
+ for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs.maAttribs)
+ {
+ assert(rAttr->GetStart() <= rAttr->GetEnd());
+ assert(!rAttr->IsFeature() || rAttr->GetLen() == 1);
+ if (0 == rAttr->GetLen())
+ {
+ // not sure if 0-length attributes allowed at all in non-empty para?
+ assert(zero_set.insert(std::make_pair(rAttr->GetStart(), rAttr->Which())).second && "duplicate 0-length attribute detected");
+ }
+ }
+ CheckOrderedList(rAttribs.GetAttribs());
+}
+#endif
+
+void CharAttribList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("CharAttribList"));
+ for (auto const & i : maAttribs) {
+ i->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx
index 8bac8f2cf67b..0d6ce0f315c4 100644
--- a/editeng/source/editeng/editdoc.cxx
+++ b/editeng/source/editeng/editdoc.cxx
@@ -1164,706 +1164,6 @@ bool operator != ( const EditPaM& r1, const EditPaM& r2 )
return !( r1 == r2 );
}
-ContentNode::ContentNode( SfxItemPool& rPool )
- : maContentAttribs( rPool )
-{
-}
-
-ContentNode::ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs )
- : maString(rStr)
- , maContentAttribs(rContentAttribs)
-{
-}
-
-ContentNode::~ContentNode()
-{
-}
-
-void ContentNode::ExpandAttribs( sal_Int32 nIndex, sal_Int32 nNew )
-{
- if ( !nNew )
- return;
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
-#endif
-
- // Since features are treated differently than normal character attributes,
- // but can also affect the order of the start list. // In every if ..., in the next (n) opportunities due to bFeature or
- // an existing special case, must (n-1) opportunities be provided with
- // bResort. The most likely possibility receives no bResort, so that is
- // not sorted anew when all attributes are the same.
- bool bResort = false;
- bool bExpandedEmptyAtIndexNull = false;
-
- std::size_t nAttr = 0;
- CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
- EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
- while ( pAttrib )
- {
- if ( pAttrib->GetEnd() >= nIndex )
- {
- // Move all attributes behind the insertion point...
- if ( pAttrib->GetStart() > nIndex )
- {
- pAttrib->MoveForward( nNew );
- }
- // 0: Expand empty attribute, if at insertion point
- else if ( pAttrib->IsEmpty() )
- {
- // Do not check Index, an empty one could only be there
- // When later checking it anyhow:
- // Special case: Start == 0; AbsLen == 1, nNew = 1
- // => Expand, because of paragraph break!
- // Start <= nIndex, End >= nIndex => Start=End=nIndex!
-// if ( pAttrib->GetStart() == nIndex )
- pAttrib->Expand( nNew );
- bResort = true;
- if ( pAttrib->GetStart() == 0 )
- bExpandedEmptyAtIndexNull = true;
- }
- // 1: Attribute starts before, goes to index ...
- else if ( pAttrib->GetEnd() == nIndex ) // Start must be before
- {
- // Only expand when there is no feature
- // and if not in exclude list!
- // Otherwise, a UL will go on until a new ULDB, expanding both
-// if ( !pAttrib->IsFeature() && !rExclList.FindAttrib( pAttrib->Which() ) )
- if ( !pAttrib->IsFeature() && !maCharAttribList.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
- {
- if ( !pAttrib->IsEdge() )
- pAttrib->Expand( nNew );
- }
- else
- bResort = true;
- }
- // 2: Attribute starts before, goes past the Index...
- else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
- {
- DBG_ASSERT( !pAttrib->IsFeature(), "Large Feature?!" );
- pAttrib->Expand( nNew );
- }
- // 3: Attribute starts on index...
- else if ( pAttrib->GetStart() == nIndex )
- {
- if ( pAttrib->IsFeature() )
- {
- pAttrib->MoveForward( nNew );
- bResort = true;
- }
- else
- {
- bool bExpand = false;
- if ( nIndex == 0 )
- {
- bExpand = true;
- if( bExpandedEmptyAtIndexNull )
- {
- // Check if this kind of attribute was empty and expanded here...
- sal_uInt16 nW = pAttrib->GetItem()->Which();
- for ( std::size_t nA = 0; nA < nAttr; nA++ )
- {
- const EditCharAttrib& r = *maCharAttribList.GetAttribs()[nA];
- if ( ( r.GetStart() == 0 ) && ( r.GetItem()->Which() == nW ) )
- {
- bExpand = false;
- break;
- }
- }
-
- }
- }
- if ( bExpand )
- {
- pAttrib->Expand( nNew );
- bResort = true;
- }
- else
- {
- pAttrib->MoveForward( nNew );
- }
- }
- }
- }
-
- if ( pAttrib->IsEdge() )
- pAttrib->SetEdge(false);
-
- DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
-
- DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribute distorted!" );
- DBG_ASSERT( ( pAttrib->GetEnd() <= Len() ), "Expand: Attribute larger than paragraph!" );
- if ( pAttrib->IsEmpty() )
- {
- OSL_FAIL( "Empty Attribute after ExpandAttribs?" );
- bResort = true;
- rAttribs.erase(rAttribs.begin()+nAttr);
- }
- else
- {
- ++nAttr;
- }
- pAttrib = GetAttrib(rAttribs, nAttr);
- }
-
- if ( bResort )
- maCharAttribList.ResortAttribs();
-
- if (mpWrongList)
- {
- bool bSep = ( maString[ nIndex ] == ' ' ) || IsFeature( nIndex );
- mpWrongList->TextInserted( nIndex, nNew, bSep );
- }
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
-#endif
-}
-
-void ContentNode::CollapseAttribs( sal_Int32 nIndex, sal_Int32 nDeleted )
-{
- if ( !nDeleted )
- return;
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
-#endif
-
- // Since features are treated differently than normal character attributes,
- // but can also affect the order of the start list
- bool bResort = false;
- sal_Int32 nEndChanges = nIndex+nDeleted;
-
- std::size_t nAttr = 0;
- CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
- EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
- while ( pAttrib )
- {
- bool bDelAttr = false;
- if ( pAttrib->GetEnd() >= nIndex )
- {
- // Move all Attribute behind the insert point...
- if ( pAttrib->GetStart() >= nEndChanges )
- {
- pAttrib->MoveBackward( nDeleted );
- }
- // 1. Delete Internal attributes...
- else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
- {
- // Special case: Attribute covers the area exactly
- // => keep as empty Attribute.
- if ( !pAttrib->IsFeature() && ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
- {
- pAttrib->GetEnd() = nIndex; // empty
- bResort = true;
- }
- else
- bDelAttr = true;
- }
- // 2. Attribute starts earlier, ends inside or behind it ...
- else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
- {
- DBG_ASSERT( !pAttrib->IsFeature(), "Collapsing Feature!" );
- if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
- pAttrib->GetEnd() = nIndex;
- else
- pAttrib->Collaps( nDeleted ); // ends behind
- }
- // 3. Attribute starts inside, ending behind ...
- else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
- {
- // Features not allowed to expand!
- if ( pAttrib->IsFeature() )
- {
- pAttrib->MoveBackward( nDeleted );
- bResort = true;
- }
- else
- {
- pAttrib->GetStart() = nEndChanges;
- pAttrib->MoveBackward( nDeleted );
- }
- }
- }
- DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
-
- DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collapse: Attribute distorted!" );
- DBG_ASSERT( ( pAttrib->GetEnd() <= Len()) || bDelAttr, "Collapse: Attribute larger than paragraph!" );
- if ( bDelAttr )
- {
- bResort = true;
- rAttribs.erase(rAttribs.begin()+nAttr);
- }
- else
- {
- if ( pAttrib->IsEmpty() )
- maCharAttribList.SetHasEmptyAttribs(true);
- nAttr++;
- }
-
- pAttrib = GetAttrib(rAttribs, nAttr);
- }
-
- if ( bResort )
- maCharAttribList.ResortAttribs();
-
- if (mpWrongList)
- mpWrongList->TextDeleted(nIndex, nDeleted);
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
-#endif
-}
-
-void ContentNode::CopyAndCutAttribs( ContentNode* pPrevNode, SfxItemPool& rPool, bool bKeepEndingAttribs )
-{
- assert(pPrevNode);
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
- CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
-#endif
-
- sal_Int32 nCut = pPrevNode->Len();
-
- std::size_t nAttr = 0;
- CharAttribList::AttribsType& rPrevAttribs = pPrevNode->GetCharAttribs().GetAttribs();
- EditCharAttrib* pAttrib = GetAttrib(rPrevAttribs, nAttr);
- while ( pAttrib )
- {
- if ( pAttrib->GetEnd() < nCut )
- {
- // remain unchanged...
- nAttr++;
- }
- else if ( pAttrib->GetEnd() == nCut )
- {
- // must be copied as an empty attributes.
- if ( bKeepEndingAttribs && !pAttrib->IsFeature() && !maCharAttribList.FindAttrib( pAttrib->GetItem()->Which(), 0 ) )
- {
- EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, 0 );
- assert(pNewAttrib);
- maCharAttribList.InsertAttrib( pNewAttrib );
- }
- nAttr++;
- }
- else if ( pAttrib->IsInside( nCut ) || ( !nCut && !pAttrib->GetStart() && !pAttrib->IsFeature() ) )
- {
- // If cut is done right at the front then the attribute must be
- // kept! Has to be copied and changed.
- EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, pAttrib->GetEnd()-nCut );
- assert(pNewAttrib);
- maCharAttribList.InsertAttrib( pNewAttrib );
- pAttrib->GetEnd() = nCut;
- nAttr++;
- }
- else
- {
- // Move all attributes in the current node (this)
- CharAttribList::AttribsType::iterator it = rPrevAttribs.begin() + nAttr;
- maCharAttribList.InsertAttrib(it->release());
- rPrevAttribs.erase(it);
- pAttrib->MoveBackward( nCut );
- }
- pAttrib = GetAttrib(rPrevAttribs, nAttr);
- }
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
- CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
-#endif
-}
-
-void ContentNode::AppendAttribs( ContentNode* pNextNode )
-{
- assert(pNextNode);
-
- sal_Int32 nNewStart = maString.getLength();
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
- CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
-#endif
-
- std::size_t nAttr = 0;
- CharAttribList::AttribsType& rNextAttribs = pNextNode->GetCharAttribs().GetAttribs();
- EditCharAttrib* pAttrib = GetAttrib(rNextAttribs, nAttr);
- while ( pAttrib )
- {
- // Move all attributes in the current node (this)
- bool bMelted = false;
- if ( ( pAttrib->GetStart() == 0 ) && ( !pAttrib->IsFeature() ) )
- {
- // Attributes can possibly be summarized as:
- std::size_t nTmpAttr = 0;
- EditCharAttrib* pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
- while ( !bMelted && pTmpAttrib )
- {
- ++nTmpAttr;
- if ( pTmpAttrib->GetEnd() == nNewStart )
- {
- if (pTmpAttrib->Which() == pAttrib->Which())
- {
- // prevent adding 2 0-length attributes at same position
- if ((*(pTmpAttrib->GetItem()) == *(pAttrib->GetItem()))
- || (0 == pAttrib->GetLen()))
- {
- pTmpAttrib->GetEnd() =
- pTmpAttrib->GetEnd() + pAttrib->GetLen();
- rNextAttribs.erase(rNextAttribs.begin()+nAttr);
- // Unsubscribe from the pool?!
- bMelted = true;
- }
- else if (0 == pTmpAttrib->GetLen())
- {
- --nTmpAttr; // to cancel earlier increment...
- maCharAttribList.Remove(nTmpAttr);
- }
- }
- }
- pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
- }
- }
-
- if ( !bMelted )
- {
- pAttrib->GetStart() = pAttrib->GetStart() + nNewStart;
- pAttrib->GetEnd() = pAttrib->GetEnd() + nNewStart;
- CharAttribList::AttribsType::iterator it = rNextAttribs.begin() + nAttr;
- maCharAttribList.InsertAttrib(it->release());
- rNextAttribs.erase(it);
- }
- pAttrib = GetAttrib(rNextAttribs, nAttr);
- }
- // For the Attributes that just moved over:
- rNextAttribs.clear();
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(maCharAttribList);
- CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
-#endif
-}
-
-void ContentNode::CreateDefFont()
-{
- // First use the information from the style ...
- SfxStyleSheet* pS = maContentAttribs.GetStyleSheet();
- if ( pS )
- CreateFont( GetCharAttribs().GetDefFont(), pS->GetItemSet() );
-
- // ... then iron out the hard paragraph formatting...
- CreateFont( GetCharAttribs().GetDefFont(),
- GetContentAttribs().GetItems(), pS == nullptr );
-}
-
-void ContentNode::SetStyleSheet( SfxStyleSheet* pS, const SvxFont& rFontFromStyle )
-{
- maContentAttribs.SetStyleSheet( pS );
-
-
- // First use the information from the style ...
- GetCharAttribs().GetDefFont() = rFontFromStyle;
- // ... then iron out the hard paragraph formatting...
- CreateFont( GetCharAttribs().GetDefFont(),
- GetContentAttribs().GetItems(), pS == nullptr );
-}
-
-void ContentNode::SetStyleSheet( SfxStyleSheet* pS, bool bRecalcFont )
-{
- maContentAttribs.SetStyleSheet( pS );
- if ( bRecalcFont )
- CreateDefFont();
-}
-
-bool ContentNode::IsFeature( sal_Int32 nPos ) const
-{
- return maString[nPos] == CH_FEATURE;
-}
-
-sal_Int32 ContentNode::Len() const
-{
- return maString.getLength();
-}
-
-sal_Int32 ContentNode::GetExpandedLen() const
-{
- sal_Int32 nLen = maString.getLength();
-
- // Fields can be longer than the placeholder in the Node
- const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
- for (sal_Int32 nAttr = rAttrs.size(); nAttr; )
- {
- const EditCharAttrib& rAttr = *rAttrs[--nAttr];
- if (rAttr.Which() == EE_FEATURE_FIELD)
- {
- nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
- --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0
- }
- }
-
- return nLen;
-}
-
-OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos) const
-{
- if ( nEndPos < 0 || nEndPos > Len() )
- nEndPos = Len();
-
- DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );
-
- sal_Int32 nIndex = nStartPos;
- OUStringBuffer aStr(256);
- const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex );
- while ( nIndex < nEndPos )
- {
- sal_Int32 nEnd = nEndPos;
- if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
- nEnd = pNextFeature->GetStart();
- else
- pNextFeature = nullptr; // Feature does not interest the below
-
- DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
- //!! beware of sub string length of -1
- if (nEnd > nIndex)
- aStr.append( GetString().subView(nIndex, nEnd - nIndex) );
-
- if ( pNextFeature )
- {
- switch ( pNextFeature->GetItem()->Which() )
- {
- case EE_FEATURE_TAB: aStr.append( "\t" );
- break;
- case EE_FEATURE_LINEBR: aStr.append( "\x0A" );
- break;
- case EE_FEATURE_FIELD:
- aStr.append( static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue() );
- break;
- default: OSL_FAIL( "What feature?" );
- }
- pNextFeature = GetCharAttribs().FindFeature( ++nEnd );
- }
- nIndex = nEnd;
- }
- return aStr.makeStringAndClear();
-}
-
-void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart )
-{
- sal_Int32 nOffset = 0;
-
- const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
- for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr )
- {
- const EditCharAttrib& rAttr = *rAttrs[nAttr];
- assert (!(nAttr < rAttrs.size() - 1) ||
- rAttrs[nAttr]->GetStart() <= rAttrs[nAttr + 1]->GetStart());
-
- nOffset = rAttr.GetStart();
-
- if (nOffset >= rPos) // happens after the position
- return;
-
- if (rAttr.Which() == EE_FEATURE_FIELD)
- {
- sal_Int32 nChunk = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
- nChunk--; // Character representing the field in the string
-
- if (nOffset + nChunk >= rPos) // we're inside the field
- {
- if (bBiasStart)
- rPos = rAttr.GetStart();
- else
- rPos = rAttr.GetEnd();
- return;
- }
- // Adjust for the position
- rPos -= nChunk;
- }
- }
- assert (rPos <= Len());
-}
-
-/*
- * Fields are represented by a single character in the underlying string
- * and/or selection, however, they can be expanded to the full value of
- * the field. When we're dealing with selection / offsets however we need
- * to deal in character positions inside the real (unexpanded) string.
- * This method maps us back to character offsets.
- */
-void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos )
-{
- UnExpandPosition( rStartPos, true );
- UnExpandPosition( rEndPos, false );
-}
-
-void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c)
-{
- maString = maString.replaceAt(nPos, 1, rtl::OUStringChar(c));
-}
-
-void ContentNode::Insert(std::u16string_view rStr, sal_Int32 nPos)
-{
- maString = maString.replaceAt(nPos, 0, rStr);
-}
-
-void ContentNode::Append(std::u16string_view rStr)
-{
- maString += rStr;
-}
-
-void ContentNode::Erase(sal_Int32 nPos)
-{
- maString = maString.copy(0, nPos);
-}
-
-void ContentNode::Erase(sal_Int32 nPos, sal_Int32 nCount)
-{
- maString = maString.replaceAt(nPos, nCount, u"");
-}
-
-OUString ContentNode::Copy(sal_Int32 nPos) const
-{
- return maString.copy(nPos);
-}
-
-OUString ContentNode::Copy(sal_Int32 nPos, sal_Int32 nCount) const
-{
- return maString.copy(nPos, nCount);
-}
-
-sal_Unicode ContentNode::GetChar(sal_Int32 nPos) const
-{
- return maString[nPos];
-}
-
-void ContentNode::EnsureWrongList()
-{
- if (!mpWrongList)
- CreateWrongList();
-}
-
-WrongList* ContentNode::GetWrongList()
-{
- return mpWrongList.get();
-}
-
-const WrongList* ContentNode::GetWrongList() const
-{
- return mpWrongList.get();
-}
-
-void ContentNode::SetWrongList( WrongList* p )
-{
- mpWrongList.reset(p);
-}
-
-void ContentNode::CreateWrongList()
-{
- SAL_WARN_IF( mpWrongList && !mpWrongList->empty(), "editeng", "WrongList already exist!");
- if (!mpWrongList || !mpWrongList->empty())
- mpWrongList.reset(new WrongList);
-}
-
-void ContentNode::DestroyWrongList()
-{
- mpWrongList.reset();
-}
-
-void ContentNode::dumpAsXml(xmlTextWriterPtr pWriter) const
-{
- (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentNode"));
- (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maString"), BAD_CAST(maString.toUtf8().getStr()));
- maContentAttribs.dumpAsXml(pWriter);
- maCharAttribList.dumpAsXml(pWriter);
- (void)xmlTextWriterEndElement(pWriter);
-}
-
-void ContentNode::checkAndDeleteEmptyAttribs() const
-{
- // Delete empty attributes, but only if paragraph is not empty!
- if (GetCharAttribs().HasEmptyAttribs() && Len())
- {
- const_cast<ContentNode*>(this)->GetCharAttribs().DeleteEmptyAttribs();
- }
-}
-
-ContentAttribs::ContentAttribs( SfxItemPool& rPool )
-: mpStyle(nullptr)
-, maAttribSet( rPool )
-{
-}
-
-
-SvxTabStop ContentAttribs::FindTabStop( sal_Int32 nCurPos, sal_uInt16 nDefTab )
-{
- const SvxTabStopItem& rTabs = GetItem( EE_PARA_TABS );
- for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
- {
- const SvxTabStop& rTab = rTabs[i];
- if ( rTab.GetTabPos() > nCurPos )
- return rTab;
- }
-
- // if there's a default tab size defined for this item use that instead
- if (rTabs.GetDefaultDistance())
- nDefTab = rTabs.GetDefaultDistance();
-
- // Determine DefTab ...
- SvxTabStop aTabStop;
- const sal_Int32 x = nCurPos / nDefTab + 1;
- aTabStop.GetTabPos() = nDefTab * x;
- return aTabStop;
-}
-
-void ContentAttribs::SetStyleSheet( SfxStyleSheet* pS )
-{
- bool bStyleChanged = ( mpStyle != pS );
- mpStyle = pS;
- // Only when other style sheet, not when current style sheet modified
- if ( !(mpStyle && bStyleChanged) )
- return;
-
- // Selectively remove the attributes from the paragraph formatting
- // which are specified in the style, so that the attributes of the
- // style can have an affect.
- const SfxItemSet& rStyleAttribs = mpStyle->GetItemSet();
- for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
- {
- // Don't change bullet on/off
- if ( ( nWhich != EE_PARA_BULLETSTATE ) && ( rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET ) )
- maAttribSet.ClearItem( nWhich );
- }
-}
-
-const SfxPoolItem& ContentAttribs::GetItem( sal_uInt16 nWhich ) const
-{
- // Hard paragraph attributes take precedence!
- const SfxItemSet* pTakeFrom = &maAttribSet;
- if ( mpStyle && ( maAttribSet.GetItemState( nWhich, false ) != SfxItemState::SET ) )
- pTakeFrom = &mpStyle->GetItemSet();
-
- return pTakeFrom->Get( nWhich );
-}
-
-bool ContentAttribs::HasItem( sal_uInt16 nWhich ) const
-{
- bool bHasItem = false;
- if ( maAttribSet.GetItemState( nWhich, false ) == SfxItemState::SET )
- bHasItem = true;
- else if ( mpStyle && mpStyle->GetItemSet().GetItemState( nWhich ) == SfxItemState::SET )
- bHasItem = true;
-
- return bHasItem;
-}
-
-void ContentAttribs::dumpAsXml(xmlTextWriterPtr pWriter) const
-{
- (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentAttribs"));
- (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("style"), "%s", mpStyle->GetName().toUtf8().getStr());
- maAttribSet.dumpAsXml(pWriter);
- (void)xmlTextWriterEndElement(pWriter);
-}
-
EditDoc::EditDoc( SfxItemPool* pPool ) :
nLastCache(0),
pItemPool(pPool ? pPool : new EditEngineItemPool()),
@@ -2653,290 +1953,6 @@ void EditDoc::dumpAsXml(xmlTextWriterPtr pWriter) const
}
}
-
-namespace {
-
-struct LessByStart
-{
- bool operator() (const std::unique_ptr<EditCharAttrib>& left, const std::unique_ptr<EditCharAttrib>& right) const
- {
- return left->GetStart() < right->GetStart();
- }
-};
-
-}
-
-CharAttribList::CharAttribList()
-: mbHasEmptyAttribs(false)
-{
-}
-
-CharAttribList::~CharAttribList()
-{
-}
-
-void CharAttribList::InsertAttrib( EditCharAttrib* pAttrib )
-{
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-// optimize: binary search? !
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- // Maybe just simply iterate backwards:
- // The most common and critical case: Attributes are already sorted
- // (InsertTextObject!) binary search would not be optimal here.
- // => Would bring something!
-
- const sal_Int32 nStart = pAttrib->GetStart(); // may be better for Comp.Opt.
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(*this);
-#endif
-
- if ( pAttrib->IsEmpty() )
- mbHasEmptyAttribs = true;
-
- bool bInsert(true);
- for (sal_Int32 i = 0, n = maAttribs.size(); i < n; ++i)
- {
- const EditCharAttrib& rCurAttrib = *maAttribs[i];
- if (rCurAttrib.GetStart() > nStart)
- {
- maAttribs.insert(maAttribs.begin()+i, std::unique_ptr<EditCharAttrib>(pAttrib));
- bInsert = false;
- break;
- }
- }
-
- if (bInsert) maAttribs.push_back(std::unique_ptr<EditCharAttrib>(pAttrib));
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(*this);
-#endif
-}
-
-void CharAttribList::ResortAttribs()
-{
- std::sort(maAttribs.begin(), maAttribs.end(), LessByStart());
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(*this);
-#endif
-}
-
-void CharAttribList::OptimizeRanges()
-{
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(*this);
-#endif
- for (sal_Int32 i = 0; i < static_cast<sal_Int32>(maAttribs.size()); ++i)
- {
- EditCharAttrib& rAttr = *maAttribs[i];
- for (sal_Int32 nNext = i+1; nNext < static_cast<sal_Int32>(maAttribs.size()); ++nNext)
- {
- EditCharAttrib& rNext = *maAttribs[nNext];
- if (!rAttr.IsFeature() && rNext.GetStart() == rAttr.GetEnd() && rNext.Which() == rAttr.Which())
- {
- if (*rNext.GetItem() == *rAttr.GetItem())
- {
- rAttr.GetEnd() = rNext.GetEnd();
- maAttribs.erase(maAttribs.begin()+nNext);
- }
- break; // only 1 attr with same which can start here.
- }
- else if (rNext.GetStart() > rAttr.GetEnd())
- {
- break;
- }
- }
- }
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
- CharAttribList::DbgCheckAttribs(*this);
-#endif
-}
-
-sal_Int32 CharAttribList::Count() const
-{
- return maAttribs.size();
-}
-
-const EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) const
-{
- // Backwards, if one ends where the next starts.
- // => The starting one is the valid one ...
- AttribsType::const_reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
- [&nWhich, &nPos](const AttribsType::value_type& rxAttr) {
- return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
- if (it != maAttribs.rend())
- {
- const EditCharAttrib& rAttr = **it;
- return &rAttr;
- }
- return nullptr;
-}
-
-EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
-{
- // Backwards, if one ends where the next starts.
- // => The starting one is the valid one ...
- AttribsType::reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
- [&nWhich, &nPos](AttribsType::value_type& rxAttr) {
- return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
- if (it != maAttribs.rend())
- {
- EditCharAttrib& rAttr = **it;
- return &rAttr;
- }
- return nullptr;
-}
-
-const EditCharAttrib* CharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_Int32 nFromPos ) const
-{
- assert(nWhich);
- for (auto const& attrib : maAttribs)
- {
- const EditCharAttrib& rAttr = *attrib;
- if (rAttr.GetStart() >= nFromPos && rAttr.Which() == nWhich)
- return &rAttr;
- }
- return nullptr;
-}
-
-bool CharAttribList::HasAttrib( sal_Int32 nStartPos, sal_Int32 nEndPos ) const
-{
- return std::any_of(maAttribs.rbegin(), maAttribs.rend(),
- [&nStartPos, &nEndPos](const AttribsType::value_type& rxAttr) {
- return rxAttr->GetStart() < nEndPos && rxAttr->GetEnd() > nStartPos; });
-}
-
-
-namespace {
-
-class FindByAddress
-{
- const EditCharAttrib* mpAttr;
-public:
- explicit FindByAddress(const EditCharAttrib* p) : mpAttr(p) {}
- bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
- {
- return r.get() == mpAttr;
- }
-};
-
-}
-
-void CharAttribList::Remove(const EditCharAttrib* p)
-{
- AttribsType::iterator it = std::find_if(maAttribs.begin(), maAttribs.end(), FindByAddress(p));
- if (it != maAttribs.end())
- maAttribs.erase(it);
-}
-
-void CharAttribList::Remove(sal_Int32 nPos)
-{
- if (nPos >= static_cast<sal_Int32>(maAttribs.size()))
- return;
-
- maAttribs.erase(maAttribs.begin()+nPos);
-}
-
-void CharAttribList::SetHasEmptyAttribs(bool b)
-{
- mbHasEmptyAttribs = b;
-}
-
-bool CharAttribList::HasBoundingAttrib( sal_Int32 nBound ) const
-{
- // Backwards, if one ends where the next starts.
- // => The starting one is the valid one ...
- AttribsType::const_reverse_iterator it = maAttribs.rbegin(), itEnd = maAttribs.rend();
- for (; it != itEnd; ++it)
- {
- const EditCharAttrib& rAttr = **it;
- if (rAttr.GetEnd() < nBound)
- return false;
-
- if (rAttr.GetStart() == nBound || rAttr.GetEnd() == nBound)
- return true;
- }
- return false;
-}
-
-EditCharAttrib* CharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
-{
- if ( !mbHasEmptyAttribs )
- return nullptr;
-
- for (const std::unique_ptr<EditCharAttrib>& rAttr : maAttribs)
- {
- if (rAttr->GetStart() == nPos && rAttr->GetEnd() == nPos && rAttr->Which() == nWhich)
- return rAttr.get();
- }
- return nullptr;
-}
-
-namespace {
-
-class FindByStartPos
-{
- sal_Int32 mnPos;
-public:
- explicit FindByStartPos(sal_Int32 nPos) : mnPos(nPos) {}
- bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
- {
- return r->GetStart() >= mnPos;
- }
-};
-
-}
-
-const EditCharAttrib* CharAttribList::FindFeature( sal_Int32 nPos ) const
-{
- // First, find the first attribute that starts at or after specified position.
- AttribsType::const_iterator it =
- std::find_if(maAttribs.begin(), maAttribs.end(), FindByStartPos(nPos));
-
- if (it == maAttribs.end())
- // All attributes are before the specified position.
- return nullptr;
-
- // And find the first attribute with feature.
- it = std::find_if(it, maAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsFeature(); } );
- return it == maAttribs.end() ? nullptr : it->get();
-}
-
-void CharAttribList::DeleteEmptyAttribs()
-{
- std::erase_if(maAttribs, [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsEmpty(); } );
- mbHasEmptyAttribs = false;
-}
-
-#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
-void CharAttribList::DbgCheckAttribs(CharAttribList const& rAttribs)
-{
- std::set<std::pair<sal_Int32, sal_uInt16>> zero_set;
- for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs.maAttribs)
- {
- assert(rAttr->GetStart() <= rAttr->GetEnd());
- assert(!rAttr->IsFeature() || rAttr->GetLen() == 1);
- if (0 == rAttr->GetLen())
- {
- // not sure if 0-length attributes allowed at all in non-empty para?
- assert(zero_set.insert(std::make_pair(rAttr->GetStart(), rAttr->Which())).second && "duplicate 0-length attribute detected");
- }
- }
- CheckOrderedList(rAttribs.GetAttribs());
-}
-#endif
-
-void CharAttribList::dumpAsXml(xmlTextWriterPtr pWriter) const
-{
- (void)xmlTextWriterStartElement(pWriter, BAD_CAST("CharAttribList"));
- for (auto const & i : maAttribs) {
- i->dumpAsXml(pWriter);
- }
- (void)xmlTextWriterEndElement(pWriter);
-}
-
EditEngineItemPool::EditEngineItemPool()
: SfxItemPool( "EditEngineItemPool", EE_ITEMS_START, EE_ITEMS_END,
aItemInfos, nullptr )