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 <set>
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 typedef std::set<FieldResult, sortfieldresults> FieldResultSet;
61 
62 namespace {
63 
64 struct block
65 {
66  sal_Int32 m_nStart;
67  sal_Int32 m_nLen;
68  bool m_bVisible;
69  FieldResultSet m_aAttrs;
70  block(sal_Int32 nStart, sal_Int32 nLen, bool bVisible)
71  : m_nStart(nStart), m_nLen(nLen), m_bVisible(bVisible)
72  {
73  }
74 };
75 
76 struct containsPos
77 {
78  const sal_Int32 m_nPos;
79  explicit containsPos(const sal_Int32 nPos)
80  : m_nPos(nPos)
81  {
82  }
83  bool operator() (const block& rIn) const
84  {
85  return m_nPos >= rIn.m_nStart && m_nPos < rIn.m_nStart + rIn.m_nLen;
86  }
87 };
88 
89 }
90 
92  SwRootFrame const*const pLayout, ExpandMode eMode)
93 {
94  const OUString& rNodeText = rNode.GetText();
95  m_aRetText = rNodeText;
96 
97  if (eMode == ExpandMode::PassThrough)
98  return;
99 
100  Range aRange( 0, rNodeText.isEmpty() ? 0 : rNodeText.getLength() - 1);
101  MultiSelection aHiddenMulti(aRange);
102 
103  if (eMode & ExpandMode::HideInvisible)
104  SwScriptInfo::selectHiddenTextProperty(rNode, aHiddenMulti, nullptr);
105 
106  if (eMode & ExpandMode::HideDeletions)
107  SwScriptInfo::selectRedLineDeleted(rNode, aHiddenMulti);
108 
109  std::vector<block> aBlocks;
110 
111  sal_Int32 nShownStart = 0;
112  for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
113  {
114  const Range& rRange = aHiddenMulti.GetRange(i);
115  const sal_Int32 nHiddenStart = rRange.Min();
116  const sal_Int32 nHiddenEnd = rRange.Max() + 1;
117  const sal_Int32 nHiddenLen = nHiddenEnd - nHiddenStart;
118 
119  const sal_Int32 nShownEnd = nHiddenStart;
120  const sal_Int32 nShownLen = nShownEnd - nShownStart;
121 
122  if (nShownLen)
123  aBlocks.emplace_back(nShownStart, nShownLen, true);
124 
125  if (nHiddenLen)
126  aBlocks.emplace_back(nHiddenStart, nHiddenLen, false);
127 
128  nShownStart = nHiddenEnd;
129  }
130 
131  sal_Int32 nTrailingShownLen = rNodeText.getLength() - nShownStart;
132  if (nTrailingShownLen)
133  aBlocks.emplace_back(nShownStart, nTrailingShownLen, true);
134 
136  {
137  //first the normal fields, get their position in the node and what the text they expand
138  //to is
139  const SwpHints* pSwpHints2 = rNode.GetpSwpHints();
140  for ( size_t i = 0; pSwpHints2 && i < pSwpHints2->Count(); ++i )
141  {
142  const SwTextAttr* pAttr = pSwpHints2->Get(i);
143  if (pAttr->HasDummyChar())
144  {
145  const sal_Int32 nDummyCharPos = pAttr->GetStart();
146  if (aHiddenMulti.IsSelected(nDummyCharPos))
147  continue;
148  std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(),
149  aBlocks.end(), containsPos(nDummyCharPos));
150  if (aFind != aBlocks.end())
151  {
152  FieldResult aFieldResult(nDummyCharPos);
153  switch (pAttr->Which())
154  {
155  case RES_TXTATR_FIELD:
157  if (eMode & ExpandMode::ExpandFields)
158  {
159  // add a ZWSP before the expanded field in replace mode
160  aFieldResult.m_sExpand = ((eMode & ExpandMode::ReplaceMode)
161  ? OUString(CHAR_ZWSP) : OUString("")) +
162  static_txtattr_cast<SwTextField const*>(pAttr)->
163  GetFormatField().GetField()->ExpandField(true, pLayout);
164  aFieldResult.m_eType = FieldResult::FIELD;
165  }
166  break;
167  case RES_TXTATR_FTN:
168  if (eMode & ExpandMode::ExpandFootnote)
169  {
170  const SwFormatFootnote& rFootnote = static_cast<SwTextFootnote const*>(pAttr)->GetFootnote();
171  const SwDoc *pDoc = rNode.GetDoc();
172  aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
173  ? OUString(CHAR_ZWSP)
174  : rFootnote.GetViewNumStr(*pDoc, pLayout);
175  aFieldResult.m_eType = FieldResult::FOOTNOTE;
176  }
177  break;
178  default:
179  break;
180  }
181  aFind->m_aAttrs.insert(aFieldResult);
182  }
183  }
184  }
185 
186  if (eMode & ExpandMode::ExpandFields)
187  {
188  //now get the dropdown formfields, get their position in the node and what the text they expand
189  //to is
190  SwPaM aPaM(rNode, 0, rNode, rNode.Len());
191  std::vector<sw::mark::IFieldmark*> aDropDowns =
193 
194  for (sw::mark::IFieldmark *pMark : aDropDowns)
195  {
196  const sal_Int32 nDummyCharPos = pMark->GetMarkPos().nContent.GetIndex()-1;
197  if (aHiddenMulti.IsSelected(nDummyCharPos))
198  continue;
199  std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(), aBlocks.end(),
200  containsPos(nDummyCharPos));
201  if (aFind != aBlocks.end())
202  {
203  FieldResult aFieldResult(nDummyCharPos);
204  aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
205  ? OUString(CHAR_ZWSP)
206  : sw::mark::ExpandFieldmark(pMark);
207  aFieldResult.m_eType = FieldResult::FIELD;
208  aFind->m_aAttrs.insert(aFieldResult);
209  }
210  }
211  }
212  }
213 
214  //store the end of each range in the model and where that end of range
215  //maps to in the view
216  sal_Int32 nOffset = 0;
217  for (const auto& rBlock : aBlocks)
218  {
219  const sal_Int32 nBlockLen = rBlock.m_nLen;
220  if (!nBlockLen)
221  continue;
222  const sal_Int32 nBlockStart = rBlock.m_nStart;
223  const sal_Int32 nBlockEnd = nBlockStart + nBlockLen;
224 
225  if (!rBlock.m_bVisible)
226  {
227  sal_Int32 const modelBlockPos(nBlockEnd);
228  sal_Int32 const viewBlockPos(nBlockStart + nOffset);
229  m_aMap.emplace_back(modelBlockPos, viewBlockPos, false);
230 
231  m_aRetText = m_aRetText.replaceAt(nOffset + nBlockStart, nBlockLen, OUString());
232  nOffset -= nBlockLen;
233  }
234  else
235  {
236  for (const auto& rAttr : rBlock.m_aAttrs)
237  {
238  sal_Int32 const modelFieldPos(rAttr.m_nFieldPos);
239  sal_Int32 const viewFieldPos(rAttr.m_nFieldPos + nOffset);
240  m_aMap.emplace_back(modelFieldPos, viewFieldPos, true );
241 
242  m_aRetText = m_aRetText.replaceAt(viewFieldPos, 1, rAttr.m_sExpand);
243  nOffset += rAttr.m_sExpand.getLength() - 1;
244 
245  switch (rAttr.m_eType)
246  {
247  case FieldResult::FIELD:
248  m_FieldPositions.push_back(viewFieldPos);
249  break;
250  case FieldResult::FOOTNOTE:
251  m_FootnotePositions.push_back(viewFieldPos);
252  break;
253  case FieldResult::NONE: /*ignore*/
254  break;
255  }
256  }
257 
258  sal_Int32 const modelEndBlock(nBlockEnd);
259  sal_Int32 const viewFieldPos(nBlockEnd + nOffset);
260  m_aMap.emplace_back(modelEndBlock, viewFieldPos, true);
261  }
262  }
263 }
264 
267 sal_Int32 ModelToViewHelper::ConvertToViewPosition( sal_Int32 nModelPos ) const
268 {
269  // Search for entry after nPos:
270  auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
271  [nModelPos](const ConversionMapEntry& rEntry) { return rEntry.m_nModelPos >= nModelPos; });
272  if (aIter != m_aMap.end())
273  {
274  //if it's an invisible portion, map all contained positions
275  //to the anchor viewpos
276  if (!aIter->m_bVisible)
277  return aIter->m_nViewPos;
278 
279  //if it's a visible portion, then the view position is the anchor
280  //viewpos - the offset of the input modelpos from the anchor
281  //modelpos
282  const sal_Int32 nOffsetFromEnd = aIter->m_nModelPos - nModelPos;
283  return aIter->m_nViewPos - nOffsetFromEnd;
284  }
285 
286  return nModelPos;
287 }
288 
292 {
293  ModelPosition aRet;
294  aRet.mnPos = nViewPos;
295 
296  // Search for entry after nPos:
297  auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
298  [nViewPos](const ConversionMapEntry& rEntry) { return rEntry.m_nViewPos > nViewPos; });
299 
300  // If nViewPos is in front of first field, we are finished.
301  if (aIter != m_aMap.end() && aIter != m_aMap.begin())
302  {
303  const sal_Int32 nPosModel = aIter->m_nModelPos;
304  const sal_Int32 nPosExpand = aIter->m_nViewPos;
305 
306  --aIter;
307 
308  // nPrevPosModel is the field position
309  const sal_Int32 nPrevPosModel = aIter->m_nModelPos;
310  const sal_Int32 nPrevPosExpand = aIter->m_nViewPos;
311 
312  const sal_Int32 nLengthModel = nPosModel - nPrevPosModel;
313  const sal_Int32 nLengthExpand = nPosExpand - nPrevPosExpand;
314 
315  const sal_Int32 nFieldLengthExpand = nLengthExpand - nLengthModel + 1;
316  const sal_Int32 nFieldEndExpand = nPrevPosExpand + nFieldLengthExpand;
317 
318  // Check if nPos is outside of field:
319  if ( nFieldEndExpand <= nViewPos )
320  {
321  // nPos is outside of field:
322  const sal_Int32 nDistToField = nViewPos - nFieldEndExpand + 1;
323  aRet.mnPos = nPrevPosModel + nDistToField;
324  }
325  else
326  {
327  // nViewPos is inside a field:
328  aRet.mnPos = nPrevPosModel;
329  aRet.mnSubPos = nViewPos - nPrevPosExpand;
330  aRet.mbIsField = true;
331  }
332  }
333 
334  return aRet;
335 }
336 
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
do not expand to content, but replace with zwsp
bool bVisible
size_t m_nPos
const OUString & GetText() const
Definition: ndtxt.hxx:211
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:219
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:274
Definition: doc.hxx:186
bool HasDummyChar() const
Definition: txatbase.hxx:101
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1683
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:79
OUString ExpandFieldmark(IFieldmark *pBM)
Definition: itrform2.cxx:852
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:2459
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
std::set< FieldResult, sortfieldresults > FieldResultSet
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
static void selectRedLineDeleted(const SwTextNode &rNode, MultiSelection &rHiddenMulti, bool bSelect=true)
Definition: porlay.cxx:2530
SwDoc * GetDoc()
Definition: node.hxx:702
For each expanded/hidden portion in the model string, there is an entry in the conversion map...
#define CHAR_ZWSP
Definition: swtypes.hxx:177
long Max() 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
long Min() const
sal_uInt16 nPos