1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <fmtautofmt.hxx>
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();
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
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
)
80 if ( nWhich1
> nWhich2
)
82 if (rHt1
.GetStart() < rHt2
.GetStart())
84 if (rHt1
.GetStart() > rHt2
.GetStart())
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
95 const sal_Int32 nEnd1
= rHt1
.GetAnyEnd();
96 const sal_Int32 nEnd2
= rHt2
.GetAnyEnd();
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();
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
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
);
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
)
154 if (m_bEndMapNeedsSorting
)
156 if (m_bWhichMapNeedsSorting
)
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();
180 #define CHECK_ERR(cond, text) \
183 SAL_WARN("sw.core", text); \
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
;
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
236 CHECK_ERR( CompareSwpHtStart( pLastStart
, pHt
), "HintsCheck: IsLastStart" );
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
253 CHECK_ERR( CompareSwpHtEnd()( pLastEnd
, pHtEnd
), "HintsCheck: IsLastEnd" );
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;
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())
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
)
289 static_txtattr_cast
<const SwTextCharFormat
*>(pHtLast
)
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)
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
)
321 for (size_t j
= i
+ 1; j
< Count(); ++j
)
323 SwTextAttr
*const pOther(m_HintsByStart
[j
]);
324 if (pOther
->GetStart() > *pHt
->End())
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());
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?
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!");
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
)
462 auto it
= std::lower_bound(m_HintsByWhichAndStart
.begin(), m_HintsByWhichAndStart
.end(), nWhich
, CompareSwpHtWhichStart());
463 if ( it
== m_HintsByWhichAndStart
.end() )
465 return it
- m_HintsByWhichAndStart
.begin();
468 int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos
) const
470 if (m_bEndMapNeedsSorting
)
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
)
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
)
483 return it
- m_HintsByStart
.begin();
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */