docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / core / txtnode / ndhints.cxx
blobb7f82239a22e771738b1d7bac0e333c55649dfba
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: Start
68 /// (char style: sort number), at last the pointer
69 namespace {
70 struct CompareSwpHtStartOnly
72 bool operator()( const SwTextAttr* lhs, sal_Int32 rhs ) const
74 return lhs->GetStart() < rhs;
76 bool operator()( sal_Int32 lhs, const SwTextAttr* rhs ) const
78 return lhs < rhs->GetStart();
81 struct CompareSwpHtEndOnly
83 bool operator()( const SwTextAttr* lhs, sal_Int32 rhs ) const
85 return lhs->GetAnyEnd() < rhs;
87 bool operator()( sal_Int32 lhs, const SwTextAttr* rhs ) const
89 return lhs < rhs->GetAnyEnd();
94 /// sort order: Which, Start, End(reverse) at last the pointer
95 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
97 return lhs->Which() < nWhich;
99 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
101 const SwTextAttr &rHt1 = *lhs;
102 const SwTextAttr &rHt2 = *rhs;
103 const sal_uInt16 nWhich1 = rHt1.Which();
104 const sal_uInt16 nWhich2 = rHt2.Which();
105 if ( nWhich1 < nWhich2 )
106 return true;
107 if ( nWhich1 > nWhich2 )
108 return false;
109 if (rHt1.GetStart() < rHt2.GetStart())
110 return true;
111 if (rHt1.GetStart() > rHt2.GetStart())
112 return false;
113 if ( RES_TXTATR_CHARFMT == nWhich1 )
115 const sal_uInt16 nS1 =
116 static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
117 const sal_uInt16 nS2 =
118 static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
119 if ( nS1 != nS2 ) // robust
120 return nS1 < nS2;
122 const sal_Int32 nEnd1 = rHt1.GetAnyEnd();
123 const sal_Int32 nEnd2 = rHt2.GetAnyEnd();
124 if ( nEnd1 > nEnd2 )
125 return true;
126 if ( nEnd1 < nEnd2 )
127 return false;
128 return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
130 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const WhichStartPair rhs ) const
132 if ( lhs->Which() < rhs.first )
133 return true;
134 if ( lhs->Which() > rhs.first )
135 return false;
136 return lhs->GetStart() < rhs.second;
138 bool CompareSwpHtWhichStart::operator()( const WhichStartPair lhs, const SwTextAttr* rhs ) const
140 if ( lhs.first < rhs->Which() )
141 return true;
142 if ( lhs.first > rhs->Which() )
143 return false;
144 return lhs.second < rhs->GetStart();
147 /// sort order: End, Start(reverse), Which
148 /// (char style: sort number), at last the pointer(reverse)
149 bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
151 return nEndPos < rhs->GetAnyEnd();
153 bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
155 const SwTextAttr &rHt1 = *lhs;
156 const SwTextAttr &rHt2 = *rhs;
157 const sal_Int32 nHt1 = rHt1.GetAnyEnd();
158 const sal_Int32 nHt2 = rHt2.GetAnyEnd();
159 if ( nHt1 == nHt2 )
161 if ( rHt1.GetStart() == rHt2.GetStart() )
163 const sal_uInt16 nWhich1 = rHt1.Which();
164 const sal_uInt16 nWhich2 = rHt2.Which();
165 if ( nWhich1 == nWhich2 )
167 if ( RES_TXTATR_CHARFMT == nWhich1 )
169 const sal_uInt16 nS1 =
170 static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
171 const sal_uInt16 nS2 =
172 static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
173 if ( nS1 != nS2 ) // robust
174 return nS1 > nS2;
177 return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
179 // order is important! for requirements see hintids.hxx
180 return ( nWhich1 < nWhich2 );
182 else
183 return ( rHt1.GetStart() > rHt2.GetStart() );
185 return ( nHt1 < nHt2 );
188 void SwpHints::Insert(SwTextAttr* pHt)
190 assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
191 == m_HintsByStart.end()); // "Insert: hint already in HtStart"
192 assert( pHt->m_pHints == nullptr );
193 pHt->m_pHints = this;
195 ResortStartMap();
196 ResortEndMap();
197 ResortWhichMap();
199 auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), pHt, CompareSwpHtStart);
200 m_HintsByStart.insert(it1, pHt);
202 auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
203 m_HintsByEnd.insert(it2, pHt);
205 auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
206 m_HintsByWhichAndStart.insert(it3, pHt);
209 bool SwpHints::Contains( const SwTextAttr *pHt ) const
211 // DO NOT use find() or CHECK here!
212 // if called from SwTextNode::InsertItem, pHt has already been deleted,
213 // so it cannot be dereferenced
214 return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
215 != m_HintsByStart.end();
218 #ifdef DBG_UTIL
220 #define CHECK_ERR(cond, text) \
221 if(!(cond)) \
223 SAL_WARN("sw.core", text); \
224 Resort(); \
225 return false; \
228 bool SwpHints::Check(bool bPortionsMerged) const
230 // 1) both arrays have same size
231 CHECK_ERR( m_HintsByStart.size() == m_HintsByEnd.size(),
232 "HintsCheck: wrong sizes" );
233 sal_Int32 nLastStart = 0;
234 sal_Int32 nLastEnd = 0;
236 const SwTextAttr *pLastStart = nullptr;
237 const SwTextAttr *pLastEnd = nullptr;
238 o3tl::sorted_vector<SwTextAttr const*> RsidOnlyAutoFormats;
239 if (bPortionsMerged)
241 for (size_t i = 0; i < Count(); ++i)
243 SwTextAttr const*const pHint(m_HintsByStart[i]);
244 if (RES_TXTATR_AUTOFMT == pHint->Which())
246 std::shared_ptr<SfxItemSet> const & pSet(
247 pHint->GetAutoFormat().GetStyleHandle());
248 if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
250 RsidOnlyAutoFormats.insert(pHint);
256 // --- cross checks ---
257 // same pointers in both arrays
258 auto tmpHintsByEnd = m_HintsByEnd;
259 std::sort(tmpHintsByEnd.begin(), tmpHintsByEnd.end(), CompareSwpHtStart);
260 CHECK_ERR( tmpHintsByEnd == m_HintsByStart, "HintsCheck: the two arrays do not contain the same set of pointers" );
262 for( size_t i = 0; i < Count(); ++i )
264 // --- check Starts ---
266 // 2a) valid pointer? depends on overwriting freed mem with 0xFF
267 const SwTextAttr *pHt = m_HintsByStart[i];
268 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
270 // 3a) start sort order?
271 sal_Int32 nIdx = pHt->GetStart();
272 CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
274 // 4a) IsLessStart consistency
275 if( pLastStart )
276 CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
278 nLastStart = nIdx;
279 pLastStart = pHt;
281 // --- check Ends ---
283 // 2b) valid pointer? see DELETEFF
284 const SwTextAttr *pHtEnd = m_HintsByEnd[i];
285 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
287 // 3b) end sort order?
288 nIdx = pHtEnd->GetAnyEnd();
289 CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
291 // 4b) IsLessEnd consistency
292 if( pLastEnd )
293 CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
295 nLastEnd = nIdx;
296 pLastEnd = pHtEnd;
298 CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
300 // 7a) character attributes in array?
301 sal_uInt16 nWhich = pHt->Which();
302 CHECK_ERR( !isCHRATR(nWhich),
303 "HintsCheck: Character attribute in start array" );
305 // 7b) character attributes in array?
306 nWhich = pHtEnd->Which();
307 CHECK_ERR( !isCHRATR(nWhich),
308 "HintsCheck: Character attribute in end array" );
310 // 8) style portion check
311 const SwTextAttr* pHtThis = m_HintsByStart[i];
312 const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
313 CHECK_ERR( (0 == i)
314 || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
315 && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
316 || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
317 && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
318 || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
319 || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
320 && (*pHtThis->End() == *pHtLast->End())
321 ) // same range
322 && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
323 || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
324 ) // never two AUTOFMT on same range
325 && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
326 || (pHtLast->Which() != RES_TXTATR_CHARFMT)
327 || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
328 ->GetSortNumber() !=
329 static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
330 ->GetSortNumber())
331 ) // multiple CHARFMT on same range need distinct sorter
333 || (pHtThis->GetStart() == *pHtThis->End()), // this empty
334 "HintsCheck: Portion inconsistency. "
335 "This can be temporarily ok during undo operations" );
337 // 8 1/2) format ignore start/end flag check
338 // (problems because MergePortions buggy or not called)
339 if (bPortionsMerged)
341 if (RES_TXTATR_AUTOFMT == pHt->Which() ||
342 RES_TXTATR_CHARFMT == pHt->Which())
344 // mostly ignore the annoying no-length hints
345 // BuildPortions inserts these in the middle of an existing one
346 bool const bNoLength(pHt->GetStart() == *pHt->End());
347 bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
348 bool bForbidContinuation(!bNoLength && !bNeedContinuation);
349 if (RES_TXTATR_AUTOFMT == pHt->Which())
351 if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
353 assert(pHt->IsFormatIgnoreStart());
354 bNeedContinuation = false;
355 // don't forbid continuation - may be other hint here!
358 if (bNeedContinuation || bForbidContinuation)
360 bool bFound(false);
361 for (size_t j = i + 1; j < Count(); ++j)
363 SwTextAttr *const pOther(m_HintsByStart[j]);
364 if (pOther->GetStart() > *pHt->End())
366 break; // done
368 else if (pOther->GetStart() == pOther->GetAnyEnd())
370 continue; // empty hint: ignore
372 else if (pOther->GetStart() == *pHt->End())
374 if (RES_TXTATR_AUTOFMT == pOther->Which() ||
375 RES_TXTATR_CHARFMT == pOther->Which())
376 { // multiple charfmt on same range must all match
377 if (bNeedContinuation)
379 assert(pOther->IsFormatIgnoreStart());
380 bFound = true;
382 else if (bForbidContinuation &&
383 (RsidOnlyAutoFormats.find(pOther) ==
384 RsidOnlyAutoFormats.end()))
386 assert(!pOther->IsFormatIgnoreStart());
391 if (bNeedContinuation)
393 assert(bFound); // ? can this happen temp. during undo?
397 else
399 assert(!pHt->IsFormatIgnoreStart());
400 assert(!pHt->IsFormatIgnoreEnd());
404 // 9) nesting portion check
405 if (pHtThis->IsNesting())
407 for (size_t j = 0; j < i; ++j)
409 SwTextAttr const * const pOther( m_HintsByStart[j] );
410 if (pOther->IsNesting())
412 SwComparePosition cmp = ComparePosition(
413 pHtThis->GetStart(), *pHtThis->End(),
414 pOther->GetStart(), *pOther->End());
415 CHECK_ERR( (SwComparePosition::OverlapBefore != cmp) &&
416 (SwComparePosition::OverlapBehind != cmp),
417 "HintsCheck: overlapping nesting hints!!!" );
422 // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
423 if (pHtThis->HasDummyChar())
425 for ( size_t j = 0; j < i; ++j )
427 SwTextAttr const * const pOther( m_HintsByStart[j] );
428 if (pOther->HasDummyChar())
430 CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
431 "HintsCheck: multiple hints claim same CH_TXTATR!");
436 return true;
439 #endif /* DBG_UTIL */
441 // Resort() is called before every Insert and Delete.
442 // Various SwTextNode methods modify hints in a way that violates the
443 // sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
444 // to restore the order.
446 void SwpHints::Resort() const
448 ResortStartMap();
449 ResortEndMap();
450 ResortWhichMap();
453 void SwpHints::ResortStartMap() const
455 if (m_StartMapNeedsSortingRange.first != SAL_MAX_INT32)
457 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
458 if (m_StartMapNeedsSortingRange.first == -1)
459 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
460 else
462 // only need to sort a partial range of the array
463 auto it1 = std::lower_bound(rStartMap.begin(), rStartMap.end(), m_StartMapNeedsSortingRange.first, CompareSwpHtStartOnly());
464 auto it2 = std::upper_bound(rStartMap.begin(), rStartMap.end(), m_StartMapNeedsSortingRange.second, CompareSwpHtStartOnly());
465 std::sort(rStartMap.begin() + std::distance(rStartMap.begin(), it1),
466 rStartMap.begin() + std::distance(rStartMap.begin(), it2), CompareSwpHtStart);
468 m_StartMapNeedsSortingRange = { SAL_MAX_INT32, -1 };
472 void SwpHints::ResortEndMap() const
474 if (m_EndMapNeedsSortingRange.first != SAL_MAX_INT32)
476 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
477 if (m_EndMapNeedsSortingRange.first == -1)
478 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
479 else
481 // only need to sort a partial range of the array
482 auto it1 = std::lower_bound(rEndMap.begin(), rEndMap.end(), m_EndMapNeedsSortingRange.first, CompareSwpHtEndOnly());
483 auto it2 = std::upper_bound(rEndMap.begin(), rEndMap.end(), m_EndMapNeedsSortingRange.second, CompareSwpHtEndOnly());
484 std::sort(rEndMap.begin() + std::distance(rEndMap.begin(), it1),
485 rEndMap.begin() + std::distance(rEndMap.begin(), it2), CompareSwpHtEnd());
487 m_EndMapNeedsSortingRange = { SAL_MAX_INT32, -1 };
491 void SwpHints::ResortWhichMap() const
493 if (m_WhichMapNeedsSortingRange.first.first != SAL_MAX_INT32)
495 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
496 if (m_WhichMapNeedsSortingRange.first.first == -1)
497 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
498 else
500 // only need to sort a partial range of the array
501 auto it1 = std::lower_bound(rWhichStartMap.begin(), rWhichStartMap.end(), m_WhichMapNeedsSortingRange.first, CompareSwpHtWhichStart());
502 auto it2 = std::upper_bound(rWhichStartMap.begin(), rWhichStartMap.end(), m_WhichMapNeedsSortingRange.second, CompareSwpHtWhichStart());
503 std::sort(rWhichStartMap.begin() + std::distance(rWhichStartMap.begin(), it1),
504 rWhichStartMap.begin() + std::distance(rWhichStartMap.begin(), it2), CompareSwpHtWhichStart());
506 m_WhichMapNeedsSortingRange = { { SAL_MAX_INT32, -1 }, { -1, -1 } };
510 size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
512 if (m_WhichMapNeedsSortingRange.first.first != SAL_MAX_INT32)
513 ResortWhichMap();
514 auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
515 if ( it == m_HintsByWhichAndStart.end() )
516 return SAL_MAX_SIZE;
517 return it - m_HintsByWhichAndStart.begin();
520 int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
522 if (m_EndMapNeedsSortingRange.first != SAL_MAX_INT32)
523 ResortEndMap();
524 auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
525 return it - m_HintsByEnd.begin() - 1;
528 size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
530 if (m_StartMapNeedsSortingRange.first != SAL_MAX_INT32)
531 ResortStartMap();
532 auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
533 if ( it == m_HintsByStart.end() || *it != pHt )
534 return SAL_MAX_SIZE;
535 return it - m_HintsByStart.begin();
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */