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 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);
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);
213 static void DumpEntries(std::vector
<MarkEntry
>* pEntries
)
215 for (MarkEntry
& aEntry
: *pEntries
)
221 void ContentIdxStoreImpl::SaveBkmks(SwDoc
& rDoc
, SwNodeOffset nNode
, sal_Int32 nContent
)
223 IDocumentMarkAccess
* const pMarkAccess
= rDoc
.getIDocumentMarkAccess();
224 const auto ppBkmkEnd
= pMarkAccess
->getAllMarksEnd();
226 auto ppBkmk
= pMarkAccess
->getAllMarksBegin();
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
)
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
) :
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
);
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();
324 SwFrame
* pFrame
= pNode
->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() );
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() ) )
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
)
352 aSave
.m_bOther
= true;
358 m_aFlyEntries
.push_back(aSave
);
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
)
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())
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
);
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());
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();
434 for(SwViewShell
& rCurShell
: pShell
->GetRingContainer())
436 if( auto pCursorShell
= dynamic_cast<SwCursorShell
*>(&rCurShell
) )
438 SwPaM
*_pStackCursor
= pCursorShell
->GetStackCursor();
442 lcl_ChkPaMBoth( m_aShellCursorEntries
, nNode
, nContent
, *_pStackCursor
);
445 _pStackCursor
= _pStackCursor
->GetNext();
446 if (_pStackCursor
== pCursorShell
->GetStackCursor())
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
);
467 std::shared_ptr
<ContentIdxStore
> ContentIdxStore::Create()
469 return std::make_shared
<ContentIdxStoreImpl
>();
472 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */