summaryrefslogtreecommitdiffstats
path: root/sw/source/core/txtnode/ndhints.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/txtnode/ndhints.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/txtnode/ndhints.cxx')
-rw-r--r--sw/source/core/txtnode/ndhints.cxx486
1 files changed, 486 insertions, 0 deletions
diff --git a/sw/source/core/txtnode/ndhints.cxx b/sw/source/core/txtnode/ndhints.cxx
new file mode 100644
index 0000000000..a9b9d4b6c5
--- /dev/null
+++ b/sw/source/core/txtnode/ndhints.cxx
@@ -0,0 +1,486 @@
+/* -*- 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 <editeng/rsiditem.hxx>
+#include <sal/log.hxx>
+#include <txatbase.hxx>
+#include <ndhints.hxx>
+#include <txtatr.hxx>
+
+#ifdef DBG_UTIL
+#include <pam.hxx>
+#include <fmtautofmt.hxx>
+#endif
+
+/// sort order: Start, End (reverse), Which (reverse),
+/// (char style: sort number), at last the pointer
+static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ if ( rHt1.GetStart() == rHt2.GetStart() )
+ {
+ const sal_Int32 nHt1 = rHt1.GetAnyEnd();
+ const sal_Int32 nHt2 = rHt2.GetAnyEnd();
+ if ( nHt1 == nHt2 )
+ {
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 == nWhich2 )
+ {
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 < nS2;
+ }
+
+ return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
+ }
+ // order is important! for requirements see hintids.hxx
+ return ( nWhich1 > nWhich2 );
+ }
+ return ( nHt1 > nHt2 );
+ }
+ return ( rHt1.GetStart() < rHt2.GetStart() );
+}
+
+/// sort order: Which, Start, End(reverse) at last the pointer
+bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
+{
+ return lhs->Which() < nWhich;
+}
+bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 < nWhich2 )
+ return true;
+ if ( nWhich1 > nWhich2 )
+ return false;
+ if (rHt1.GetStart() < rHt2.GetStart())
+ return true;
+ if (rHt1.GetStart() > rHt2.GetStart())
+ return false;
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 < nS2;
+ }
+ const sal_Int32 nEnd1 = rHt1.GetAnyEnd();
+ const sal_Int32 nEnd2 = rHt2.GetAnyEnd();
+ if ( nEnd1 > nEnd2 )
+ return true;
+ if ( nEnd1 < nEnd2 )
+ return false;
+ return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
+}
+
+/// sort order: End, Start(reverse), Which
+/// (char style: sort number), at last the pointer(reverse)
+bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
+{
+ return nEndPos < rhs->GetAnyEnd();
+}
+bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ const sal_Int32 nHt1 = rHt1.GetAnyEnd();
+ const sal_Int32 nHt2 = rHt2.GetAnyEnd();
+ if ( nHt1 == nHt2 )
+ {
+ if ( rHt1.GetStart() == rHt2.GetStart() )
+ {
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 == nWhich2 )
+ {
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 > nS2;
+ }
+
+ return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
+ }
+ // order is important! for requirements see hintids.hxx
+ return ( nWhich1 < nWhich2 );
+ }
+ else
+ return ( rHt1.GetStart() > rHt2.GetStart() );
+ }
+ return ( nHt1 < nHt2 );
+}
+
+void SwpHints::Insert(SwTextAttr* pHt)
+{
+ assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
+ == m_HintsByStart.end()); // "Insert: hint already in HtStart"
+ assert( pHt->m_pHints == nullptr );
+ pHt->m_pHints = this;
+
+ if (m_bStartMapNeedsSorting)
+ ResortStartMap();
+ if (m_bEndMapNeedsSorting)
+ ResortEndMap();
+ if (m_bWhichMapNeedsSorting)
+ ResortWhichMap();
+
+ auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), pHt, CompareSwpHtStart);
+ m_HintsByStart.insert(it1, pHt);
+
+ auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
+ m_HintsByEnd.insert(it2, pHt);
+
+ auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
+ m_HintsByWhichAndStart.insert(it3, pHt);
+}
+
+bool SwpHints::Contains( const SwTextAttr *pHt ) const
+{
+ // DO NOT use find() or CHECK here!
+ // if called from SwTextNode::InsertItem, pHt has already been deleted,
+ // so it cannot be dereferenced
+ return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
+ != m_HintsByStart.end();
+}
+
+#ifdef DBG_UTIL
+
+#define CHECK_ERR(cond, text) \
+ if(!(cond)) \
+ { \
+ SAL_WARN("sw.core", text); \
+ Resort(); \
+ return false; \
+ }
+
+bool SwpHints::Check(bool bPortionsMerged) const
+{
+ // 1) both arrays have same size
+ CHECK_ERR( m_HintsByStart.size() == m_HintsByEnd.size(),
+ "HintsCheck: wrong sizes" );
+ sal_Int32 nLastStart = 0;
+ sal_Int32 nLastEnd = 0;
+
+ const SwTextAttr *pLastStart = nullptr;
+ const SwTextAttr *pLastEnd = nullptr;
+ o3tl::sorted_vector<SwTextAttr const*> RsidOnlyAutoFormats;
+ if (bPortionsMerged)
+ {
+ for (size_t i = 0; i < Count(); ++i)
+ {
+ SwTextAttr const*const pHint(m_HintsByStart[i]);
+ if (RES_TXTATR_AUTOFMT == pHint->Which())
+ {
+ std::shared_ptr<SfxItemSet> const & pSet(
+ pHint->GetAutoFormat().GetStyleHandle());
+ if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
+ {
+ RsidOnlyAutoFormats.insert(pHint);
+ }
+ }
+ }
+ }
+
+ // --- cross checks ---
+ // same pointers in both arrays
+ auto tmpHintsByEnd = m_HintsByEnd;
+ std::sort(tmpHintsByEnd.begin(), tmpHintsByEnd.end(), CompareSwpHtStart);
+ CHECK_ERR( tmpHintsByEnd == m_HintsByStart, "HintsCheck: the two arrays do not contain the same set of pointers" );
+
+ for( size_t i = 0; i < Count(); ++i )
+ {
+ // --- check Starts ---
+
+ // 2a) valid pointer? depends on overwriting freed mem with 0xFF
+ const SwTextAttr *pHt = m_HintsByStart[i];
+ CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
+
+ // 3a) start sort order?
+ sal_Int32 nIdx = pHt->GetStart();
+ CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
+
+ // 4a) IsLessStart consistency
+ if( pLastStart )
+ CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
+
+ nLastStart = nIdx;
+ pLastStart = pHt;
+
+ // --- check Ends ---
+
+ // 2b) valid pointer? see DELETEFF
+ const SwTextAttr *pHtEnd = m_HintsByEnd[i];
+ CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
+
+ // 3b) end sort order?
+ nIdx = pHtEnd->GetAnyEnd();
+ CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
+
+ // 4b) IsLessEnd consistency
+ if( pLastEnd )
+ CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
+
+ nLastEnd = nIdx;
+ pLastEnd = pHtEnd;
+
+ CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
+
+ // 7a) character attributes in array?
+ sal_uInt16 nWhich = pHt->Which();
+ CHECK_ERR( !isCHRATR(nWhich),
+ "HintsCheck: Character attribute in start array" );
+
+ // 7b) character attributes in array?
+ nWhich = pHtEnd->Which();
+ CHECK_ERR( !isCHRATR(nWhich),
+ "HintsCheck: Character attribute in end array" );
+
+ // 8) style portion check
+ const SwTextAttr* pHtThis = m_HintsByStart[i];
+ const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
+ CHECK_ERR( (0 == i)
+ || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
+ && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
+ || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
+ && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
+ || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
+ || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
+ && (*pHtThis->End() == *pHtLast->End())
+ ) // same range
+ && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
+ || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
+ ) // never two AUTOFMT on same range
+ && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
+ || (pHtLast->Which() != RES_TXTATR_CHARFMT)
+ || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
+ ->GetSortNumber() !=
+ static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
+ ->GetSortNumber())
+ ) // multiple CHARFMT on same range need distinct sorter
+ )
+ || (pHtThis->GetStart() == *pHtThis->End()), // this empty
+ "HintsCheck: Portion inconsistency. "
+ "This can be temporarily ok during undo operations" );
+
+ // 8 1/2) format ignore start/end flag check
+ // (problems because MergePortions buggy or not called)
+ if (bPortionsMerged)
+ {
+ if (RES_TXTATR_AUTOFMT == pHt->Which() ||
+ RES_TXTATR_CHARFMT == pHt->Which())
+ {
+ // mostly ignore the annoying no-length hints
+ // BuildPortions inserts these in the middle of an existing one
+ bool const bNoLength(pHt->GetStart() == *pHt->End());
+ bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
+ bool bForbidContinuation(!bNoLength && !bNeedContinuation);
+ if (RES_TXTATR_AUTOFMT == pHt->Which())
+ {
+ if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
+ {
+ assert(pHt->IsFormatIgnoreStart());
+ bNeedContinuation = false;
+ // don't forbid continuation - may be other hint here!
+ }
+ }
+ if (bNeedContinuation || bForbidContinuation)
+ {
+ bool bFound(false);
+ for (size_t j = i + 1; j < Count(); ++j)
+ {
+ SwTextAttr *const pOther(m_HintsByStart[j]);
+ if (pOther->GetStart() > *pHt->End())
+ {
+ break; // done
+ }
+ else if (pOther->GetStart() == pOther->GetAnyEnd())
+ {
+ continue; // empty hint: ignore
+ }
+ else if (pOther->GetStart() == *pHt->End())
+ {
+ if (RES_TXTATR_AUTOFMT == pOther->Which() ||
+ RES_TXTATR_CHARFMT == pOther->Which())
+ { // multiple charfmt on same range must all match
+ if (bNeedContinuation)
+ {
+ assert(pOther->IsFormatIgnoreStart());
+ bFound = true;
+ }
+ else if (bForbidContinuation &&
+ (RsidOnlyAutoFormats.find(pOther) ==
+ RsidOnlyAutoFormats.end()))
+ {
+ assert(!pOther->IsFormatIgnoreStart());
+ }
+ }
+ }
+ }
+ if (bNeedContinuation)
+ {
+ assert(bFound); // ? can this happen temp. during undo?
+ }
+ }
+ }
+ else
+ {
+ assert(!pHt->IsFormatIgnoreStart());
+ assert(!pHt->IsFormatIgnoreEnd());
+ }
+ }
+
+ // 9) nesting portion check
+ if (pHtThis->IsNesting())
+ {
+ for (size_t j = 0; j < i; ++j)
+ {
+ SwTextAttr const * const pOther( m_HintsByStart[j] );
+ if (pOther->IsNesting())
+ {
+ SwComparePosition cmp = ComparePosition(
+ pHtThis->GetStart(), *pHtThis->End(),
+ pOther->GetStart(), *pOther->End());
+ CHECK_ERR( (SwComparePosition::OverlapBefore != cmp) &&
+ (SwComparePosition::OverlapBehind != cmp),
+ "HintsCheck: overlapping nesting hints!!!" );
+ }
+ }
+ }
+
+ // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
+ if (pHtThis->HasDummyChar())
+ {
+ for ( size_t j = 0; j < i; ++j )
+ {
+ SwTextAttr const * const pOther( m_HintsByStart[j] );
+ if (pOther->HasDummyChar())
+ {
+ CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
+ "HintsCheck: multiple hints claim same CH_TXTATR!");
+ }
+ }
+ }
+ }
+ return true;
+}
+
+#endif /* DBG_UTIL */
+
+// Resort() is called before every Insert and Delete.
+// Various SwTextNode methods modify hints in a way that violates the
+// sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
+// to restore the order.
+
+void SwpHints::Resort() const
+{
+ if (m_bStartMapNeedsSorting)
+ {
+ auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+ std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+ m_bStartMapNeedsSorting = false;
+ }
+ if (m_bEndMapNeedsSorting)
+ {
+ auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+ std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
+ m_bEndMapNeedsSorting = false;
+ }
+ if (m_bWhichMapNeedsSorting)
+ {
+ auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
+ std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
+ m_bWhichMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortStartMap() const
+{
+ if (m_bStartMapNeedsSorting)
+ {
+ auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+ std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+ m_bStartMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortEndMap() const
+{
+ if (m_bEndMapNeedsSorting)
+ {
+ auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+ std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
+ m_bEndMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortWhichMap() const
+{
+ if (m_bWhichMapNeedsSorting)
+ {
+ auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
+ std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
+ m_bWhichMapNeedsSorting = false;
+ }
+}
+
+size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
+{
+ if (m_bWhichMapNeedsSorting)
+ ResortWhichMap();
+ auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
+ if ( it == m_HintsByWhichAndStart.end() )
+ return SAL_MAX_SIZE;
+ return it - m_HintsByWhichAndStart.begin();
+}
+
+int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
+{
+ if (m_bEndMapNeedsSorting)
+ ResortEndMap();
+ auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
+ return it - m_HintsByEnd.begin() - 1;
+}
+
+size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
+{
+ if (m_bStartMapNeedsSorting)
+ ResortStartMap();
+ auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
+ if ( it == m_HintsByStart.end() || *it != pHt )
+ return SAL_MAX_SIZE;
+ return it - m_HintsByStart.begin();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */