LibreOffice Module sw (master)  1
modeltoviewhelper.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 <tools/multisel.hxx>
21 #include <doc.hxx>
22 #include <IMark.hxx>
23 #include <fldbas.hxx>
24 #include <fmtfld.hxx>
25 #include <fmtftn.hxx>
26 #include <modeltoviewhelper.hxx>
27 #include <ndtxt.hxx>
28 #include <pam.hxx>
29 #include <txatbase.hxx>
30 #include <txtfld.hxx>
31 #include <txtftn.hxx>
32 #include <scriptinfo.hxx>
33 #include <IDocumentMarkAccess.hxx>
34 #include <o3tl/sorted_vector.hxx>
35 #include <vector>
36 
37 namespace {
38 
39 struct FieldResult
40 {
41  sal_Int32 m_nFieldPos;
42  OUString m_sExpand;
43  enum { NONE, FIELD, FOOTNOTE } m_eType;
44  explicit FieldResult(sal_Int32 const nPos)
45  : m_nFieldPos(nPos), m_eType(NONE)
46  { }
47 };
48 
49 class sortfieldresults
50 {
51 public:
52  bool operator()(const FieldResult &rOne, const FieldResult &rTwo) const
53  {
54  return rOne.m_nFieldPos < rTwo.m_nFieldPos;
55  }
56 };
57 
58 }
59 
60 namespace {
61 
62 struct block
63 {
64  sal_Int32 m_nStart;
65  sal_Int32 m_nLen;
66  bool m_bVisible;
68  block(sal_Int32 nStart, sal_Int32 nLen, bool bVisible)
69  : m_nStart(nStart), m_nLen(nLen), m_bVisible(bVisible)
70  {
71  }
72 };
73 
74 struct containsPos
75 {
76  const sal_Int32 m_nPos;
77  explicit containsPos(const sal_Int32 nPos)
78  : m_nPos(nPos)
79  {
80  }
81  bool operator() (const block& rIn) const
82  {
83  return m_nPos >= rIn.m_nStart && m_nPos < rIn.m_nStart + rIn.m_nLen;
84  }
85 };
86 
87 }
88 
90  SwRootFrame const*const pLayout, ExpandMode eMode)
91 {
92  const OUString& rNodeText = rNode.GetText();
93  m_aRetText = rNodeText;
94 
95  if (eMode == ExpandMode::PassThrough)
96  return;
97 
98  Range aRange( 0, rNodeText.isEmpty() ? 0 : rNodeText.getLength() - 1);
99  MultiSelection aHiddenMulti(aRange);
100 
101  if (eMode & ExpandMode::HideInvisible)
102  SwScriptInfo::selectHiddenTextProperty(rNode, aHiddenMulti, nullptr);
103 
104  if (eMode & ExpandMode::HideDeletions)
105  SwScriptInfo::selectRedLineDeleted(rNode, aHiddenMulti);
106 
107  std::vector<block> aBlocks;
108 
109  sal_Int32 nShownStart = 0;
110  for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
111  {
112  const Range& rRange = aHiddenMulti.GetRange(i);
113  const sal_Int32 nHiddenStart = rRange.Min();
114  const sal_Int32 nHiddenEnd = rRange.Max() + 1;
115  const sal_Int32 nHiddenLen = nHiddenEnd - nHiddenStart;
116 
117  const sal_Int32 nShownEnd = nHiddenStart;
118  const sal_Int32 nShownLen = nShownEnd - nShownStart;
119 
120  if (nShownLen)
121  aBlocks.emplace_back(nShownStart, nShownLen, true);
122 
123  if (nHiddenLen)
124  aBlocks.emplace_back(nHiddenStart, nHiddenLen, false);
125 
126  nShownStart = nHiddenEnd;
127  }
128 
129  sal_Int32 nTrailingShownLen = rNodeText.getLength() - nShownStart;
130  if (nTrailingShownLen)
131  aBlocks.emplace_back(nShownStart, nTrailingShownLen, true);
132 
134  {
135  //first the normal fields, get their position in the node and what the text they expand
136  //to is
137  const SwpHints* pSwpHints2 = rNode.GetpSwpHints();
138  for ( size_t i = 0; pSwpHints2 && i < pSwpHints2->Count(); ++i )
139  {
140  const SwTextAttr* pAttr = pSwpHints2->Get(i);
141  if (pAttr->HasDummyChar())
142  {
143  const sal_Int32 nDummyCharPos = pAttr->GetStart();
144  if (aHiddenMulti.IsSelected(nDummyCharPos))
145  continue;
146  std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(),
147  aBlocks.end(), containsPos(nDummyCharPos));
148  if (aFind != aBlocks.end())
149  {
150  FieldResult aFieldResult(nDummyCharPos);
151  switch (pAttr->Which())
152  {
153  case RES_TXTATR_FIELD:
155  if (eMode & ExpandMode::ExpandFields)
156  {
157  // add a ZWSP before the expanded field in replace mode
158  aFieldResult.m_sExpand = ((eMode & ExpandMode::ReplaceMode)
159  ? OUString(CHAR_ZWSP) : OUString("")) +
160  static_txtattr_cast<SwTextField const*>(pAttr)->
161  GetFormatField().GetField()->ExpandField(true, pLayout);
162  aFieldResult.m_eType = FieldResult::FIELD;
163  }
164  break;
165  case RES_TXTATR_FTN:
166  if (eMode & ExpandMode::ExpandFootnote)
167  {
168  const SwFormatFootnote& rFootnote = static_cast<SwTextFootnote const*>(pAttr)->GetFootnote();
169  const SwDoc& rDoc = rNode.GetDoc();
170  aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
171  ? OUString(CHAR_ZWSP)
172  : rFootnote.GetViewNumStr(rDoc, pLayout);
173  aFieldResult.m_eType = FieldResult::FOOTNOTE;
174  }
175  break;
176  default:
177  break;
178  }
179  aFind->m_aAttrs.insert(aFieldResult);
180  }
181  }
182  }
183 
184  if (eMode & ExpandMode::ExpandFields)
185  {
186  //now get the dropdown formfields, get their position in the node and what the text they expand
187  //to is
188  SwPaM aPaM(rNode, 0, rNode, rNode.Len());
189  std::vector<sw::mark::IFieldmark*> aDropDowns =
191 
192  for (sw::mark::IFieldmark *pMark : aDropDowns)
193  {
194  const sal_Int32 nDummyCharPos = pMark->GetMarkPos().nContent.GetIndex()-1;
195  if (aHiddenMulti.IsSelected(nDummyCharPos))
196  continue;
197  std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(), aBlocks.end(),
198  containsPos(nDummyCharPos));
199  if (aFind != aBlocks.end())
200  {
201  FieldResult aFieldResult(nDummyCharPos);
202  aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
203  ? OUString(CHAR_ZWSP)
204  : sw::mark::ExpandFieldmark(pMark);
205  aFieldResult.m_eType = FieldResult::FIELD;
206  aFind->m_aAttrs.insert(aFieldResult);
207  }
208  }
209  }
210  }
211 
212  //store the end of each range in the model and where that end of range
213  //maps to in the view
214  sal_Int32 nOffset = 0;
215  for (const auto& rBlock : aBlocks)
216  {
217  const sal_Int32 nBlockLen = rBlock.m_nLen;
218  if (!nBlockLen)
219  continue;
220  const sal_Int32 nBlockStart = rBlock.m_nStart;
221  const sal_Int32 nBlockEnd = nBlockStart + nBlockLen;
222 
223  if (!rBlock.m_bVisible)
224  {
225  sal_Int32 const modelBlockPos(nBlockEnd);
226  sal_Int32 const viewBlockPos(nBlockStart + nOffset);
227  m_aMap.emplace_back(modelBlockPos, viewBlockPos, false);
228 
229  m_aRetText = m_aRetText.replaceAt(nOffset + nBlockStart, nBlockLen, OUString());
230  nOffset -= nBlockLen;
231  }
232  else
233  {
234  for (const auto& rAttr : rBlock.m_aAttrs)
235  {
236  sal_Int32 const modelFieldPos(rAttr.m_nFieldPos);
237  sal_Int32 const viewFieldPos(rAttr.m_nFieldPos + nOffset);
238  m_aMap.emplace_back(modelFieldPos, viewFieldPos, true );
239 
240  m_aRetText = m_aRetText.replaceAt(viewFieldPos, 1, rAttr.m_sExpand);
241  nOffset += rAttr.m_sExpand.getLength() - 1;
242 
243  switch (rAttr.m_eType)
244  {
245  case FieldResult::FIELD:
246  m_FieldPositions.push_back(viewFieldPos);
247  break;
248  case FieldResult::FOOTNOTE:
249  m_FootnotePositions.push_back(viewFieldPos);
250  break;
251  case FieldResult::NONE: /*ignore*/
252  break;
253  }
254  }
255 
256  sal_Int32 const modelEndBlock(nBlockEnd);
257  sal_Int32 const viewFieldPos(nBlockEnd + nOffset);
258  m_aMap.emplace_back(modelEndBlock, viewFieldPos, true);
259  }
260  }
261 }
262 
265 sal_Int32 ModelToViewHelper::ConvertToViewPosition( sal_Int32 nModelPos ) const
266 {
267  // Search for entry after nPos:
268  auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
269  [nModelPos](const ConversionMapEntry& rEntry) { return rEntry.m_nModelPos >= nModelPos; });
270  if (aIter != m_aMap.end())
271  {
272  //if it's an invisible portion, map all contained positions
273  //to the anchor viewpos
274  if (!aIter->m_bVisible)
275  return aIter->m_nViewPos;
276 
277  //if it's a visible portion, then the view position is the anchor
278  //viewpos - the offset of the input modelpos from the anchor
279  //modelpos
280  const sal_Int32 nOffsetFromEnd = aIter->m_nModelPos - nModelPos;
281  return aIter->m_nViewPos - nOffsetFromEnd;
282  }
283 
284  return nModelPos;
285 }
286 
290 {
291  ModelPosition aRet;
292  aRet.mnPos = nViewPos;
293 
294  // Search for entry after nPos:
295  auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
296  [nViewPos](const ConversionMapEntry& rEntry) { return rEntry.m_nViewPos > nViewPos; });
297 
298  // If nViewPos is in front of first field, we are finished.
299  if (aIter != m_aMap.end() && aIter != m_aMap.begin())
300  {
301  const sal_Int32 nPosModel = aIter->m_nModelPos;
302  const sal_Int32 nPosExpand = aIter->m_nViewPos;
303 
304  --aIter;
305 
306  // nPrevPosModel is the field position
307  const sal_Int32 nPrevPosModel = aIter->m_nModelPos;
308  const sal_Int32 nPrevPosExpand = aIter->m_nViewPos;
309 
310  const sal_Int32 nLengthModel = nPosModel - nPrevPosModel;
311  const sal_Int32 nLengthExpand = nPosExpand - nPrevPosExpand;
312 
313  const sal_Int32 nFieldLengthExpand = nLengthExpand - nLengthModel + 1;
314  const sal_Int32 nFieldEndExpand = nPrevPosExpand + nFieldLengthExpand;
315 
316  // Check if nPos is outside of field:
317  if ( nFieldEndExpand <= nViewPos )
318  {
319  // nPos is outside of field:
320  const sal_Int32 nDistToField = nViewPos - nFieldEndExpand + 1;
321  aRet.mnPos = nPrevPosModel + nDistToField;
322  }
323  else
324  {
325  // nViewPos is inside a field:
326  aRet.mnPos = nPrevPosModel;
327  aRet.mnSubPos = nViewPos - nPrevPosExpand;
328  aRet.mbIsField = true;
329  }
330  }
331 
332  return aRet;
333 }
334 
335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
do not expand to content, but replace with zwsp
size_t m_nPos
const OUString & GetText() const
Definition: ndtxt.hxx:212
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:220
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:275
Definition: doc.hxx:186
bool HasDummyChar() const
Definition: txatbase.hxx:101
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1704
This struct defines a position in the model string.
sal_uInt16 Which() const
Definition: txatbase.hxx:110
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(59)
The root element of a Writer document layout.
Definition: rootfrm.hxx:82
OUString ExpandFieldmark(IFieldmark *pBM)
Definition: itrform2.cxx:862
OUString GetViewNumStr(const SwDoc &rDoc, SwRootFrame const *pLayout, bool bInclStrings=false) const
Returns string to be displayed of footnote / endnote.
Definition: atrftn.cxx:213
static void selectHiddenTextProperty(const SwTextNode &rNode, MultiSelection &rHiddenMulti, std::vector< std::pair< sw::mark::IBookmark const *, MarkKind >> *pBookmarks)
Definition: porlay.cxx:2494
NONE
sal_Int32 ConvertToViewPosition(sal_Int32 nModelPos) const
Converts a model position into a view position.
std::vector< sal_Int32 > m_FieldPositions
store positions of fields and footnotes for grammar checkers
virtual std::vector< ::sw::mark::IFieldmark * > getDropDownsFor(const SwPaM &rPaM) const =0
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
constexpr TypedWhichId< SwFormatFootnote > RES_TXTATR_FTN(58)
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
size_t Count() const
Definition: ndhints.hxx:142
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
int i
SwDoc & GetDoc()
Definition: node.hxx:211
static void selectRedLineDeleted(const SwTextNode &rNode, MultiSelection &rHiddenMulti, bool bSelect=true)
Definition: porlay.cxx:2587
For each expanded/hidden portion in the model string, there is an entry in the conversion map...
#define CHAR_ZWSP
Definition: swtypes.hxx:169
tools::Long Min() const
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:80
ExpandMode
Some helpers for converting model strings to view strings.
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
std::vector< sal_Int32 > m_FootnotePositions
ModelPosition ConvertToModelPosition(sal_Int32 nViewPos) const
Converts a view position into a model position.
const EnumerationType m_eType
tools::Long Max() const