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 
33 static 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 
68 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
69 {
70  return lhs->Which() < nWhich;
71 }
72 bool 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 
106 bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
107 {
108  return nEndPos < rhs->GetAnyEnd();
109 }
110 bool 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 
153  ResortStartMap();
155  ResortEndMap();
157  ResortWhichMap();
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 
169 bool 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 
188 bool 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 
412 void SwpHints::Resort() const
413 {
415  {
416  auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
417  std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
418  m_bStartMapNeedsSorting = false;
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());
430  m_bWhichMapNeedsSorting = false;
431  }
432 }
433 
435 {
437  {
438  auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
439  std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
440  m_bStartMapNeedsSorting = false;
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());
460  m_bWhichMapNeedsSorting = false;
461  }
462 }
463 
464 size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
465 {
467  ResortWhichMap();
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 
474 int 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 
482 size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
483 {
485  ResortStartMap();
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: */
bool operator()(const SwTextAttr *lhs, const sal_uInt16 nWhich) const
sort order: Which, Start, End(reverse) at last the pointer
Definition: ndhints.cxx:68
std::vector< SwTextAttr * > m_HintsByEnd
Definition: ndhints.hxx:77
SW_DLLPUBLIC void Resort() const
Definition: ndhints.cxx:412
bool IsFormatIgnoreEnd() const
Definition: txatbase.hxx:107
size_t GetIndexOf(const SwTextAttr *pHt) const
Definition: ndhints.cxx:482
bool IsNesting() const
Definition: txatbase.hxx:104
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:476
SwComparePosition ComparePosition(const T &rStt1, const T &rEnd1, const T &rStt2, const T &rEnd2)
Definition: pam.hxx:77
SwpHints * m_pHints
Definition: txatbase.hxx:65
const_iterator find(const Value &x) const
bool HasDummyChar() const
Definition: txatbase.hxx:105
SW_DLLPUBLIC void ResortWhichMap() const
Definition: ndhints.cxx:454
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:49
sal_uInt16 Which() const
Definition: txatbase.hxx:114
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:157
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:41
sal_Int32 GetStart() const
Definition: txatbase.hxx:86
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
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
size_t Count() const
Definition: ndhints.hxx:142
int i
SW_DLLPUBLIC void ResortEndMap() const
Definition: ndhints.cxx:444
void Insert(SwTextAttr *pHt)
Definition: ndhints.cxx:145
size_t GetFirstPosSortedByWhichAndStart(sal_uInt16 nWhich) const
Definition: ndhints.cxx:464
SW_DLLPUBLIC void ResortStartMap() const
Definition: ndhints.cxx:434
int GetLastPosSortedByEnd(sal_Int32 nEndPos) const
Definition: ndhints.cxx:474
#define CHECK_ERR(cond, text)
Definition: ndhints.cxx:180
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:241
std::vector< SwTextAttr * > m_HintsByStart
Definition: ndhints.hxx:76
const_iterator end() const
bool m_bEndMapNeedsSorting
Definition: ndhints.hxx:93
constexpr TypedWhichId< SvxRsidItem > RES_CHRATR_RSID(39)
bool m_bStartMapNeedsSorting
Definition: ndhints.hxx:92
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
Pos1 overlaps Pos2 at the end.
const sal_Int32 * End() const
Definition: txatbase.hxx:152
bool Check(bool) const
Definition: ndhints.cxx:188
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:58
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
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:189
std::pair< const_iterator, bool > insert(Value &&x)
bool IsFormatIgnoreStart() const
Definition: txatbase.hxx:106
std::vector< SwTextAttr * > m_HintsByWhichAndStart
Definition: ndhints.hxx:78
Pos1 overlaps Pos2 at the beginning.
bool Contains(const SwTextAttr *pHt) const
Definition: ndhints.cxx:169
SwComparePosition
Definition: pam.hxx:64
bool m_bWhichMapNeedsSorting
Definition: ndhints.hxx:94