1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
23 #include <IDocumentRedlineAccess.hxx>
24 #include <IDocumentLayoutAccess.hxx>
27 #include <fmtanchr.hxx>
33 #include <redline.hxx>
34 #include <sal/types.h>
35 #include <unocrsr.hxx>
37 #include <frameformats.hxx>
40 using namespace ::boost
;
41 using namespace ::sw::mark
;
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
;
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
;
66 nReturn
= BEHIND_SAME_NODE
;
68 else if( nIndex
> nNode
)
69 nReturn
= BEHIND_NODE
;
78 #include <sal/log.hxx>
81 SAL_INFO("sw.core", "Index: " << m_nIdx
<< "\tOther: " << m_bOther
<< "\tContent: " << m_nContent
);
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
);
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
) );
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 void Clear() override
132 m_aBkmkEntries
.clear();
133 m_aRedlineEntries
.clear();
134 m_aFlyEntries
.clear();
135 m_aUnoCursorEntries
.clear();
136 m_aShellCursorEntries
.clear();
138 virtual bool Empty() override
140 return m_aBkmkEntries
.empty() && m_aRedlineEntries
.empty() && m_aFlyEntries
.empty() && m_aUnoCursorEntries
.empty() && m_aShellCursorEntries
.empty();
142 virtual void Save(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
, bool bSaveFlySplit
=false) override
144 SaveBkmks(rDoc
, nNode
, nContent
);
145 SaveRedlines(rDoc
, nNode
, nContent
);
146 SaveFlys(rDoc
, nNode
, nContent
, bSaveFlySplit
);
147 SaveUnoCursors(rDoc
, nNode
, nContent
);
148 SaveShellCursors(rDoc
, nNode
, nContent
);
150 virtual void Restore(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nOffset
=0, bool bAuto
= false, bool bAtStart
= false, RestoreMode eMode
= RestoreMode::All
) override
152 SwContentNode
* pCNd
= rDoc
.GetNodes()[ nNode
]->GetContentNode();
153 updater_t aUpdater
= OffsetUpdater(pCNd
, nOffset
);
154 if (eMode
& RestoreMode::NonFlys
)
156 RestoreBkmks(rDoc
, aUpdater
);
157 RestoreRedlines(rDoc
, aUpdater
);
158 RestoreUnoCursors(aUpdater
);
159 RestoreShellCursors(aUpdater
);
161 if (eMode
& RestoreMode::Flys
)
163 RestoreFlys(rDoc
, aUpdater
, bAuto
, bAtStart
);
166 virtual void Restore(SwNode
& rNd
, sal_Int32 nLen
, sal_Int32 nCorrLen
, RestoreMode eMode
= RestoreMode::All
) override
168 SwContentNode
* pCNd
= rNd
.GetContentNode();
169 SwDoc
& rDoc
= rNd
.GetDoc();
170 updater_t aUpdater
= LimitUpdater(pCNd
, nLen
, nCorrLen
);
171 if (eMode
& RestoreMode::NonFlys
)
173 RestoreBkmks(rDoc
, aUpdater
);
174 RestoreRedlines(rDoc
, aUpdater
);
175 RestoreUnoCursors(aUpdater
);
176 RestoreShellCursors(aUpdater
);
178 if (eMode
& RestoreMode::Flys
)
180 RestoreFlys(rDoc
, aUpdater
, false, false);
185 void SaveBkmks(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
);
186 void RestoreBkmks(SwDoc
& rDoc
, updater_t
const & rUpdater
);
187 void SaveRedlines(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
);
188 void RestoreRedlines(SwDoc
& rDoc
, updater_t
const & rUpdater
);
189 void SaveFlys(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
, bool bSaveFlySplit
);
190 void RestoreFlys(SwDoc
& rDoc
, updater_t
const & rUpdater
, bool bAuto
, bool bAtStart
);
191 void SaveUnoCursors(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
);
192 void RestoreUnoCursors(updater_t
const & rUpdater
);
193 void SaveShellCursors(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
);
194 void RestoreShellCursors(updater_t
const & rUpdater
);
195 static const SwPosition
& GetRightMarkPos(::sw::mark::IMark
const * pMark
, bool bOther
)
196 { return bOther
? pMark
->GetOtherMarkPos() : pMark
->GetMarkPos(); };
197 static void SetRightMarkPos(MarkBase
* pMark
, bool bOther
, const SwPosition
* const pPos
)
198 { bOther
? pMark
->SetOtherMarkPos(*pPos
) : pMark
->SetMarkPos(*pPos
); };
200 void lcl_ChkPaM( std::vector
<PaMEntry
>& rPaMEntries
, const SwNodeOffset nNode
, const sal_Int32 nContent
, SwPaM
& rPaM
, const bool bGetPoint
, bool bSetMark
)
202 const SwPosition
* pPos
= &rPaM
.GetBound(bGetPoint
);
203 if( pPos
->GetNodeIndex() == nNode
&& pPos
->GetContentIndex() < nContent
)
205 const PaMEntry aEntry
= { &rPaM
, bSetMark
, pPos
->GetContentIndex() };
206 rPaMEntries
.push_back(aEntry
);
209 void lcl_ChkPaMBoth( std::vector
<PaMEntry
>& rPaMEntries
, const SwNodeOffset nNode
, const sal_Int32 nContent
, SwPaM
& rPaM
)
211 lcl_ChkPaM(rPaMEntries
, nNode
, nContent
, rPaM
, true, true);
212 lcl_ChkPaM(rPaMEntries
, nNode
, nContent
, rPaM
, false, false);
214 void lcl_ChkUnoCrsrPaMBoth(std::vector
<PaMEntry
>& rPaMEntries
, const SwNodeOffset nNode
, const sal_Int32 nContent
, SwPaM
& rPaM
)
216 lcl_ChkPaM(rPaMEntries
, nNode
, nContent
, rPaM
, true, false);
217 lcl_ChkPaM(rPaMEntries
, nNode
, nContent
, rPaM
, false, true);
221 static void DumpEntries(std::vector
<MarkEntry
>* pEntries
)
223 for (MarkEntry
& aEntry
: *pEntries
)
229 void ContentIdxStoreImpl::SaveBkmks(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
)
231 IDocumentMarkAccess
* const pMarkAccess
= rDoc
.getIDocumentMarkAccess();
232 const IDocumentMarkAccess::const_iterator_t ppBkmkEnd
= pMarkAccess
->getAllMarksEnd();
234 IDocumentMarkAccess::const_iterator_t ppBkmk
= pMarkAccess
->getAllMarksBegin();
238 const ::sw::mark::IMark
* pBkmk
= *ppBkmk
;
239 bool bMarkPosEqual
= false;
240 if(pBkmk
->GetMarkPos().GetNodeIndex() == nNode
241 && pBkmk
->GetMarkPos().GetContentIndex() <= nContent
)
243 if(pBkmk
->GetMarkPos().GetContentIndex() < nContent
)
245 const MarkEntry aEntry
= { static_cast<tools::Long
>(ppBkmk
- pMarkAccess
->getAllMarksBegin()), false, pBkmk
->GetMarkPos().GetContentIndex() };
246 m_aBkmkEntries
.push_back(aEntry
);
248 else // if a bookmark position is equal nContent, the other position
249 bMarkPosEqual
= true; // has to decide if it is added to the array
251 if(pBkmk
->IsExpanded()
252 && pBkmk
->GetOtherMarkPos().GetNodeIndex() == nNode
253 && pBkmk
->GetOtherMarkPos().GetContentIndex() <= nContent
)
256 { // the other position is before, the (main) position is equal
257 const MarkEntry aEntry
= { static_cast<tools::Long
>(ppBkmk
- pMarkAccess
->getAllMarksBegin()), false, pBkmk
->GetMarkPos().GetContentIndex() };
258 m_aBkmkEntries
.push_back(aEntry
);
260 const MarkEntry aEntry
= { static_cast<tools::Long
>(ppBkmk
- pMarkAccess
->getAllMarksBegin()), true, pBkmk
->GetOtherMarkPos().GetContentIndex() };
261 m_aBkmkEntries
.push_back(aEntry
);
266 void ContentIdxStoreImpl::RestoreBkmks(SwDoc
& rDoc
, updater_t
const & rUpdater
)
268 IDocumentMarkAccess
* const pMarkAccess
= rDoc
.getIDocumentMarkAccess();
269 for (const MarkEntry
& aEntry
: m_aBkmkEntries
)
271 if (MarkBase
*const pMark
= pMarkAccess
->getAllMarksBegin().get()[aEntry
.m_nIdx
])
273 SwPosition
aNewPos(GetRightMarkPos(pMark
, aEntry
.m_bOther
));
274 rUpdater(aNewPos
, aEntry
.m_nContent
);
275 SetRightMarkPos(pMark
, aEntry
.m_bOther
, &aNewPos
);
278 if (!m_aBkmkEntries
.empty())
279 { // tdf#105705 sort bookmarks because SaveBkmks special handling of
280 // "bMarkPosEqual" may destroy sort order
281 pMarkAccess
->assureSortedMarkContainers();
285 void ContentIdxStoreImpl::SaveRedlines(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
)
287 SwRedlineTable
const & rRedlineTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
288 tools::Long nIdx
= 0;
289 for (const SwRangeRedline
* pRdl
: rRedlineTable
)
291 int nPointPos
= lcl_RelativePosition( *pRdl
->GetPoint(), nNode
, nContent
);
292 int nMarkPos
= pRdl
->HasMark() ? lcl_RelativePosition( *pRdl
->GetMark(), nNode
, nContent
) :
294 // #i59534: We have to store the positions inside the same node before the insert position
295 // and the one at the insert position if the corresponding Point/Mark position is before
296 // the insert position.
297 if( nPointPos
== BEFORE_SAME_NODE
||
298 ( nPointPos
== SAME_POSITION
&& nMarkPos
< SAME_POSITION
) )
300 const MarkEntry aEntry
= { nIdx
, false, pRdl
->GetPoint()->GetContentIndex() };
301 m_aRedlineEntries
.push_back(aEntry
);
303 if( pRdl
->HasMark() && ( nMarkPos
== BEFORE_SAME_NODE
||
304 ( nMarkPos
== SAME_POSITION
&& nPointPos
< SAME_POSITION
) ) )
306 const MarkEntry aEntry
= { nIdx
, true, pRdl
->GetMark()->GetContentIndex() };
307 m_aRedlineEntries
.push_back(aEntry
);
313 void ContentIdxStoreImpl::RestoreRedlines(SwDoc
& rDoc
, updater_t
const & rUpdater
)
315 const SwRedlineTable
& rRedlTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
316 for (const MarkEntry
& aEntry
: m_aRedlineEntries
)
318 SwPosition
* const pPos
= aEntry
.m_bOther
319 ? rRedlTable
[ aEntry
.m_nIdx
]->GetMark()
320 : rRedlTable
[ aEntry
.m_nIdx
]->GetPoint();
321 rUpdater(*pPos
, aEntry
.m_nContent
);
325 void ContentIdxStoreImpl::SaveFlys(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
, bool bSaveFlySplit
)
327 SwContentNode
*pNode
= rDoc
.GetNodes()[nNode
]->GetContentNode();
330 SwFrame
* pFrame
= pNode
->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() );
333 // sw_redlinehide: this looks like an invalid optimisation if merged,
334 // assuming that flys in deleted redlines should be saved here too.
335 if ((!pFrame
->IsTextFrame() || !static_cast<SwTextFrame
const*>(pFrame
)->GetMergedPara())
336 && !pFrame
->GetDrawObjs())
337 return; // if we have a layout and no DrawObjs, we can skip this
339 MarkEntry aSave
= { 0, false, 0 };
340 for (const SwFrameFormat
* pFrameFormat
: *rDoc
.GetSpzFrameFormats())
342 if ( RES_FLYFRMFMT
== pFrameFormat
->Which() || RES_DRAWFRMFMT
== pFrameFormat
->Which() )
344 const SwFormatAnchor
& rAnchor
= pFrameFormat
->GetAnchor();
345 SwNode
const*const pAnchorNode
= rAnchor
.GetAnchorNode();
346 if ( pAnchorNode
&& ( nNode
== pAnchorNode
->GetIndex() ) &&
347 ( RndStdIds::FLY_AT_PARA
== rAnchor
.GetAnchorId() ||
348 RndStdIds::FLY_AT_CHAR
== rAnchor
.GetAnchorId() ) )
351 aSave
.m_bOther
= false;
352 aSave
.m_nContent
= rAnchor
.GetAnchorContentOffset();
353 if ( RndStdIds::FLY_AT_CHAR
== rAnchor
.GetAnchorId() )
355 if( nContent
<= aSave
.m_nContent
)
358 aSave
.m_bOther
= true;
364 m_aFlyEntries
.push_back(aSave
);
371 void ContentIdxStoreImpl::RestoreFlys(SwDoc
& rDoc
, updater_t
const & rUpdater
, bool bAuto
, bool bAtStart
)
373 sw::SpzFrameFormats
* pSpz
= rDoc
.GetSpzFrameFormats();
374 for (const MarkEntry
& aEntry
: m_aFlyEntries
)
378 sw::SpzFrameFormat
* pFrameFormat
= (*pSpz
)[ aEntry
.m_nIdx
];
379 const SwFormatAnchor
& rFlyAnchor
= pFrameFormat
->GetAnchor();
380 if( rFlyAnchor
.GetContentAnchor() )
382 if(bAtStart
&& RndStdIds::FLY_AT_PARA
== rFlyAnchor
.GetAnchorId())
384 SwFormatAnchor
aNew( rFlyAnchor
);
385 SwPosition
aNewPos( *rFlyAnchor
.GetContentAnchor() );
386 rUpdater(aNewPos
, aEntry
.m_nContent
);
387 if ( RndStdIds::FLY_AT_CHAR
!= rFlyAnchor
.GetAnchorId() )
389 aNewPos
.nContent
.Assign( nullptr, 0 );
391 aNew
.SetAnchor( &aNewPos
);
392 pFrameFormat
->SetFormatAttr( aNew
);
397 SwFrameFormat
* pFrameFormat
= (*pSpz
)[ aEntry
.m_nIdx
];
398 const SfxPoolItem
* pAnchor
= &pFrameFormat
->GetAnchor();
399 pFrameFormat
->CallSwClientNotify(sw::LegacyModifyHint(pAnchor
, pAnchor
));
404 void ContentIdxStoreImpl::SaveUnoCursors(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
)
406 rDoc
.cleanupUnoCursorTable();
407 for (const auto& pWeakUnoCursor
: rDoc
.mvUnoCursorTable
)
409 auto pUnoCursor(pWeakUnoCursor
.lock());
412 for(SwPaM
& rPaM
: pUnoCursor
->GetRingContainer())
414 lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries
, nNode
, nContent
, rPaM
);
416 const SwUnoTableCursor
* pUnoTableCursor
= dynamic_cast<const SwUnoTableCursor
*>(pUnoCursor
.get());
417 if( pUnoTableCursor
)
419 for(SwPaM
& rPaM
: const_cast<SwUnoTableCursor
*>(pUnoTableCursor
)->GetSelRing().GetRingContainer())
421 lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries
, nNode
, nContent
, rPaM
);
427 void ContentIdxStoreImpl::RestoreUnoCursors(updater_t
const & rUpdater
)
429 for (const PaMEntry
& aEntry
: m_aUnoCursorEntries
)
431 rUpdater(aEntry
.m_pPaM
->GetBound(!aEntry
.m_isMark
), aEntry
.m_nContent
);
435 void ContentIdxStoreImpl::SaveShellCursors(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
)
437 SwCursorShell
* pShell
= rDoc
.GetEditShell();
440 for(SwViewShell
& rCurShell
: pShell
->GetRingContainer())
442 if( auto pCursorShell
= dynamic_cast<SwCursorShell
*>(&rCurShell
) )
444 SwPaM
*_pStackCursor
= pCursorShell
->GetStackCursor();
448 lcl_ChkPaMBoth( m_aShellCursorEntries
, nNode
, nContent
, *_pStackCursor
);
451 _pStackCursor
= _pStackCursor
->GetNext();
452 if (_pStackCursor
== pCursorShell
->GetStackCursor())
456 for(SwPaM
& rPaM
: pCursorShell
->GetCursor_()->GetRingContainer())
458 lcl_ChkPaMBoth( m_aShellCursorEntries
, nNode
, nContent
, rPaM
);
464 void ContentIdxStoreImpl::RestoreShellCursors(updater_t
const & rUpdater
)
466 for (const PaMEntry
& aEntry
: m_aShellCursorEntries
)
468 rUpdater(aEntry
.m_pPaM
->GetBound(aEntry
.m_isMark
), aEntry
.m_nContent
);
473 std::shared_ptr
<ContentIdxStore
> ContentIdxStore::Create()
475 return std::make_shared
<ContentIdxStoreImpl
>();
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */