ITEM: Refactor ItemType
[LibreOffice.git] / sw / source / core / doc / CntntIdxStore.cxx
blob5ef370dbe98baa2ceb1a3f28e550880bd6937815
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <bookmark.hxx>
21 #include <cntfrm.hxx>
22 #include <doc.hxx>
23 #include <IDocumentRedlineAccess.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <docary.hxx>
26 #include <editsh.hxx>
27 #include <fmtanchr.hxx>
28 #include <frmfmt.hxx>
29 #include <functional>
30 #include <mvsave.hxx>
31 #include <node.hxx>
32 #include <pam.hxx>
33 #include <redline.hxx>
34 #include <sal/types.h>
35 #include <unocrsr.hxx>
36 #include <txtfrm.hxx>
37 #include <frameformats.hxx>
38 #include <memory>
40 using namespace ::boost;
41 using namespace ::sw::mark;
43 namespace
45 // #i59534: If a paragraph will be split we have to restore some redline positions
46 // This help function checks a position compared with a node and a content index
48 const int BEFORE_NODE = 0; // Position before the given node index
49 const int BEFORE_SAME_NODE = 1; // Same node index but content index before given content index
50 const int SAME_POSITION = 2; // Same node index and samecontent index
51 const int BEHIND_SAME_NODE = 3; // Same node index but content index behind given content index
52 const int BEHIND_NODE = 4; // Position behind the given node index
54 int lcl_RelativePosition( const SwPosition& rPos, SwNodeOffset nNode, sal_Int32 nContent )
56 SwNodeOffset nIndex = rPos.GetNodeIndex();
57 int nReturn = BEFORE_NODE;
58 if( nIndex == nNode )
60 const sal_Int32 nCntIdx = rPos.GetContentIndex();
61 if( nCntIdx < nContent )
62 nReturn = BEFORE_SAME_NODE;
63 else if( nCntIdx == nContent )
64 nReturn = SAME_POSITION;
65 else
66 nReturn = BEHIND_SAME_NODE;
68 else if( nIndex > nNode )
69 nReturn = BEHIND_NODE;
70 return nReturn;
72 struct MarkEntry
74 tools::Long m_nIdx;
75 bool m_bOther;
76 sal_Int32 m_nContent;
77 #if 0
78 #include <sal/log.hxx>
79 void Dump()
81 SAL_INFO("sw.core", "Index: " << m_nIdx << "\tOther: " << m_bOther << "\tContent: " << m_nContent);
83 #endif
85 struct PaMEntry
87 SwPaM* m_pPaM;
88 bool m_isMark;
89 sal_Int32 m_nContent;
91 struct OffsetUpdater
93 const SwContentNode* m_pNewContentNode;
94 const sal_Int32 m_nOffset;
95 OffsetUpdater(SwContentNode const * pNewContentNode, sal_Int32 nOffset)
96 : m_pNewContentNode(pNewContentNode), m_nOffset(nOffset) {};
97 void operator()(SwPosition& rPos, sal_Int32 nContent) const
99 rPos.Assign(*m_pNewContentNode, nContent + m_nOffset);
102 struct LimitUpdater
104 const SwContentNode* m_pNewContentNode;
105 const sal_uLong m_nLen;
106 const sal_Int32 m_nCorrLen;
107 LimitUpdater(SwContentNode const * pNewContentNode, sal_uLong nLen, sal_Int32 nCorrLen)
108 : m_pNewContentNode(pNewContentNode), m_nLen(nLen), m_nCorrLen(nCorrLen) {};
109 void operator()(SwPosition& rPos, sal_Int32 nContent) const
111 if( nContent < m_nCorrLen )
113 nContent = std::min( nContent, static_cast<sal_Int32>(m_nLen) );
115 else
117 nContent -= m_nCorrLen;
119 rPos.Assign( *m_pNewContentNode, nContent );
122 struct ContentIdxStoreImpl : sw::mark::ContentIdxStore
124 std::vector<MarkEntry> m_aBkmkEntries;
125 std::vector<MarkEntry> m_aRedlineEntries;
126 std::vector<MarkEntry> m_aFlyEntries;
127 std::vector<PaMEntry> m_aUnoCursorEntries;
128 std::vector<PaMEntry> m_aShellCursorEntries;
129 typedef std::function<void (SwPosition& rPos, sal_Int32 nContent)> updater_t;
130 virtual bool Empty() override
132 return m_aBkmkEntries.empty() && m_aRedlineEntries.empty() && m_aFlyEntries.empty() && m_aUnoCursorEntries.empty() && m_aShellCursorEntries.empty();
134 virtual void Save(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit=false) override
136 SaveBkmks(rDoc, nNode, nContent);
137 SaveRedlines(rDoc, nNode, nContent);
138 SaveFlys(rDoc, nNode, nContent, bSaveFlySplit);
139 SaveUnoCursors(rDoc, nNode, nContent);
140 SaveShellCursors(rDoc, nNode, nContent);
142 virtual void Restore(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nOffset=0, bool bAuto = false, bool bAtStart = false, RestoreMode eMode = RestoreMode::All) override
144 SwContentNode* pCNd = rDoc.GetNodes()[ nNode ]->GetContentNode();
145 updater_t aUpdater = OffsetUpdater(pCNd, nOffset);
146 if (eMode & RestoreMode::NonFlys)
148 RestoreBkmks(rDoc, aUpdater);
149 RestoreRedlines(rDoc, aUpdater);
150 RestoreUnoCursors(aUpdater);
151 RestoreShellCursors(aUpdater);
153 if (eMode & RestoreMode::Flys)
155 RestoreFlys(rDoc, aUpdater, bAuto, bAtStart);
158 virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode eMode = RestoreMode::All) override
160 SwContentNode* pCNd = rNd.GetContentNode();
161 SwDoc& rDoc = rNd.GetDoc();
162 updater_t aUpdater = LimitUpdater(pCNd, nLen, nCorrLen);
163 if (eMode & RestoreMode::NonFlys)
165 RestoreBkmks(rDoc, aUpdater);
166 RestoreRedlines(rDoc, aUpdater);
167 RestoreUnoCursors(aUpdater);
168 RestoreShellCursors(aUpdater);
170 if (eMode & RestoreMode::Flys)
172 RestoreFlys(rDoc, aUpdater, false, false);
176 private:
177 void SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
178 void RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater);
179 void SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
180 void RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater);
181 void SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit);
182 void RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart);
183 void SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
184 void RestoreUnoCursors(updater_t const & rUpdater);
185 void SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
186 void RestoreShellCursors(updater_t const & rUpdater);
187 static const SwPosition& GetRightMarkPos(::sw::mark::MarkBase const * pMark, bool bOther)
188 { return bOther ? pMark->GetOtherMarkPos() : pMark->GetMarkPos(); };
189 static void SetRightMarkPos(MarkBase* pMark, bool bOther, const SwPosition* const pPos)
190 { bOther ? pMark->SetOtherMarkPos(*pPos) : pMark->SetMarkPos(*pPos); };
192 void lcl_ChkPaM( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM, const bool bGetPoint, bool bSetMark)
194 const SwPosition* pPos = &rPaM.GetBound(bGetPoint);
195 if( pPos->GetNodeIndex() == nNode && pPos->GetContentIndex() < nContent )
197 const PaMEntry aEntry = { &rPaM, bSetMark, pPos->GetContentIndex() };
198 rPaMEntries.push_back(aEntry);
201 void lcl_ChkPaMBoth( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
203 lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, true);
204 lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, false);
206 void lcl_ChkUnoCrsrPaMBoth(std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
208 lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, false);
209 lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, true);
212 #if 0
213 static void DumpEntries(std::vector<MarkEntry>* pEntries)
215 for (MarkEntry& aEntry : *pEntries)
216 aEntry.Dump();
218 #endif
221 void ContentIdxStoreImpl::SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
223 IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
224 const auto ppBkmkEnd = pMarkAccess->getAllMarksEnd();
225 for(
226 auto ppBkmk = pMarkAccess->getAllMarksBegin();
227 ppBkmk != ppBkmkEnd;
228 ++ppBkmk)
230 const ::sw::mark::MarkBase* pBkmk = *ppBkmk;
231 bool bMarkPosEqual = false;
232 if(pBkmk->GetMarkPos().GetNodeIndex() == nNode
233 && pBkmk->GetMarkPos().GetContentIndex() <= nContent)
235 if(pBkmk->GetMarkPos().GetContentIndex() < nContent)
237 const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().GetContentIndex() };
238 m_aBkmkEntries.push_back(aEntry);
240 else // if a bookmark position is equal nContent, the other position
241 bMarkPosEqual = true; // has to decide if it is added to the array
243 if(pBkmk->IsExpanded()
244 && pBkmk->GetOtherMarkPos().GetNodeIndex() == nNode
245 && pBkmk->GetOtherMarkPos().GetContentIndex() <= nContent)
247 if(bMarkPosEqual)
248 { // the other position is before, the (main) position is equal
249 const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().GetContentIndex() };
250 m_aBkmkEntries.push_back(aEntry);
252 const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), true, pBkmk->GetOtherMarkPos().GetContentIndex() };
253 m_aBkmkEntries.push_back(aEntry);
258 void ContentIdxStoreImpl::RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater)
260 IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
261 sal_Int32 nMinIndexModified = SAL_MAX_INT32;
262 for (const MarkEntry& aEntry : m_aBkmkEntries)
264 if (MarkBase *const pMark = pMarkAccess->getAllMarksBegin()[aEntry.m_nIdx])
266 nMinIndexModified = std::min(nMinIndexModified, sal_Int32(aEntry.m_nIdx));
267 SwPosition aNewPos(GetRightMarkPos(pMark, aEntry.m_bOther));
268 rUpdater(aNewPos, aEntry.m_nContent);
269 SetRightMarkPos(pMark, aEntry.m_bOther, &aNewPos);
272 if (!m_aBkmkEntries.empty())
273 { // tdf#105705 sort bookmarks because SaveBkmks special handling of
274 // "bMarkPosEqual" may destroy sort order
275 pMarkAccess->assureSortedMarkContainers(nMinIndexModified);
279 void ContentIdxStoreImpl::SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
281 SwRedlineTable const & rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
282 tools::Long nIdx = 0;
283 for (const SwRangeRedline* pRdl : rRedlineTable)
285 int nPointPos = lcl_RelativePosition( *pRdl->GetPoint(), nNode, nContent );
286 int nMarkPos = pRdl->HasMark() ? lcl_RelativePosition( *pRdl->GetMark(), nNode, nContent ) :
287 nPointPos;
288 // #i59534: We have to store the positions inside the same node before the insert position
289 // and the one at the insert position if the corresponding Point/Mark position is before
290 // the insert position.
291 if( nPointPos == BEFORE_SAME_NODE ||
292 ( nPointPos == SAME_POSITION && nMarkPos < SAME_POSITION ) )
294 const MarkEntry aEntry = { nIdx, false, pRdl->GetPoint()->GetContentIndex() };
295 m_aRedlineEntries.push_back(aEntry);
297 if( pRdl->HasMark() && ( nMarkPos == BEFORE_SAME_NODE ||
298 ( nMarkPos == SAME_POSITION && nPointPos < SAME_POSITION ) ) )
300 const MarkEntry aEntry = { nIdx, true, pRdl->GetMark()->GetContentIndex() };
301 m_aRedlineEntries.push_back(aEntry);
303 ++nIdx;
307 void ContentIdxStoreImpl::RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater)
309 const SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
310 for (const MarkEntry& aEntry : m_aRedlineEntries)
312 SwPosition* const pPos = aEntry.m_bOther
313 ? rRedlTable[ aEntry.m_nIdx ]->GetMark()
314 : rRedlTable[ aEntry.m_nIdx ]->GetPoint();
315 rUpdater(*pPos, aEntry.m_nContent);
319 void ContentIdxStoreImpl::SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit)
321 SwContentNode *pNode = rDoc.GetNodes()[nNode]->GetContentNode();
322 if( !pNode )
323 return;
324 SwFrame* pFrame = pNode->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
325 if( pFrame )
327 // sw_redlinehide: this looks like an invalid optimisation if merged,
328 // assuming that flys in deleted redlines should be saved here too.
329 if ((!pFrame->IsTextFrame() || !static_cast<SwTextFrame const*>(pFrame)->GetMergedPara())
330 && !pFrame->GetDrawObjs())
331 return; // if we have a layout and no DrawObjs, we can skip this
333 MarkEntry aSave = { 0, false, 0 };
334 for (const SwFrameFormat* pFrameFormat : *rDoc.GetSpzFrameFormats())
336 if ( RES_FLYFRMFMT == pFrameFormat->Which() || RES_DRAWFRMFMT == pFrameFormat->Which() )
338 const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
339 SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
340 if ( pAnchorNode && ( nNode == pAnchorNode->GetIndex() ) &&
341 ( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() ||
342 RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() ) )
344 bool bSkip = false;
345 aSave.m_bOther = false;
346 aSave.m_nContent = rAnchor.GetAnchorContentOffset();
347 if ( RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() )
349 if( nContent <= aSave.m_nContent )
351 if( bSaveFlySplit )
352 aSave.m_bOther = true;
353 else
354 bSkip = true;
357 if(!bSkip)
358 m_aFlyEntries.push_back(aSave);
361 ++aSave.m_nIdx;
365 void ContentIdxStoreImpl::RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart)
367 sw::SpzFrameFormats* pSpz = rDoc.GetSpzFrameFormats();
368 for (const MarkEntry& aEntry : m_aFlyEntries)
370 if(!aEntry.m_bOther)
372 sw::SpzFrameFormat* pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
373 const SwFormatAnchor& rFlyAnchor = pFrameFormat->GetAnchor();
374 if( rFlyAnchor.GetContentAnchor() )
376 if(bAtStart && RndStdIds::FLY_AT_PARA == rFlyAnchor.GetAnchorId())
377 continue;
378 SwFormatAnchor aNew( rFlyAnchor );
379 SwPosition aNewPos( *rFlyAnchor.GetContentAnchor() );
380 rUpdater(aNewPos, aEntry.m_nContent);
381 if ( RndStdIds::FLY_AT_CHAR != rFlyAnchor.GetAnchorId() )
383 aNewPos.nContent.Assign( nullptr, 0 );
385 aNew.SetAnchor( &aNewPos );
386 pFrameFormat->SetFormatAttr( aNew );
389 else if( bAuto )
391 SwFrameFormat* pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
392 const SfxPoolItem* pAnchor = &pFrameFormat->GetAnchor();
393 pFrameFormat->CallSwClientNotify(sw::LegacyModifyHint(pAnchor, pAnchor));
398 void ContentIdxStoreImpl::SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
400 rDoc.cleanupUnoCursorTable();
401 for (const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
403 auto pUnoCursor(pWeakUnoCursor.lock());
404 if(!pUnoCursor)
405 continue;
406 for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
408 lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
410 const SwUnoTableCursor* pUnoTableCursor = dynamic_cast<const SwUnoTableCursor*>(pUnoCursor.get());
411 if( pUnoTableCursor )
413 for(SwPaM& rPaM : const_cast<SwUnoTableCursor*>(pUnoTableCursor)->GetSelRing().GetRingContainer())
415 lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
421 void ContentIdxStoreImpl::RestoreUnoCursors(updater_t const & rUpdater)
423 for (const PaMEntry& aEntry : m_aUnoCursorEntries)
425 rUpdater(aEntry.m_pPaM->GetBound(!aEntry.m_isMark), aEntry.m_nContent);
429 void ContentIdxStoreImpl::SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
431 SwCursorShell* pShell = rDoc.GetEditShell();
432 if (!pShell)
433 return;
434 for(SwViewShell& rCurShell : pShell->GetRingContainer())
436 if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rCurShell) )
438 SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
439 if( _pStackCursor )
440 for (;;)
442 lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, *_pStackCursor);
443 if (!_pStackCursor)
444 break;
445 _pStackCursor = _pStackCursor->GetNext();
446 if (_pStackCursor == pCursorShell->GetStackCursor())
447 break;
450 for(SwPaM& rPaM : pCursorShell->GetCursor_()->GetRingContainer())
452 lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, rPaM);
458 void ContentIdxStoreImpl::RestoreShellCursors(updater_t const & rUpdater)
460 for (const PaMEntry& aEntry : m_aShellCursorEntries)
462 rUpdater(aEntry.m_pPaM->GetBound(aEntry.m_isMark), aEntry.m_nContent);
466 namespace sw::mark {
467 std::shared_ptr<ContentIdxStore> ContentIdxStore::Create()
469 return std::make_shared<ContentIdxStoreImpl>();
472 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */