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