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 <svl/eitem.hxx>
23 #include <txatbase.hxx>
24 #include <ndhints.hxx>
25 #include <txtatr.hxx>
26 
27 #ifdef DBG_UTIL
28 #include <pam.hxx>
29 #include <fmtautofmt.hxx>
30 #include <set>
31 #endif
32 
35 static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
36 {
37  const SwTextAttr &rHt1 = *lhs;
38  const SwTextAttr &rHt2 = *rhs;
39  if ( rHt1.GetStart() == rHt2.GetStart() )
40  {
41  const sal_Int32 nHt1 = rHt1.GetAnyEnd();
42  const sal_Int32 nHt2 = rHt2.GetAnyEnd();
43  if ( nHt1 == nHt2 )
44  {
45  const sal_uInt16 nWhich1 = rHt1.Which();
46  const sal_uInt16 nWhich2 = rHt2.Which();
47  if ( nWhich1 == nWhich2 )
48  {
49  if ( RES_TXTATR_CHARFMT == nWhich1 )
50  {
51  const sal_uInt16 nS1 =
52  static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
53  const sal_uInt16 nS2 =
54  static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
55  if ( nS1 != nS2 ) // robust
56  return nS1 < nS2;
57  }
58 
59  return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
60  }
61  // order is important! for requirements see hintids.hxx
62  return ( nWhich1 > nWhich2 );
63  }
64  return ( nHt1 > nHt2 );
65  }
66  return ( rHt1.GetStart() < rHt2.GetStart() );
67 }
68 
70 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
71 {
72  return lhs->Which() < nWhich;
73 }
74 bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
75 {
76  const SwTextAttr &rHt1 = *lhs;
77  const SwTextAttr &rHt2 = *rhs;
78  const sal_uInt16 nWhich1 = rHt1.Which();
79  const sal_uInt16 nWhich2 = rHt2.Which();
80  if ( nWhich1 < nWhich2 )
81  return true;
82  if ( nWhich1 > nWhich2 )
83  return false;
84  if (rHt1.GetStart() < rHt2.GetStart())
85  return true;
86  if (rHt1.GetStart() > rHt2.GetStart())
87  return false;
88  if ( RES_TXTATR_CHARFMT == nWhich1 )
89  {
90  const sal_uInt16 nS1 =
91  static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
92  const sal_uInt16 nS2 =
93  static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
94  if ( nS1 != nS2 ) // robust
95  return nS1 < nS2;
96  }
97  const sal_Int32 nEnd1 = rHt1.GetAnyEnd();
98  const sal_Int32 nEnd2 = rHt2.GetAnyEnd();
99  if ( nEnd1 > nEnd2 )
100  return true;
101  if ( nEnd1 < nEnd2 )
102  return false;
103  return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
104 }
105 
108 bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
109 {
110  return nEndPos < rhs->GetAnyEnd();
111 }
112 bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
113 {
114  const SwTextAttr &rHt1 = *lhs;
115  const SwTextAttr &rHt2 = *rhs;
116  const sal_Int32 nHt1 = rHt1.GetAnyEnd();
117  const sal_Int32 nHt2 = rHt2.GetAnyEnd();
118  if ( nHt1 == nHt2 )
119  {
120  if ( rHt1.GetStart() == rHt2.GetStart() )
121  {
122  const sal_uInt16 nWhich1 = rHt1.Which();
123  const sal_uInt16 nWhich2 = rHt2.Which();
124  if ( nWhich1 == nWhich2 )
125  {
126  if ( RES_TXTATR_CHARFMT == nWhich1 )
127  {
128  const sal_uInt16 nS1 =
129  static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
130  const sal_uInt16 nS2 =
131  static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
132  if ( nS1 != nS2 ) // robust
133  return nS1 > nS2;
134  }
135 
136  return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
137  }
138  // order is important! for requirements see hintids.hxx
139  return ( nWhich1 < nWhich2 );
140  }
141  else
142  return ( rHt1.GetStart() > rHt2.GetStart() );
143  }
144  return ( nHt1 < nHt2 );
145 }
146 
147 void SwpHints::Insert( const SwTextAttr *pHt )
148 {
149  assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt))
150  == m_HintsByStart.end()); // "Insert: hint already in HtStart"
151  assert( pHt->m_pHints == nullptr );
152  const_cast<SwTextAttr*>(pHt)->m_pHints = this;
153 
155  ResortStartMap();
157  ResortEndMap();
159  ResortWhichMap();
160 
161  auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
162  m_HintsByStart.insert(it1, const_cast<SwTextAttr*>(pHt));
163 
164  auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd());
165  m_HintsByEnd.insert(it2, const_cast<SwTextAttr*>(pHt));
166 
167  auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtWhichStart());
168  m_HintsByWhichAndStart.insert(it3, const_cast<SwTextAttr*>(pHt));
169 }
170 
171 bool SwpHints::Contains( const SwTextAttr *pHt ) const
172 {
173  // DO NOT use find() or CHECK here!
174  // if called from SwTextNode::InsertItem, pHt has already been deleted,
175  // so it cannot be dereferenced
176  return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
177  != m_HintsByStart.end();
178 }
179 
180 #ifdef DBG_UTIL
181 
182 #define CHECK_ERR(cond, text) \
183  if(!(cond)) \
184  { \
185  SAL_WARN("sw.core", text); \
186  Resort(); \
187  return false; \
188  }
189 
190 bool SwpHints::Check(bool bPortionsMerged) const
191 {
192  // 1) both arrays have same size
193  CHECK_ERR( m_HintsByStart.size() == m_HintsByEnd.size(),
194  "HintsCheck: wrong sizes" );
195  sal_Int32 nLastStart = 0;
196  sal_Int32 nLastEnd = 0;
197 
198  const SwTextAttr *pLastStart = nullptr;
199  const SwTextAttr *pLastEnd = nullptr;
200  std::set<SwTextAttr const*> RsidOnlyAutoFormats;
201  if (bPortionsMerged)
202  {
203  for (size_t i = 0; i < Count(); ++i)
204  {
205  SwTextAttr const*const pHint(m_HintsByStart[i]);
206  if (RES_TXTATR_AUTOFMT == pHint->Which())
207  {
208  std::shared_ptr<SfxItemSet> const pSet(
209  pHint->GetAutoFormat().GetStyleHandle());
210  if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
211  {
212  RsidOnlyAutoFormats.insert(pHint);
213  }
214  }
215  }
216  }
217 
218  for( size_t i = 0; i < Count(); ++i )
219  {
220  // --- check Starts ---
221 
222  // 2a) valid pointer? depends on overwriting freed mem with 0xFF
223  const SwTextAttr *pHt = m_HintsByStart[i];
224  CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
225 
226  // 3a) start sort order?
227  sal_Int32 nIdx = pHt->GetStart();
228  CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
229 
230  // 4a) IsLessStart consistency
231  if( pLastStart )
232  CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
233 
234  nLastStart = nIdx;
235  pLastStart = pHt;
236 
237  // --- check Ends ---
238 
239  // 2b) valid pointer? see DELETEFF
240  const SwTextAttr *pHtEnd = m_HintsByEnd[i];
241  CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
242 
243  // 3b) end sort order?
244  nIdx = pHtEnd->GetAnyEnd();
245  CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
246 
247  // 4b) IsLessEnd consistency
248  if( pLastEnd )
249  CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
250 
251  nLastEnd = nIdx;
252  pLastEnd = pHtEnd;
253 
254  // --- cross checks ---
255 
256  // 5) same pointers in both arrays
257  if (std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart) == m_HintsByStart.end())
258  nIdx = COMPLETE_STRING;
259 
260  CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetStartOf" );
261 
262  // 6) same pointers in both arrays
263  if (std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd()) == m_HintsByEnd.end())
264  nIdx = COMPLETE_STRING;
265 
266  CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
267 
268  // 7a) character attributes in array?
269  sal_uInt16 nWhich = pHt->Which();
270  CHECK_ERR( !isCHRATR(nWhich),
271  "HintsCheck: Character attribute in start array" );
272 
273  // 7b) character attributes in array?
274  nWhich = pHtEnd->Which();
275  CHECK_ERR( !isCHRATR(nWhich),
276  "HintsCheck: Character attribute in end array" );
277 
278  // 8) style portion check
279  const SwTextAttr* pHtThis = m_HintsByStart[i];
280  const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
281  CHECK_ERR( (0 == i)
282  || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
283  && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
284  || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
285  && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
286  || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
287  || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
288  && (*pHtThis->End() == *pHtLast->End())
289  ) // same range
290  && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
291  || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
292  ) // never two AUTOFMT on same range
293  && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
294  || (pHtLast->Which() != RES_TXTATR_CHARFMT)
295  || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
296  ->GetSortNumber() !=
297  static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
298  ->GetSortNumber())
299  ) // multiple CHARFMT on same range need distinct sorter
300  )
301  || (pHtThis->GetStart() == *pHtThis->End()), // this empty
302  "HintsCheck: Portion inconsistency. "
303  "This can be temporarily ok during undo operations" );
304 
305  // 8 1/2) format ignore start/end flag check
306  // (problems because MergePortions buggy or not called)
307  if (bPortionsMerged)
308  {
309  if (RES_TXTATR_AUTOFMT == pHt->Which() ||
310  RES_TXTATR_CHARFMT == pHt->Which())
311  {
312  // mostly ignore the annoying no-length hints
313  // BuildPortions inserts these in the middle of an existing one
314  bool const bNoLength(pHt->GetStart() == *pHt->End());
315  bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
316  bool bForbidContinuation(!bNoLength && !bNeedContinuation);
317  if (RES_TXTATR_AUTOFMT == pHt->Which())
318  {
319  if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
320  {
321  assert(pHt->IsFormatIgnoreStart());
322  bNeedContinuation = false;
323  // don't forbid continuation - may be other hint here!
324  }
325  }
326  if (bNeedContinuation || bForbidContinuation)
327  {
328  bool bFound(false);
329  for (size_t j = i + 1; j < Count(); ++j)
330  {
331  SwTextAttr *const pOther(m_HintsByStart[j]);
332  if (pOther->GetStart() > *pHt->End())
333  {
334  break; // done
335  }
336  else if (pOther->GetStart() == pOther->GetAnyEnd())
337  {
338  continue; // empty hint: ignore
339  }
340  else if (pOther->GetStart() == *pHt->End())
341  {
342  if (RES_TXTATR_AUTOFMT == pOther->Which() ||
343  RES_TXTATR_CHARFMT == pOther->Which())
344  { // multiple charfmt on same range must all match
345  if (bNeedContinuation)
346  {
347  assert(pOther->IsFormatIgnoreStart());
348  bFound = true;
349  }
350  else if (bForbidContinuation &&
351  (RsidOnlyAutoFormats.find(pOther) ==
352  RsidOnlyAutoFormats.end()))
353  {
354  assert(!pOther->IsFormatIgnoreStart());
355  }
356  }
357  }
358  }
359  if (bNeedContinuation)
360  {
361  assert(bFound); // ? can this happen temp. during undo?
362  }
363  }
364  }
365  else
366  {
367  assert(!pHt->IsFormatIgnoreStart());
368  assert(!pHt->IsFormatIgnoreEnd());
369  }
370  }
371 
372  // 9) nesting portion check
373  if (pHtThis->IsNesting())
374  {
375  for (size_t j = 0; j < i; ++j)
376  {
377  SwTextAttr const * const pOther( m_HintsByStart[j] );
378  if (pOther->IsNesting())
379  {
381  pHtThis->GetStart(), *pHtThis->End(),
382  pOther->GetStart(), *pOther->End());
385  "HintsCheck: overlapping nesting hints!!!" );
386  }
387  }
388  }
389 
390  // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
391  if (pHtThis->HasDummyChar())
392  {
393  for ( size_t j = 0; j < i; ++j )
394  {
395  SwTextAttr const * const pOther( m_HintsByStart[j] );
396  if (pOther->HasDummyChar())
397  {
398  CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
399  "HintsCheck: multiple hints claim same CH_TXTATR!");
400  }
401  }
402  }
403  }
404  return true;
405 }
406 
407 #endif /* DBG_UTIL */
408 
409 // Resort() is called before every Insert and Delete.
410 // Various SwTextNode methods modify hints in a way that violates the
411 // sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
412 // to restore the order.
413 
414 void SwpHints::Resort() const
415 {
416  auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
417  std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
418  auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
419  std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
420  auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
421  std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
422  m_bStartMapNeedsSorting = false;
423  m_bEndMapNeedsSorting = false;
424  m_bWhichMapNeedsSorting = false;
425 }
426 
428 {
429  auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
430  std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
431  m_bStartMapNeedsSorting = false;
432 }
433 
435 {
436  auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
437  std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
438  m_bEndMapNeedsSorting = false;
439 }
440 
442 {
443  m_bWhichMapNeedsSorting = false;
444  auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
445  std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
446 }
447 
448 size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
449 {
451  ResortWhichMap();
452  auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
453  if ( it == m_HintsByWhichAndStart.end() )
454  return SAL_MAX_SIZE;
455  return it - m_HintsByWhichAndStart.begin();
456 }
457 
458 int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
459 {
461  ResortEndMap();
462  auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
463  return it - m_HintsByEnd.begin() - 1;
464 }
465 
466 size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
467 {
469  ResortStartMap();
470  auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
471  if ( it == m_HintsByStart.end() || *it != pHt )
472  return SAL_MAX_SIZE;
473  return it - m_HintsByStart.begin();
474 }
475 
476 /* 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:70
std::vector< SwTextAttr * > m_HintsByEnd
Definition: ndhints.hxx:79
SW_DLLPUBLIC void Resort() const
Definition: ndhints.cxx:414
bool IsFormatIgnoreEnd() const
Definition: txatbase.hxx:103
size_t GetIndexOf(const SwTextAttr *pHt) const
Definition: ndhints.cxx:466
bool IsNesting() const
Definition: txatbase.hxx:100
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:342
SwComparePosition ComparePosition(const T &rStt1, const T &rEnd1, const T &rStt2, const T &rEnd2)
Definition: pam.hxx:77
SwpHints * m_pHints
Definition: txatbase.hxx:61
bool HasDummyChar() const
Definition: txatbase.hxx:101
SW_DLLPUBLIC void ResortWhichMap() const
Definition: ndhints.cxx:441
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:48
#define RES_TXTATR_CHARFMT
Definition: hintids.hxx:142
sal_uInt16 Which() const
Definition: txatbase.hxx:110
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:153
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
void Insert(const SwTextAttr *pHt)
Definition: ndhints.cxx:147
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:108
size_t Count() const
Definition: ndhints.hxx:144
SW_DLLPUBLIC void ResortEndMap() const
Definition: ndhints.cxx:434
int i
size_t GetFirstPosSortedByWhichAndStart(sal_uInt16 nWhich) const
Definition: ndhints.cxx:448
SW_DLLPUBLIC void ResortStartMap() const
Definition: ndhints.cxx:427
int GetLastPosSortedByEnd(sal_Int32 nEndPos) const
Definition: ndhints.cxx:458
#define CHECK_ERR(cond, text)
Definition: ndhints.cxx:182
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:237
std::vector< SwTextAttr * > m_HintsByStart
Definition: ndhints.hxx:78
bool m_bEndMapNeedsSorting
Definition: ndhints.hxx:95
bool m_bStartMapNeedsSorting
Definition: ndhints.hxx:94
#define RES_TXTATR_AUTOFMT
Definition: hintids.hxx:140
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:69
Pos1 overlaps Pos2 at the end.
const sal_Int32 * End() const
Definition: txatbase.hxx:148
bool Check(bool) const
Definition: ndhints.cxx:190
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:35
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:185
bool IsFormatIgnoreStart() const
Definition: txatbase.hxx:102
std::vector< SwTextAttr * > m_HintsByWhichAndStart
Definition: ndhints.hxx:80
const sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:61
Pos1 overlaps Pos2 at the beginning.
bool Contains(const SwTextAttr *pHt) const
Definition: ndhints.cxx:171
SwComparePosition
Definition: pam.hxx:64
#define RES_CHRATR_RSID
Definition: hintids.hxx:107
bool m_bWhichMapNeedsSorting
Definition: ndhints.hxx:96