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>
34#include <bookmark.hxx>
36#include <deque>
37#include <vector>
38
39namespace {
40
41struct FieldResult
42{
43 sal_Int32 m_nFieldPos;
44 OUString m_sExpand;
45 enum { NONE, FIELD, FOOTNOTE } m_eType;
46 explicit FieldResult(sal_Int32 const nPos)
47 : m_nFieldPos(nPos), m_eType(NONE)
48 { }
49};
50
51class sortfieldresults
52{
53public:
54 bool operator()(const FieldResult &rOne, const FieldResult &rTwo) const
55 {
56 return rOne.m_nFieldPos < rTwo.m_nFieldPos;
57 }
58};
59
60}
61
62namespace {
63
64struct block
65{
66 sal_Int32 m_nStart;
67 sal_Int32 m_nLen;
68 bool m_bVisible;
70 block(sal_Int32 nStart, sal_Int32 nLen, bool bVisible)
71 : m_nStart(nStart), m_nLen(nLen), m_bVisible(bVisible)
72 {
73 }
74};
75
76struct 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
98 return;
99
100 Range aRange( 0, rNodeText.isEmpty() ? 0 : rNodeText.getLength() - 1);
101 MultiSelection aHiddenMulti(aRange);
102
104 SwScriptInfo::selectHiddenTextProperty(rNode, aHiddenMulti, nullptr);
105
107 SwScriptInfo::selectRedLineDeleted(rNode, aHiddenMulti);
108
110 {
111 // hide fieldmark commands
112 IDocumentMarkAccess const& rIDMA(*rNode.GetDoc().getIDocumentMarkAccess());
113 ::std::deque<::std::pair<sw::mark::IFieldmark const*, bool>> startedFields;
114 SwPaM cursor(rNode, 0);
115 while (true)
116 {
117 sw::mark::IFieldmark const* pFieldMark(nullptr);
118 while (true) // loop to skip NonTextFieldmarks, those are handled later
119 {
120 pFieldMark = rIDMA.getInnerFieldmarkFor(*cursor.GetPoint());
121 if (pFieldMark == nullptr
122 || pFieldMark->GetMarkStart().GetNode().GetTextNode()->GetText()[
123 pFieldMark->GetMarkStart().GetContentIndex()]
125 {
126 break;
127 }
128 pFieldMark = nullptr;
129 if (!cursor.Move(fnMoveBackward, GoInContent))
130 {
131 break;
132 }
133 }
134 if (!pFieldMark)
135 {
136 break;
137 }
138 assert(pFieldMark->GetMarkStart().GetNode().GetTextNode()->GetText()[pFieldMark->GetMarkStart().GetContentIndex()] != CH_TXT_ATR_FORMELEMENT);
139 // getInnerFieldmarkFor may also return one that starts at rNode,0 -
140 // skip it, must be handled in loop below
141 if (pFieldMark->GetMarkStart().GetNode() < rNode)
142 {
143 // this can be a nested field's end - skip over those!
144 if (pFieldMark->GetMarkEnd().GetNode() < rNode)
145 {
146 assert(cursor.GetPoint()->GetNode().GetTextNode()->GetText()[cursor.GetPoint()->GetContentIndex()] == CH_TXT_ATR_FIELDEND);
147 }
148 else
149 {
150 SwPosition const sepPos(::sw::mark::FindFieldSep(*pFieldMark));
151 startedFields.emplace_front(pFieldMark, sepPos.GetNode() < rNode);
152 }
153 *cursor.GetPoint() = pFieldMark->GetMarkStart();
154 }
155 if (!cursor.Move(fnMoveBackward, GoInContent))
156 {
157 break;
158 }
159 }
160 ::std::optional<sal_Int32> oStartHidden;
161 if (!::std::all_of(startedFields.begin(), startedFields.end(),
162 [](auto const& it) { return it.second; }))
163 {
164 oStartHidden.emplace(0); // node starts out hidden as field command
165 }
166 for (sal_Int32 i = 0; i < rNode.GetText().getLength(); ++i)
167 {
168 switch (rNode.GetText()[i])
169 {
171 {
172 auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rNode, i)));
173 assert(pFieldMark);
174 startedFields.emplace_back(pFieldMark, false);
175 if (!oStartHidden)
176 {
177 oStartHidden.emplace(i);
178 }
179 break;
180 }
182 {
183 assert(startedFields.back().first->IsCoveringPosition(SwPosition(rNode, i)));
184 startedFields.back().second = true;
185 assert(oStartHidden);
186 if (::std::all_of(startedFields.begin(), startedFields.end(),
187 [](auto const& it) { return it.second; }))
188 {
189// prevent -Werror=maybe-uninitialized under gcc 11.2.0
190#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 11 && __GNUC__ <= 12
191#pragma GCC diagnostic push
192#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
193#endif
194 // i is still hidden but the Range end is oddly "-1"
195 aHiddenMulti.Select({*oStartHidden, i}, true);
196 oStartHidden.reset();
197#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 11 && __GNUC__ <= 12
198#pragma GCC diagnostic pop
199#endif
200 }
201 break;
202 }
204 {
205 assert(startedFields.back().first == rIDMA.getFieldmarkAt(SwPosition(rNode, i)));
206 startedFields.pop_back();
207 aHiddenMulti.Select({i, i}, true);
208 break;
209 }
210 }
211 }
212 if (oStartHidden && rNode.Len() != 0)
213 {
214 aHiddenMulti.Select({*oStartHidden, rNode.Len() - 1}, true);
215 }
216 }
217 else if (eMode & ExpandMode::ExpandFields) // subset: only hide dummy chars
218 {
219 for (sal_Int32 i = 0; i < rNode.GetText().getLength(); ++i)
220 {
221 switch (rNode.GetText()[i])
222 {
226 {
227 aHiddenMulti.Select({i, i}, true);
228 break;
229 }
230 }
231 }
232 }
233
234 std::vector<block> aBlocks;
235
236 sal_Int32 nShownStart = 0;
237 for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
238 {
239 const Range& rRange = aHiddenMulti.GetRange(i);
240 const sal_Int32 nHiddenStart = rRange.Min();
241 const sal_Int32 nHiddenEnd = rRange.Max() + 1;
242 const sal_Int32 nHiddenLen = nHiddenEnd - nHiddenStart;
243
244 const sal_Int32 nShownEnd = nHiddenStart;
245 const sal_Int32 nShownLen = nShownEnd - nShownStart;
246
247 if (nShownLen)
248 aBlocks.emplace_back(nShownStart, nShownLen, true);
249
250 if (nHiddenLen)
251 aBlocks.emplace_back(nHiddenStart, nHiddenLen, false);
252
253 nShownStart = nHiddenEnd;
254 }
255
256 sal_Int32 nTrailingShownLen = rNodeText.getLength() - nShownStart;
257 if (nTrailingShownLen)
258 aBlocks.emplace_back(nShownStart, nTrailingShownLen, true);
259
261 {
262 //first the normal fields, get their position in the node and what the text they expand
263 //to is
264 const SwpHints* pSwpHints2 = rNode.GetpSwpHints();
265 for ( size_t i = 0; pSwpHints2 && i < pSwpHints2->Count(); ++i )
266 {
267 const SwTextAttr* pAttr = pSwpHints2->Get(i);
268 if (pAttr->HasDummyChar())
269 {
270 const sal_Int32 nDummyCharPos = pAttr->GetStart();
271 if (aHiddenMulti.IsSelected(nDummyCharPos))
272 continue;
273 std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(),
274 aBlocks.end(), containsPos(nDummyCharPos));
275 if (aFind != aBlocks.end())
276 {
277 FieldResult aFieldResult(nDummyCharPos);
278 switch (pAttr->Which())
279 {
282 {
283 // this uses CH_TXTATR_INWORD so replace with nothing
284 aFieldResult.m_eType = FieldResult::FIELD;
285 }
286 break;
287 case RES_TXTATR_FIELD:
289 {
290 // add a ZWSP before the expanded field in replace mode
291 aFieldResult.m_sExpand = ((eMode & ExpandMode::ReplaceMode)
292 ? OUString(CHAR_ZWSP) : OUString("")) +
293 static_txtattr_cast<SwTextField const*>(pAttr)->
294 GetFormatField().GetField()->ExpandField(true, pLayout);
295 aFieldResult.m_eType = FieldResult::FIELD;
296 }
297 break;
298 case RES_TXTATR_FTN:
300 {
301 const SwFormatFootnote& rFootnote = static_cast<SwTextFootnote const*>(pAttr)->GetFootnote();
302 const SwDoc& rDoc = rNode.GetDoc();
303 aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
304 ? OUString(CHAR_ZWSP)
305 : rFootnote.GetViewNumStr(rDoc, pLayout);
306 aFieldResult.m_eType = FieldResult::FOOTNOTE;
307 }
308 break;
309 default:
310 break;
311 }
312 aFind->m_aAttrs.insert(aFieldResult);
313 }
314 }
315 }
316
318 {
319 //now get the dropdown formfields, get their position in the node and what the text they expand
320 //to is
321 SwPaM aPaM(rNode, 0, rNode, rNode.Len());
322 std::vector<sw::mark::IFieldmark*> aNoTextFieldmarks =
324
325 for (sw::mark::IFieldmark *const pMark : aNoTextFieldmarks)
326 {
327 const sal_Int32 nDummyCharPos = pMark->GetMarkStart().GetContentIndex();
328 if (aHiddenMulti.IsSelected(nDummyCharPos))
329 continue;
330 std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(), aBlocks.end(),
331 containsPos(nDummyCharPos));
332 if (aFind != aBlocks.end())
333 {
334 FieldResult aFieldResult(nDummyCharPos);
335 aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
336 ? OUString(CHAR_ZWSP)
338 aFieldResult.m_eType = FieldResult::FIELD;
339 aFind->m_aAttrs.insert(aFieldResult);
340 }
341 }
342 }
343 }
344
345 //store the end of each range in the model and where that end of range
346 //maps to in the view
347 sal_Int32 nOffset = 0;
348 for (const auto& rBlock : aBlocks)
349 {
350 const sal_Int32 nBlockLen = rBlock.m_nLen;
351 if (!nBlockLen)
352 continue;
353 const sal_Int32 nBlockStart = rBlock.m_nStart;
354 const sal_Int32 nBlockEnd = nBlockStart + nBlockLen;
355
356 if (!rBlock.m_bVisible)
357 {
358 sal_Int32 const modelBlockPos(nBlockEnd);
359 sal_Int32 const viewBlockPos(nBlockStart + nOffset);
360 m_aMap.emplace_back(modelBlockPos, viewBlockPos, false);
361
362 m_aRetText = m_aRetText.replaceAt(nOffset + nBlockStart, nBlockLen, u"");
363 nOffset -= nBlockLen;
364 }
365 else
366 {
367 for (const auto& rAttr : rBlock.m_aAttrs)
368 {
369 sal_Int32 const modelFieldPos(rAttr.m_nFieldPos);
370 sal_Int32 const viewFieldPos(rAttr.m_nFieldPos + nOffset);
371 m_aMap.emplace_back(modelFieldPos, viewFieldPos, true );
372
373 m_aRetText = m_aRetText.replaceAt(viewFieldPos, 1, rAttr.m_sExpand);
374 nOffset += rAttr.m_sExpand.getLength() - 1;
375
376 switch (rAttr.m_eType)
377 {
378 case FieldResult::FIELD:
379 m_FieldPositions.push_back(viewFieldPos);
380 break;
381 case FieldResult::FOOTNOTE:
382 m_FootnotePositions.push_back(viewFieldPos);
383 break;
384 case FieldResult::NONE: /*ignore*/
385 break;
386 }
387 }
388
389 sal_Int32 const modelEndBlock(nBlockEnd);
390 sal_Int32 const viewFieldPos(nBlockEnd + nOffset);
391 m_aMap.emplace_back(modelEndBlock, viewFieldPos, true);
392 }
393 }
394}
395
398sal_Int32 ModelToViewHelper::ConvertToViewPosition( sal_Int32 nModelPos ) const
399{
400 // Search for entry after nPos:
401 auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
402 [nModelPos](const ConversionMapEntry& rEntry) { return rEntry.m_nModelPos >= nModelPos; });
403 if (aIter != m_aMap.end())
404 {
405 //if it's an invisible portion, map all contained positions
406 //to the anchor viewpos
407 if (!aIter->m_bVisible)
408 return aIter->m_nViewPos;
409
410 //if it's a visible portion, then the view position is the anchor
411 //viewpos - the offset of the input modelpos from the anchor
412 //modelpos
413 const sal_Int32 nOffsetFromEnd = aIter->m_nModelPos - nModelPos;
414 return aIter->m_nViewPos - nOffsetFromEnd;
415 }
416
417 return nModelPos;
418}
419
423{
424 ModelPosition aRet;
425 aRet.mnPos = nViewPos;
426
427 // Search for entry after nPos:
428 auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
429 [nViewPos](const ConversionMapEntry& rEntry) { return rEntry.m_nViewPos > nViewPos; });
430
431 // If nViewPos is in front of first field, we are finished.
432 if (aIter != m_aMap.end() && aIter != m_aMap.begin())
433 {
434 const sal_Int32 nPosModel = aIter->m_nModelPos;
435 const sal_Int32 nPosExpand = aIter->m_nViewPos;
436
437 --aIter;
438
439 // nPrevPosModel is the field position
440 const sal_Int32 nPrevPosModel = aIter->m_nModelPos;
441 const sal_Int32 nPrevPosExpand = aIter->m_nViewPos;
442
443 const sal_Int32 nLengthModel = nPosModel - nPrevPosModel;
444 const sal_Int32 nLengthExpand = nPosExpand - nPrevPosExpand;
445
446 const sal_Int32 nFieldLengthExpand = nLengthExpand - nLengthModel + 1;
447 const sal_Int32 nFieldEndExpand = nPrevPosExpand + nFieldLengthExpand;
448
449 // Check if nPos is outside of field:
450 if ( nFieldEndExpand <= nViewPos )
451 {
452 // nPos is outside of field:
453 const sal_Int32 nDistToField = nViewPos - nFieldEndExpand + 1;
454 aRet.mnPos = nPrevPosModel + nDistToField;
455 }
456 else
457 {
458 // nViewPos is inside a field:
459 aRet.mnPos = nPrevPosModel;
460 aRet.mnSubPos = nViewPos - nPrevPosExpand;
461 aRet.mbIsField = true;
462 }
463 }
464
465 return aRet;
466}
467
468/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Provides access to the marks of a document.
virtual std::vector<::sw::mark::IFieldmark * > getNoTextFieldmarksIn(const SwPaM &rPaM) const =0
virtual ::sw::mark::IFieldmark * getFieldmarkAt(const SwPosition &rPos) const =0
get Fieldmark for CH_TXT_ATR_FIELDSTART/CH_TXT_ATR_FIELDEND at rPos
virtual sw::mark::IFieldmark * getInnerFieldmarkFor(const SwPosition &pos) const =0
ModelPosition ConvertToModelPosition(sal_Int32 nViewPos) const
Converts a view position into a model position.
std::vector< sal_Int32 > m_FieldPositions
store positions of fields and footnotes for grammar checkers
sal_Int32 ConvertToViewPosition(sal_Int32 nModelPos) const
Converts a model position into a view position.
std::vector< sal_Int32 > m_FootnotePositions
const Range & GetRange(sal_Int32 nRange) const
sal_Int32 GetRangeCount() const
bool Select(sal_Int32 nIndex, bool bSelect=true)
bool IsSelected(sal_Int32 nIndex) const
tools::Long Max() const
tools::Long Min() const
Definition: doc.hxx:197
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1890
SfxPoolItem subclass for footnotes and endnotes, stored in the anchor text node.
Definition: fmtftn.hxx:47
OUString GetViewNumStr(const SwDoc &rDoc, SwRootFrame const *pLayout, bool bInclStrings=false) const
Returns string to be displayed of footnote / endnote.
Definition: atrftn.cxx:218
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:901
SwDoc & GetDoc()
Definition: node.hxx:233
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:657
const SwPosition * GetPoint() const
Definition: pam.hxx:253
The root element of a Writer document layout.
Definition: rootfrm.hxx:85
static void selectHiddenTextProperty(const SwTextNode &rNode, MultiSelection &rHiddenMulti, std::vector< std::pair< sw::mark::IBookmark const *, MarkKind > > *pBookmarks)
Definition: porlay.cxx:2802
static void selectRedLineDeleted(const SwTextNode &rNode, MultiSelection &rHiddenMulti, bool bSelect=true)
Definition: porlay.cxx:2874
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
bool HasDummyChar() const
Definition: txatbase.hxx:107
sal_uInt16 Which() const
Definition: txatbase.hxx:116
SwTextAttr subclass for footnotes and endnotes.
Definition: txtftn.hxx:34
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:291
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:252
const OUString & GetText() const
Definition: ndtxt.hxx:244
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
size_t Count() const
Definition: ndhints.hxx:142
float u
const EnumerationType m_eType
size_t m_nPos
#define CH_TXT_ATR_FIELDSEP
Definition: hintids.hxx:184
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:181
constexpr TypedWhichId< SwFormatFootnote > RES_TXTATR_FTN(59)
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(60)
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:185
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:183
Mode eMode
sal_uInt16 nPos
ExpandMode
Some helpers for converting model strings to view strings.
@ ReplaceMode
do not expand to content, but replace with zwsp
@ HideFieldmarkCommands
double getLength(const B2DPolygon &rCandidate)
NONE
int i
SwPosition FindFieldSep(IFieldmark const &rMark)
return position of the CH_TXT_ATR_FIELDSEP for rMark
OUString ExpandFieldmark(IFieldmark *pBM)
Definition: itrform2.cxx:1120
FOOTNOTE
bool GoInContent(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:1203
SwMoveFnCollection const & fnMoveBackward
Definition: paminit.cxx:60
For each expanded/hidden portion in the model string, there is an entry in the conversion map.
This struct defines a position in the model string.
Marks a position in the document model.
Definition: pam.hxx:38
SwNode & GetNode() const
Definition: pam.hxx:81
sal_Int32 GetContentIndex() const
Definition: pam.hxx:85
#define CHAR_ZWSP
Definition: swtypes.hxx:178
bool bVisible