Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / txtnode / ndhints.cxx
bloba9b9d4b6c51c7c00a8bc1291cd337daf8d508824
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <editeng/rsiditem.hxx>
21 #include <sal/log.hxx>
22 #include <txatbase.hxx>
23 #include <ndhints.hxx>
24 #include <txtatr.hxx>
26 #ifdef DBG_UTIL
27 #include <pam.hxx>
28 #include <fmtautofmt.hxx>
29 #endif
31 /// sort order: Start, End (reverse), Which (reverse),
32 /// (char style: sort number), at last the pointer
33 static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
35 const SwTextAttr &rHt1 = *lhs;
36 const SwTextAttr &rHt2 = *rhs;
37 if ( rHt1.GetStart() == rHt2.GetStart() )
39 const sal_Int32 nHt1 = rHt1.GetAnyEnd();
40 const sal_Int32 nHt2 = rHt2.GetAnyEnd();
41 if ( nHt1 == nHt2 )
43 const sal_uInt16 nWhich1 = rHt1.Which();
44 const sal_uInt16 nWhich2 = rHt2.Which();
45 if ( nWhich1 == nWhich2 )
47 if ( RES_TXTATR_CHARFMT == nWhich1 )
49 const sal_uInt16 nS1 =
50 static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
51 const sal_uInt16 nS2 =
52 static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
53 if ( nS1 != nS2 ) // robust
54 return nS1 < nS2;
57 return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
59 // order is important! for requirements see hintids.hxx
60 return ( nWhich1 > nWhich2 );
62 return ( nHt1 > nHt2 );
64 return ( rHt1.GetStart() < rHt2.GetStart() );
67 /// sort order: Which, Start, End(reverse) at last the pointer
68 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
70 return lhs->Which() < nWhich;
72 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
74 const SwTextAttr &rHt1 = *lhs;
75 const SwTextAttr &rHt2 = *rhs;
76 const sal_uInt16 nWhich1 = rHt1.Which();
77 const sal_uInt16 nWhich2 = rHt2.Which();
78 if ( nWhich1 < nWhich2 )
79 return true;
80 if ( nWhich1 > nWhich2 )
81 return false;
82 if (rHt1.GetStart() < rHt2.GetStart())
83 return true;
84 if (rHt1.GetStart() > rHt2.GetStart())
85 return false;
86 if ( RES_TXTATR_CHARFMT == nWhich1 )
88 const sal_uInt16 nS1 =
89 static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
90 const sal_uInt16 nS2 =
91 static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
92 if ( nS1 != nS2 ) // robust
93 return nS1 < nS2;
95 const sal_Int32 nEnd1 = rHt1.GetAnyEnd();
96 const sal_Int32 nEnd2 = rHt2.GetAnyEnd();
97 if ( nEnd1 > nEnd2 )
98 return true;
99 if ( nEnd1 < nEnd2 )
100 return false;
101 return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
104 /// sort order: End, Start(reverse), Which
105 /// (char style: sort number), at last the pointer(reverse)
106 bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
108 return nEndPos < rhs->GetAnyEnd();
110 bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
112 const SwTextAttr &rHt1 = *lhs;
113 const SwTextAttr &rHt2 = *rhs;
114 const sal_Int32 nHt1 = rHt1.GetAnyEnd();
115 const sal_Int32 nHt2 = rHt2.GetAnyEnd();
116 if ( nHt1 == nHt2 )
118 if ( rHt1.GetStart() == rHt2.GetStart() )
120 const sal_uInt16 nWhich1 = rHt1.Which();
121 const sal_uInt16 nWhich2 = rHt2.Which();
122 if ( nWhich1 == nWhich2 )
124 if ( RES_TXTATR_CHARFMT == nWhich1 )
126 const sal_uInt16 nS1 =
127 static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
128 const sal_uInt16 nS2 =
129 static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
130 if ( nS1 != nS2 ) // robust
131 return nS1 > nS2;
134 return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
136 // order is important! for requirements see hintids.hxx
137 return ( nWhich1 < nWhich2 );
139 else
140 return ( rHt1.GetStart() > rHt2.GetStart() );
142 return ( nHt1 < nHt2 );
145 void SwpHints::Insert(SwTextAttr* pHt)
147 assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
148 == m_HintsByStart.end()); // "Insert: hint already in HtStart"
149 assert( pHt->m_pHints == nullptr );
150 pHt->m_pHints = this;
152 if (m_bStartMapNeedsSorting)
153 ResortStartMap();
154 if (m_bEndMapNeedsSorting)
155 ResortEndMap();
156 if (m_bWhichMapNeedsSorting)
157 ResortWhichMap();
159 auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), pHt, CompareSwpHtStart);
160 m_HintsByStart.insert(it1, pHt);
162 auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
163 m_HintsByEnd.insert(it2, pHt);
165 auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
166 m_HintsByWhichAndStart.insert(it3, pHt);
169 bool SwpHints::Contains( const SwTextAttr *pHt ) const
171 // DO NOT use find() or CHECK here!
172 // if called from SwTextNode::InsertItem, pHt has already been deleted,
173 // so it cannot be dereferenced
174 return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
175 != m_HintsByStart.end();
178 #ifdef DBG_UTIL
180 #define CHECK_ERR(cond, text) \
181 if(!(cond)) \
183 SAL_WARN("sw.core", text); \
184 Resort(); \
185 return false; \
188 bool SwpHints::Check(bool bPortionsMerged) const
190 // 1) both arrays have same size
191 CHECK_ERR( m_HintsByStart.size() == m_HintsByEnd.size(),
192 "HintsCheck: wrong sizes" );
193 sal_Int32 nLastStart = 0;
194 sal_Int32 nLastEnd = 0;
196 const SwTextAttr *pLastStart = nullptr;
197 const SwTextAttr *pLastEnd = nullptr;
198 o3tl::sorted_vector<SwTextAttr const*> RsidOnlyAutoFormats;
199 if (bPortionsMerged)
201 for (size_t i = 0; i < Count(); ++i)
203 SwTextAttr const*const pHint(m_HintsByStart[i]);
204 if (RES_TXTATR_AUTOFMT == pHint->Which())
206 std::shared_ptr<SfxItemSet> const & pSet(
207 pHint->GetAutoFormat().GetStyleHandle());
208 if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
210 RsidOnlyAutoFormats.insert(pHint);
216 // --- cross checks ---
217 // same pointers in both arrays
218 auto tmpHintsByEnd = m_HintsByEnd;
219 std::sort(tmpHintsByEnd.begin(), tmpHintsByEnd.end(), CompareSwpHtStart);
220 CHECK_ERR( tmpHintsByEnd == m_HintsByStart, "HintsCheck: the two arrays do not contain the same set of pointers" );
222 for( size_t i = 0; i < Count(); ++i )
224 // --- check Starts ---
226 // 2a) valid pointer? depends on overwriting freed mem with 0xFF
227 const SwTextAttr *pHt = m_HintsByStart[i];
228 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
230 // 3a) start sort order?
231 sal_Int32 nIdx = pHt->GetStart();
232 CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
234 // 4a) IsLessStart consistency
235 if( pLastStart )
236 CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
238 nLastStart = nIdx;
239 pLastStart = pHt;
241 // --- check Ends ---
243 // 2b) valid pointer? see DELETEFF
244 const SwTextAttr *pHtEnd = m_HintsByEnd[i];
245 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
247 // 3b) end sort order?
248 nIdx = pHtEnd->GetAnyEnd();
249 CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
251 // 4b) IsLessEnd consistency
252 if( pLastEnd )
253 CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
255 nLastEnd = nIdx;
256 pLastEnd = pHtEnd;
258 CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
260 // 7a) character attributes in array?
261 sal_uInt16 nWhich = pHt->Which();
262 CHECK_ERR( !isCHRATR(nWhich),
263 "HintsCheck: Character attribute in start array" );
265 // 7b) character attributes in array?
266 nWhich = pHtEnd->Which();
267 CHECK_ERR( !isCHRATR(nWhich),
268 "HintsCheck: Character attribute in end array" );
270 // 8) style portion check
271 const SwTextAttr* pHtThis = m_HintsByStart[i];
272 const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
273 CHECK_ERR( (0 == i)
274 || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
275 && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
276 || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
277 && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
278 || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
279 || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
280 && (*pHtThis->End() == *pHtLast->End())
281 ) // same range
282 && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
283 || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
284 ) // never two AUTOFMT on same range
285 && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
286 || (pHtLast->Which() != RES_TXTATR_CHARFMT)
287 || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
288 ->GetSortNumber() !=
289 static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
290 ->GetSortNumber())
291 ) // multiple CHARFMT on same range need distinct sorter
293 || (pHtThis->GetStart() == *pHtThis->End()), // this empty
294 "HintsCheck: Portion inconsistency. "
295 "This can be temporarily ok during undo operations" );
297 // 8 1/2) format ignore start/end flag check
298 // (problems because MergePortions buggy or not called)
299 if (bPortionsMerged)
301 if (RES_TXTATR_AUTOFMT == pHt->Which() ||
302 RES_TXTATR_CHARFMT == pHt->Which())
304 // mostly ignore the annoying no-length hints
305 // BuildPortions inserts these in the middle of an existing one
306 bool const bNoLength(pHt->GetStart() == *pHt->End());
307 bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
308 bool bForbidContinuation(!bNoLength && !bNeedContinuation);
309 if (RES_TXTATR_AUTOFMT == pHt->Which())
311 if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
313 assert(pHt->IsFormatIgnoreStart());
314 bNeedContinuation = false;
315 // don't forbid continuation - may be other hint here!
318 if (bNeedContinuation || bForbidContinuation)
320 bool bFound(false);
321 for (size_t j = i + 1; j < Count(); ++j)
323 SwTextAttr *const pOther(m_HintsByStart[j]);
324 if (pOther->GetStart() > *pHt->End())
326 break; // done
328 else if (pOther->GetStart() == pOther->GetAnyEnd())
330 continue; // empty hint: ignore
332 else if (pOther->GetStart() == *pHt->End())
334 if (RES_TXTATR_AUTOFMT == pOther->Which() ||
335 RES_TXTATR_CHARFMT == pOther->Which())
336 { // multiple charfmt on same range must all match
337 if (bNeedContinuation)
339 assert(pOther->IsFormatIgnoreStart());
340 bFound = true;
342 else if (bForbidContinuation &&
343 (RsidOnlyAutoFormats.find(pOther) ==
344 RsidOnlyAutoFormats.end()))
346 assert(!pOther->IsFormatIgnoreStart());
351 if (bNeedContinuation)
353 assert(bFound); // ? can this happen temp. during undo?
357 else
359 assert(!pHt->IsFormatIgnoreStart());
360 assert(!pHt->IsFormatIgnoreEnd());
364 // 9) nesting portion check
365 if (pHtThis->IsNesting())
367 for (size_t j = 0; j < i; ++j)
369 SwTextAttr const * const pOther( m_HintsByStart[j] );
370 if (pOther->IsNesting())
372 SwComparePosition cmp = ComparePosition(
373 pHtThis->GetStart(), *pHtThis->End(),
374 pOther->GetStart(), *pOther->End());
375 CHECK_ERR( (SwComparePosition::OverlapBefore != cmp) &&
376 (SwComparePosition::OverlapBehind != cmp),
377 "HintsCheck: overlapping nesting hints!!!" );
382 // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
383 if (pHtThis->HasDummyChar())
385 for ( size_t j = 0; j < i; ++j )
387 SwTextAttr const * const pOther( m_HintsByStart[j] );
388 if (pOther->HasDummyChar())
390 CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
391 "HintsCheck: multiple hints claim same CH_TXTATR!");
396 return true;
399 #endif /* DBG_UTIL */
401 // Resort() is called before every Insert and Delete.
402 // Various SwTextNode methods modify hints in a way that violates the
403 // sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
404 // to restore the order.
406 void SwpHints::Resort() const
408 if (m_bStartMapNeedsSorting)
410 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
411 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
412 m_bStartMapNeedsSorting = false;
414 if (m_bEndMapNeedsSorting)
416 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
417 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
418 m_bEndMapNeedsSorting = false;
420 if (m_bWhichMapNeedsSorting)
422 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
423 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
424 m_bWhichMapNeedsSorting = false;
428 void SwpHints::ResortStartMap() const
430 if (m_bStartMapNeedsSorting)
432 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
433 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
434 m_bStartMapNeedsSorting = false;
438 void SwpHints::ResortEndMap() const
440 if (m_bEndMapNeedsSorting)
442 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
443 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
444 m_bEndMapNeedsSorting = false;
448 void SwpHints::ResortWhichMap() const
450 if (m_bWhichMapNeedsSorting)
452 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
453 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
454 m_bWhichMapNeedsSorting = false;
458 size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
460 if (m_bWhichMapNeedsSorting)
461 ResortWhichMap();
462 auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
463 if ( it == m_HintsByWhichAndStart.end() )
464 return SAL_MAX_SIZE;
465 return it - m_HintsByWhichAndStart.begin();
468 int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
470 if (m_bEndMapNeedsSorting)
471 ResortEndMap();
472 auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
473 return it - m_HintsByEnd.begin() - 1;
476 size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
478 if (m_bStartMapNeedsSorting)
479 ResortStartMap();
480 auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
481 if ( it == m_HintsByStart.end() || *it != pHt )
482 return SAL_MAX_SIZE;
483 return it - m_HintsByStart.begin();
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */