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 // --- 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" );
221
222 for( size_t i = 0; i < Count(); ++i )
223 {
224 // --- check Starts ---
225
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" );
229
230 // 3a) start sort order?
231 sal_Int32 nIdx = pHt->GetStart();
232 CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
233
234 // 4a) IsLessStart consistency
235 if( pLastStart )
236 CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
237
238 nLastStart = nIdx;
239 pLastStart = pHt;
240
241 // --- check Ends ---
242
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" );
246
247 // 3b) end sort order?
248 nIdx = pHtEnd->GetAnyEnd();
249 CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
250
251 // 4b) IsLessEnd consistency
252 if( pLastEnd )
253 CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
254
255 nLastEnd = nIdx;
256 pLastEnd = pHtEnd;
257
258 CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
259
260 // 7a) character attributes in array?
261 sal_uInt16 nWhich = pHt->Which();
262 CHECK_ERR( !isCHRATR(nWhich),
263 "HintsCheck: Character attribute in start array" );
264
265 // 7b) character attributes in array?
266 nWhich = pHtEnd->Which();
267 CHECK_ERR( !isCHRATR(nWhich),
268 "HintsCheck: Character attribute in end array" );
269
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
292 )
293 || (pHtThis->GetStart() == *pHtThis->End()), // this empty
294 "HintsCheck: Portion inconsistency. "
295 "This can be temporarily ok during undo operations" );
296
297 // 8 1/2) format ignore start/end flag check
298 // (problems because MergePortions buggy or not called)
299 if (bPortionsMerged)
300 {
301 if (RES_TXTATR_AUTOFMT == pHt->Which() ||
302 RES_TXTATR_CHARFMT == pHt->Which())
303 {
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())
310 {
311 if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
312 {
313 assert(pHt->IsFormatIgnoreStart());
314 bNeedContinuation = false;
315 // don't forbid continuation - may be other hint here!
316 }
317 }
318 if (bNeedContinuation || bForbidContinuation)
319 {
320 bool bFound(false);
321 for (size_t j = i + 1; j < Count(); ++j)
322 {
323 SwTextAttr *const pOther(m_HintsByStart[j]);
324 if (pOther->GetStart() > *pHt->End())
325 {
326 break; // done
327 }
328 else if (pOther->GetStart() == pOther->GetAnyEnd())
329 {
330 continue; // empty hint: ignore
331 }
332 else if (pOther->GetStart() == *pHt->End())
333 {
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)
338 {
339 assert(pOther->IsFormatIgnoreStart());
340 bFound = true;
341 }
342 else if (bForbidContinuation &&
343 (RsidOnlyAutoFormats.find(pOther) ==
344 RsidOnlyAutoFormats.end()))
345 {
346 assert(!pOther->IsFormatIgnoreStart());
347 }
348 }
349 }
350 }
351 if (bNeedContinuation)
352 {
353 assert(bFound); // ? can this happen temp. during undo?
354 }
355 }
356 }
357 else
358 {
359 assert(!pHt->IsFormatIgnoreStart());
360 assert(!pHt->IsFormatIgnoreEnd());
361 }
362 }
363
364 // 9) nesting portion check
365 if (pHtThis->IsNesting())
366 {
367 for (size_t j = 0; j < i; ++j)
368 {
369 SwTextAttr const * const pOther( m_HintsByStart[j] );
370 if (pOther->IsNesting())
371 {
373 pHtThis->GetStart(), *pHtThis->End(),
374 pOther->GetStart(), *pOther->End());
377 "HintsCheck: overlapping nesting hints!!!" );
378 }
379 }
380 }
381
382 // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
383 if (pHtThis->HasDummyChar())
384 {
385 for ( size_t j = 0; j < i; ++j )
386 {
387 SwTextAttr const * const pOther( m_HintsByStart[j] );
388 if (pOther->HasDummyChar())
389 {
390 CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
391 "HintsCheck: multiple hints claim same CH_TXTATR!");
392 }
393 }
394 }
395 }
396 return true;
397}
398
399#endif /* DBG_UTIL */
400
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.
405
407{
409 {
410 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
411 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
413 }
415 {
416 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
417 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
418 m_bEndMapNeedsSorting = false;
419 }
421 {
422 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
423 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
425 }
426}
427
429{
431 {
432 auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
433 std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
435 }
436}
437
439{
441 {
442 auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
443 std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
444 m_bEndMapNeedsSorting = false;
445 }
446}
447
449{
451 {
452 auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
453 std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
455 }
456}
457
458size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
459{
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();
466}
467
468int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
469{
471 ResortEndMap();
472 auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
473 return it - m_HintsByEnd.begin() - 1;
474}
475
476size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
477{
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();
484}
485
486/* 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:448
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:458
bool Contains(const SwTextAttr *pHt) const
Definition: ndhints.cxx:169
size_t GetIndexOf(const SwTextAttr *pHt) const
Definition: ndhints.cxx:476
SW_DLLPUBLIC void Resort() const
Definition: ndhints.cxx:406
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:468
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:428
SW_DLLPUBLIC void ResortEndMap() const
Definition: ndhints.cxx:438
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:470
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:122
SwComparePosition
Definition: pam.hxx:109
@ 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