LibreOffice Module sw (master) 1
ndhints.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <editeng/rsiditem.hxx>
21#include <sal/log.hxx>
22#include <txatbase.hxx>
23#include <ndhints.hxx>
24#include <txtatr.hxx>
25
26#ifdef DBG_UTIL
27#include <pam.hxx>
28#include <fmtautofmt.hxx>
29#endif
30
33static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
34{
35 const SwTextAttr &rHt1 = *lhs;
36 const SwTextAttr &rHt2 = *rhs;
37 if ( rHt1.GetStart() == rHt2.GetStart() )
38 {
39 const sal_Int32 nHt1 = rHt1.GetAnyEnd();
40 const sal_Int32 nHt2 = rHt2.GetAnyEnd();
41 if ( nHt1 == nHt2 )
42 {
43 const sal_uInt16 nWhich1 = rHt1.Which();
44 const sal_uInt16 nWhich2 = rHt2.Which();
45 if ( nWhich1 == nWhich2 )
46 {
47 if ( RES_TXTATR_CHARFMT == nWhich1 )
48 {
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;
55 }
56
57 return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
58 }
59 // order is important! for requirements see hintids.hxx
60 return ( nWhich1 > nWhich2 );
61 }
62 return ( nHt1 > nHt2 );
63 }
64 return ( rHt1.GetStart() < rHt2.GetStart() );
65}
66
68bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
69{
70 return lhs->Which() < nWhich;
71}
72bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
73{
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 )
87 {
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;
94 }
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);
102}
103
106bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
107{
108 return nEndPos < rhs->GetAnyEnd();
109}
110bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
111{
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 )
117 {
118 if ( rHt1.GetStart() == rHt2.GetStart() )
119 {
120 const sal_uInt16 nWhich1 = rHt1.Which();
121 const sal_uInt16 nWhich2 = rHt2.Which();
122 if ( nWhich1 == nWhich2 )
123 {
124 if ( RES_TXTATR_CHARFMT == nWhich1 )
125 {
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;
132 }
133
134 return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
135 }
136 // order is important! for requirements see hintids.hxx
137 return ( nWhich1 < nWhich2 );
138 }
139 else
140 return ( rHt1.GetStart() > rHt2.GetStart() );
141 }
142 return ( nHt1 < nHt2 );
143}
144
146{
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;
151
155 ResortEndMap();
158
159 auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), pHt, CompareSwpHtStart);
160 m_HintsByStart.insert(it1, pHt);
161
162 auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
163 m_HintsByEnd.insert(it2, pHt);
164
165 auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
166 m_HintsByWhichAndStart.insert(it3, pHt);
167}
168
169bool SwpHints::Contains( const SwTextAttr *pHt ) const
170{
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();
176}
177
178#ifdef DBG_UTIL
179
180#define CHECK_ERR(cond, text) \
181 if(!(cond)) \
182 { \
183 SAL_WARN("sw.core", text); \
184 Resort(); \
185 return false; \
186 }
187
188bool SwpHints::Check(bool bPortionsMerged) const
189{
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;
195
196 const SwTextAttr *pLastStart = nullptr;
197 const SwTextAttr *pLastEnd = nullptr;
198 o3tl::sorted_vector<SwTextAttr const*> RsidOnlyAutoFormats;
199 if (bPortionsMerged)
200 {
201 for (size_t i = 0; i < Count(); ++i)
202 {
203 SwTextAttr const*const pHint(m_HintsByStart[i]);
204 if (RES_TXTATR_AUTOFMT == pHint->Which())
205 {
206 std::shared_ptr<SfxItemSet> const & pSet(
207 pHint->GetAutoFormat().GetStyleHandle());
208 if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
209 {
210 RsidOnlyAutoFormats.insert(pHint);
211 }
212 }
213 }
214 }
215
216 for( size_t i = 0; i < Count(); ++i )
217 {
218 // --- check Starts ---
219
220 // 2a) valid pointer? depends on overwriting freed mem with 0xFF
221 const SwTextAttr *pHt = m_HintsByStart[i];
222 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
223
224 // 3a) start sort order?
225 sal_Int32 nIdx = pHt->GetStart();
226 CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
227
228 // 4a) IsLessStart consistency
229 if( pLastStart )
230 CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
231
232 nLastStart = nIdx;
233 pLastStart = pHt;
234
235 // --- check Ends ---
236
237 // 2b) valid pointer? see DELETEFF
238 const SwTextAttr *pHtEnd = m_HintsByEnd[i];
239 CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
240
241 // 3b) end sort order?
242 nIdx = pHtEnd->GetAnyEnd();
243 CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
244
245 // 4b) IsLessEnd consistency
246 if( pLastEnd )
247 CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
248
249 nLastEnd = nIdx;
250 pLastEnd = pHtEnd;
251
252 // --- cross checks ---
253
254 // 5) same pointers in both arrays
255 if (std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart) == m_HintsByStart.end())
256 nIdx = COMPLETE_STRING;
257
258 CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetStartOf" );
259
260 // 6) same pointers in both arrays
261 if (std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd()) == m_HintsByEnd.end())
262 nIdx = COMPLETE_STRING;
263
264 CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
265
266 // 7a) character attributes in array?
267 sal_uInt16 nWhich = pHt->Which();
268 CHECK_ERR( !isCHRATR(nWhich),
269 "HintsCheck: Character attribute in start array" );
270
271 // 7b) character attributes in array?
272 nWhich = pHtEnd->Which();
273 CHECK_ERR( !isCHRATR(nWhich),
274 "HintsCheck: Character attribute in end array" );
275
276 // 8) style portion check
277 const SwTextAttr* pHtThis = m_HintsByStart[i];
278 const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
279 CHECK_ERR( (0 == i)
280 || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
281 && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
282 || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
283 && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
284 || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
285 || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
286 && (*pHtThis->End() == *pHtLast->End())
287 ) // same range
288 && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
289 || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
290 ) // never two AUTOFMT on same range
291 && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
292 || (pHtLast->Which() != RES_TXTATR_CHARFMT)
293 || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
294 ->GetSortNumber() !=
295 static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
296 ->GetSortNumber())
297 ) // multiple CHARFMT on same range need distinct sorter
298 )
299 || (pHtThis->GetStart() == *pHtThis->End()), // this empty
300 "HintsCheck: Portion inconsistency. "
301 "This can be temporarily ok during undo operations" );
302
303 // 8 1/2) format ignore start/end flag check
304 // (problems because MergePortions buggy or not called)
305 if (bPortionsMerged)
306 {
307 if (RES_TXTATR_AUTOFMT == pHt->Which() ||
308 RES_TXTATR_CHARFMT == pHt->Which())
309 {
310 // mostly ignore the annoying no-length hints
311 // BuildPortions inserts these in the middle of an existing one
312 bool const bNoLength(pHt->GetStart() == *pHt->End());
313 bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
314 bool bForbidContinuation(!bNoLength && !bNeedContinuation);
315 if (RES_TXTATR_AUTOFMT == pHt->Which())
316 {
317 if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
318 {
319 assert(pHt->IsFormatIgnoreStart());
320 bNeedContinuation = false;
321 // don't forbid continuation - may be other hint here!
322 }
323 }
324 if (bNeedContinuation || bForbidContinuation)
325 {
326 bool bFound(false);
327 for (size_t j = i + 1; j < Count(); ++j)
328 {
329 SwTextAttr *const pOther(m_HintsByStart[j]);
330 if (pOther->GetStart() > *pHt->End())
331 {
332 break; // done
333 }
334 else if (pOther->GetStart() == pOther->GetAnyEnd())
335 {
336 continue; // empty hint: ignore
337 }
338 else if (pOther->GetStart() == *pHt->End())
339 {
340 if (RES_TXTATR_AUTOFMT == pOther->Which() ||
341 RES_TXTATR_CHARFMT == pOther->Which())
342 { // multiple charfmt on same range must all match
343 if (bNeedContinuation)
344 {
345 assert(pOther->IsFormatIgnoreStart());
346 bFound = true;
347 }
348 else if (bForbidContinuation &&
349 (RsidOnlyAutoFormats.find(pOther) ==
350 RsidOnlyAutoFormats.end()))
351 {
352 assert(!pOther->IsFormatIgnoreStart());
353 }
354 }
355 }
356 }
357 if (bNeedContinuation)
358 {
359 assert(bFound); // ? can this happen temp. during undo?
360 }
361 }
362 }
363 else
364 {
365 assert(!pHt->IsFormatIgnoreStart());
366 assert(!pHt->IsFormatIgnoreEnd());
367 }
368 }
369
370 // 9) nesting portion check
371 if (pHtThis->IsNesting())
372 {
373 for (size_t j = 0; j < i; ++j)
374 {
375 SwTextAttr const * const pOther( m_HintsByStart[j] );
376 if (pOther->IsNesting())
377 {
379 pHtThis->GetStart(), *pHtThis->End(),
380 pOther->GetStart(), *pOther->End());
383 "HintsCheck: overlapping nesting hints!!!" );
384 }
385 }
386 }
387
388 // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
389 if (pHtThis->HasDummyChar())
390 {
391 for ( size_t j = 0; j < i; ++j )
392 {
393 SwTextAttr const * const pOther( m_HintsByStart[j] );
394 if (pOther->HasDummyChar())
395 {
396 CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
397 "HintsCheck: multiple hints claim same CH_TXTATR!");
398 }
399 }
400 }
401 }
402 return true;
403}
404
405#endif /* DBG_UTIL */
406
407// Resort() is called before every Insert and Delete.
408// Various SwTextNode methods modify hints in a way that violates the
409// sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
410// to restore the order.
411
413{
415 {
416 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
417 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
419 }
421 {
422 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
423 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
424 m_bEndMapNeedsSorting = false;
425 }
427 {
428 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
429 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
431 }
432}
433
435{
437 {
438 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
439 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
441 }
442}
443
445{
447 {
448 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
449 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
450 m_bEndMapNeedsSorting = false;
451 }
452}
453
455{
457 {
458 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
459 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
461 }
462}
463
464size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
465{
468 auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
469 if ( it == m_HintsByWhichAndStart.end() )
470 return SAL_MAX_SIZE;
471 return it - m_HintsByWhichAndStart.begin();
472}
473
474int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
475{
477 ResortEndMap();
478 auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
479 return it - m_HintsByEnd.begin() - 1;
480}
481
482size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
483{
486 auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
487 if ( it == m_HintsByStart.end() || *it != pHt )
488 return SAL_MAX_SIZE;
489 return it - m_HintsByStart.begin();
490}
491
492/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:49
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:161
bool IsFormatIgnoreStart() const
Definition: txatbase.hxx:108
bool IsNesting() const
Definition: txatbase.hxx:106
const sal_Int32 * End() const
Definition: txatbase.hxx:156
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:193
bool HasDummyChar() const
Definition: txatbase.hxx:107
sal_uInt16 Which() const
Definition: txatbase.hxx:116
SwpHints * m_pHints
Definition: txatbase.hxx:67
bool IsFormatIgnoreEnd() const
Definition: txatbase.hxx:109
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SW_DLLPUBLIC void ResortWhichMap() const
Definition: ndhints.cxx:454
std::vector< SwTextAttr * > m_HintsByWhichAndStart
Definition: ndhints.hxx:78
std::vector< SwTextAttr * > m_HintsByEnd
Definition: ndhints.hxx:77
bool m_bEndMapNeedsSorting
Definition: ndhints.hxx:93
size_t GetFirstPosSortedByWhichAndStart(sal_uInt16 nWhich) const
Definition: ndhints.cxx:464
bool Contains(const SwTextAttr *pHt) const
Definition: ndhints.cxx:169
size_t GetIndexOf(const SwTextAttr *pHt) const
Definition: ndhints.cxx:482
SW_DLLPUBLIC void Resort() const
Definition: ndhints.cxx:412
size_t Count() const
Definition: ndhints.hxx:142
bool Check(bool) const
Definition: ndhints.cxx:188
int GetLastPosSortedByEnd(sal_Int32 nEndPos) const
Definition: ndhints.cxx:474
void Insert(SwTextAttr *pHt)
Definition: ndhints.cxx:145
bool m_bWhichMapNeedsSorting
Definition: ndhints.hxx:94
std::vector< SwTextAttr * > m_HintsByStart
Definition: ndhints.hxx:76
bool m_bStartMapNeedsSorting
Definition: ndhints.hxx:92
SW_DLLPUBLIC void ResortStartMap() const
Definition: ndhints.cxx:434
SW_DLLPUBLIC void ResortEndMap() const
Definition: ndhints.cxx:444
const_iterator find(const Value &x) const
const_iterator end() const
std::pair< const_iterator, bool > insert(Value &&x)
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:479
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
constexpr TypedWhichId< SvxRsidItem > RES_CHRATR_RSID(39)
int i
#define CHECK_ERR(cond, text)
Definition: ndhints.cxx:180
static bool CompareSwpHtStart(const SwTextAttr *lhs, const SwTextAttr *rhs)
sort order: Start, End (reverse), Which (reverse), (char style: sort number), at last the pointer
Definition: ndhints.cxx:33
SwComparePosition ComparePosition(const T &rStt1, const T &rEnd1, const T &rStt2, const T &rEnd2)
Definition: pam.hxx:102
SwComparePosition
Definition: pam.hxx:89
@ OverlapBehind
Pos1 overlaps Pos2 at the end.
@ OverlapBefore
Pos1 overlaps Pos2 at the beginning.
bool operator()(sal_Int32 nEndPos, const SwTextAttr *rhs) const
sort order: End, Start(reverse), Which (char style: sort number), at last the pointer(reverse)
Definition: ndhints.cxx:106
bool operator()(const SwTextAttr *lhs, const sal_uInt16 nWhich) const
sort order: Which, Start, End(reverse) at last the pointer
Definition: ndhints.cxx:68
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57