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 .
22 #include <ndindex.hxx>
24 #include <swtable.hxx>
26 #include <sectfrm.hxx>
29 #include <frmtool.hxx>
30 #include <section.hxx>
31 #include <node2lay.hxx>
32 #include <osl/diagnose.h>
35 * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is
36 * just the public interface.
40 std::unique_ptr
<SwIterator
<SwFrame
, sw::BroadcastingModify
, sw::IteratorMode::UnwrapMulti
>> mpIter
;
41 sw::BroadcastingModify
* mpMod
;
42 std::vector
<SwFrame
*> mvUpperFrames
; // To collect the Upper
43 SwNodeOffset mnIndex
; // The Index of the to-be-inserted Nodes
44 bool mbMaster
: 1; // true => only Master, false => only Frames without Follow
45 bool mbInit
: 1; // Did we already call First() at SwClient?
47 SwNode2LayImpl(const SwNode2LayImpl
&) = delete;
48 SwNode2LayImpl
& operator=(const SwNode2LayImpl
&) = delete;
51 SwNode2LayImpl( const SwNode
& rNode
, SwNodeOffset nIdx
, bool bSearch
);
52 SwFrame
* NextFrame(); // Returns the next "useful" Frame
53 SwLayoutFrame
* UpperFrame( SwFrame
* &rpFrame
, const SwNode
&rNode
);
54 void SaveUpperFrames(); // Saves (and locks if needed) the pUpper
55 // Inserts a Frame under every pUpper of the array
56 void RestoreUpperFrames( SwNodes
& rNds
, SwNodeOffset nStt
, SwNodeOffset nEnd
);
58 SwFrame
* GetFrame( const Point
* pDocPos
) const;
61 static SwNode
* GoNextWithFrame(const SwNodes
& rNodes
, SwNodeIndex
*pIdx
, SwFlowFrame
const**const ppFrame
)
63 if( pIdx
->GetIndex() >= rNodes
.Count() - 1 )
66 SwNodeIndex
aTmp(*pIdx
, +1);
67 SwNode
* pNd
= nullptr;
68 while( aTmp
< rNodes
.Count()-1 )
70 pNd
= &aTmp
.GetNode();
71 SwFrame
const* pFound(nullptr);
72 if ( pNd
->IsContentNode() )
73 // sw_redlinehide: assume that it's OK to find a node with the same
74 // frame as the caller's one
75 pFound
= SwIterator
<SwFrame
, SwContentNode
, sw::IteratorMode::UnwrapMulti
>(*static_cast<SwContentNode
*>(pNd
)).First();
76 else if ( pNd
->IsTableNode() )
77 pFound
= SwIterator
<SwFrame
,SwFormat
>(*static_cast<SwTableNode
*>(pNd
)->GetTable().GetFrameFormat()).First() ;
78 else if( pNd
->IsEndNode() && !pNd
->StartOfSectionNode()->IsSectionNode() )
83 if (pFound
!= nullptr)
87 *ppFrame
= SwFlowFrame::CastFlowFrame(pFound
);
95 if( aTmp
== rNodes
.Count()-SwNodeOffset(1) )
102 static SwNode
* GoPreviousWithFrame(SwNodeIndex
*pIdx
, SwFlowFrame
const**const ppFrame
)
104 if( !pIdx
->GetIndex() )
107 SwNodeIndex
aTmp( *pIdx
, -1 );
108 SwNode
* pNd(nullptr);
109 while( aTmp
.GetIndex() )
111 pNd
= &aTmp
.GetNode();
112 SwFrame
const* pFound(nullptr);
113 if ( pNd
->IsContentNode() )
114 // sw_redlinehide: assume that it's OK to find a node with the same
115 // frame as the caller's one
116 pFound
= SwIterator
<SwFrame
, SwContentNode
, sw::IteratorMode::UnwrapMulti
>(*static_cast<SwContentNode
*>(pNd
)).First();
117 else if ( pNd
->IsTableNode() )
118 pFound
= SwIterator
<SwFrame
,SwFormat
>(*static_cast<SwTableNode
*>(pNd
)->GetTable().GetFrameFormat()).First();
119 else if( pNd
->IsStartNode() && !pNd
->IsSectionNode() )
124 if (pFound
!= nullptr)
128 *ppFrame
= SwFlowFrame::CastFlowFrame(pFound
);
136 if( !aTmp
.GetIndex() )
145 SwFrame
const* FindNeighbourFrameForNode(SwNode
const& rNode
)
147 SwNodeIndex
idx(rNode
);
148 SwFlowFrame
const* pFlow(nullptr);
149 if (SwNode
*const pNode
= GoPreviousWithFrame(&idx
, &pFlow
))
151 if (::CheckNodesRange(rNode
, idx
.GetNode(), true))
153 while (pFlow
->HasFollow())
154 { // try to get the one on the current page
155 pFlow
= pFlow
->GetFollow();
157 return &pFlow
->GetFrame();
161 if (SwNode
*const pNode
= GoNextWithFrame(idx
.GetNodes(), &idx
, &pFlow
))
163 if (::CheckNodesRange(rNode
, idx
.GetNode(), true))
165 while (pFlow
->IsFollow())
166 { // try to get the one on the current page
167 pFlow
= pFlow
->GetPrecede();
169 return &pFlow
->GetFrame();
178 * The main purpose of this ctor is to find the right sw::BroadcastingModify to iterate over.
180 * @param bSearch true: find the next Content or TableNode which contains
181 * Frames (to collect the pUpper).
182 * Else we assume that rNode points already to such a
183 * Content or TableNode.
184 * We insert before or after it.
186 SwNode2LayImpl::SwNode2LayImpl( const SwNode
& rNode
, SwNodeOffset nIdx
, bool bSearch
)
187 : mnIndex( nIdx
), mbInit( false )
190 if( bSearch
|| rNode
.IsSectionNode() )
192 // Find the next Content/TableNode that contains a Frame, so that we can add
193 // ourselves before/after it
194 if( !bSearch
&& rNode
.GetIndex() < mnIndex
)
196 SwNodeIndex
aTmp( *rNode
.EndOfSectionNode(), +1 );
197 pNd
= GoPreviousWithFrame(&aTmp
, nullptr);
198 if( pNd
&& rNode
.GetIndex() > pNd
->GetIndex() )
199 pNd
= nullptr; // Do not go over the limits
204 SwNodeIndex
aTmp( rNode
, -1 );
205 pNd
= GoNextWithFrame(rNode
.GetNodes(), &aTmp
, nullptr);
207 if( !bSearch
&& pNd
&& rNode
.EndOfSectionIndex() < pNd
->GetIndex() )
208 pNd
= nullptr; // Do not go over the limits
214 mbMaster
= mnIndex
< rNode
.GetIndex();
218 if( pNd
->IsContentNode() )
219 mpMod
= const_cast<sw::BroadcastingModify
*>(static_cast<sw::BroadcastingModify
const *>(pNd
->GetContentNode()));
222 assert(pNd
->IsTableNode());
223 mpMod
= pNd
->GetTableNode()->GetTable().GetFrameFormat();
225 mpIter
.reset(new SwIterator
<SwFrame
, sw::BroadcastingModify
, sw::IteratorMode::UnwrapMulti
>(*mpMod
));
235 * Returns the next "useful" Frame.
237 * When calling this method for the first time, a First is triggered at the
238 * actual Iterator. The result is check for suitability: Follows are not
239 * accepted, a Master is accepted when collecting the pUpper and when
240 * inserting before it.
241 * When inserting after it, we find and return the last Follow starting
244 * If the Frame is located in a SectionFrame, we check to see whether the
245 * SectionFrame is the suitable return value (instead of the Frame itself).
246 * This is the case if the to-be-inserted Node is outside of the Section.
248 SwFrame
* SwNode2LayImpl::NextFrame()
255 pRet
= mpIter
->First();
259 pRet
= mpIter
->Next();
262 SwFlowFrame
* pFlow
= SwFlowFrame::CastFlowFrame( pRet
);
264 // Follows are pretty volatile, thus we ignore them.
265 // Even if we insert after the Frame, we start from the Master
266 // and iterate through it until the last Follow
267 if( !pFlow
->IsFollow() )
271 while( pFlow
->HasFollow() )
272 pFlow
= pFlow
->GetFollow();
273 pRet
= &(pFlow
->GetFrame());
275 if( pRet
->IsInSct() )
277 SwSectionFrame
* pSct
= pRet
->FindSctFrame();
278 // ATTENTION: If we are in a Footnote, from a Layout point of view
279 // it could be located in a Section with columns, although it
280 // should be outside of it when looking at the Nodes.
281 // Thus, when dealing with Footnotes, we need to check whether the
282 // SectionFrame is also located within the Footnote and not outside of it.
283 if( !pRet
->IsInFootnote() || pSct
->IsInFootnote() )
285 assert(pSct
&& pSct
->GetSection());
286 SwSectionNode
* pNd
= pSct
->GetSection()->GetFormat()->GetSectionNode();
288 // If the result Frame is located within a Section Frame
289 // whose Section does not contain the Node, we return with
290 // the SectionFrame, else we return with the Content/TabFrame
293 if( pNd
->GetIndex() >= mnIndex
)
296 else if( pNd
->EndOfSectionIndex() < mnIndex
)
302 pRet
= mpIter
->Next();
307 void SwNode2LayImpl::SaveUpperFrames()
310 while( nullptr != (pFrame
= NextFrame()) )
312 SwFrame
* pPrv
= pFrame
->GetPrev();
313 pFrame
= pFrame
->GetUpper();
316 if( pFrame
->IsFootnoteFrame() )
317 static_cast<SwFootnoteFrame
*>(pFrame
)->ColLock();
318 else if( pFrame
->IsInSct() )
319 pFrame
->FindSctFrame()->ColLock();
320 if( pPrv
&& pPrv
->IsSctFrame() )
321 static_cast<SwSectionFrame
*>(pPrv
)->LockJoin();
322 mvUpperFrames
.push_back( pPrv
);
323 mvUpperFrames
.push_back( pFrame
);
330 SwLayoutFrame
* SwNode2LayImpl::UpperFrame( SwFrame
* &rpFrame
, const SwNode
&rNode
)
332 rpFrame
= NextFrame();
335 SwLayoutFrame
* pUpper
= rpFrame
->GetUpper();
336 if( rpFrame
->IsSctFrame() )
338 const SwNode
* pNode
= rNode
.StartOfSectionNode();
339 if( pNode
->IsSectionNode() )
341 SwFrame
* pFrame
= mbMaster
? rpFrame
->FindPrev() : rpFrame
->FindNext();
342 if( pFrame
&& pFrame
->IsSctFrame() )
344 // pFrame could be a "dummy"-section
345 if( static_cast<SwSectionFrame
*>(pFrame
)->GetSection() &&
346 (&static_cast<const SwSectionNode
*>(pNode
)->GetSection() ==
347 static_cast<SwSectionFrame
*>(pFrame
)->GetSection()) )
349 // #i22922# - consider columned sections
350 // 'Go down' the section frame as long as the layout frame
351 // is found, which would contain content.
352 while ( pFrame
->IsLayoutFrame() &&
353 static_cast<SwLayoutFrame
*>(pFrame
)->Lower() &&
354 !static_cast<SwLayoutFrame
*>(pFrame
)->Lower()->IsFlowFrame() &&
355 static_cast<SwLayoutFrame
*>(pFrame
)->Lower()->IsLayoutFrame() )
357 pFrame
= static_cast<SwLayoutFrame
*>(pFrame
)->Lower();
359 assert(pFrame
->IsLayoutFrame());
360 rpFrame
= mbMaster
? nullptr
361 : static_cast<SwLayoutFrame
*>(pFrame
)->Lower();
362 assert((!rpFrame
|| rpFrame
->IsFlowFrame()) &&
363 "<SwNode2LayImpl::UpperFrame(..)> - expected sibling isn't a flow frame." );
364 return static_cast<SwLayoutFrame
*>(pFrame
);
367 pUpper
= new SwSectionFrame(const_cast<SwSectionNode
*>(static_cast<const SwSectionNode
*>(pNode
))->GetSection(), rpFrame
);
368 pUpper
->Paste( rpFrame
->GetUpper(),
369 mbMaster
? rpFrame
: rpFrame
->GetNext() );
370 // coverity[freed_arg : FALSE] - pUpper->Lower() is not freed here
371 static_cast<SwSectionFrame
*>(pUpper
)->Init();
373 // 'Go down' the section frame as long as the layout frame
374 // is found, which would contain content.
375 while ( pUpper
->Lower() &&
376 !pUpper
->Lower()->IsFlowFrame() &&
377 pUpper
->Lower()->IsLayoutFrame() )
379 pUpper
= static_cast<SwLayoutFrame
*>(pUpper
->Lower());
386 rpFrame
= rpFrame
->GetNext();
390 void SwNode2LayImpl::RestoreUpperFrames( SwNodes
& rNds
, SwNodeOffset nStt
, SwNodeOffset nEnd
)
393 SwDoc
& rDoc
= rNds
.GetDoc();
395 for( ; nStt
< nEnd
; ++nStt
)
397 SwFrame
* pNew
= nullptr;
400 if( (pNd
= rNds
[nStt
])->IsContentNode() )
401 for( std::vector
<SwFrame
*>::size_type n
= 0; n
< mvUpperFrames
.size(); )
403 pNxt
= mvUpperFrames
[n
++];
404 if( bFirst
&& pNxt
&& pNxt
->IsSctFrame() )
405 static_cast<SwSectionFrame
*>(pNxt
)->UnlockJoin();
406 pUp
= static_cast<SwLayoutFrame
*>(mvUpperFrames
[n
++]);
408 pNxt
= pNxt
->GetNext();
411 pNew
= static_cast<SwContentNode
*>(pNd
)->MakeFrame( pUp
);
412 pNew
->Paste( pUp
, pNxt
);
413 mvUpperFrames
[n
-2] = pNew
;
415 else if( pNd
->IsTableNode() )
416 for( std::vector
<SwFrame
*>::size_type x
= 0; x
< mvUpperFrames
.size(); )
418 pNxt
= mvUpperFrames
[x
++];
419 if( bFirst
&& pNxt
&& pNxt
->IsSctFrame() )
420 static_cast<SwSectionFrame
*>(pNxt
)->UnlockJoin();
421 pUp
= static_cast<SwLayoutFrame
*>(mvUpperFrames
[x
++]);
423 pNxt
= pNxt
->GetNext();
426 pNew
= static_cast<SwTableNode
*>(pNd
)->MakeFrame( pUp
);
427 assert(pNew
->IsTabFrame());
428 pNew
->Paste( pUp
, pNxt
);
429 static_cast<SwTabFrame
*>(pNew
)->RegistFlys();
430 mvUpperFrames
[x
-2] = pNew
;
432 else if( pNd
->IsSectionNode() )
434 nStt
= pNd
->EndOfSectionIndex();
435 for( std::vector
<SwFrame
*>::size_type x
= 0; x
< mvUpperFrames
.size(); )
437 pNxt
= mvUpperFrames
[x
++];
438 if( bFirst
&& pNxt
&& pNxt
->IsSctFrame() )
439 static_cast<SwSectionFrame
*>(pNxt
)->UnlockJoin();
440 pUp
= static_cast<SwLayoutFrame
*>(mvUpperFrames
[x
++]);
441 OSL_ENSURE( pUp
->GetUpper() || pUp
->IsFlyFrame(), "Lost Upper" );
442 ::InsertCnt_( pUp
, &rDoc
, pNd
->GetIndex(), false, nStt
+1, pNxt
);
443 pNxt
= pUp
->GetLastLower();
444 mvUpperFrames
[x
-2] = pNxt
;
449 for( std::vector
<SwFrame
*>::size_type x
= 0; x
< mvUpperFrames
.size(); ++x
)
451 SwFrame
* pTmp
= mvUpperFrames
[++x
];
452 if( pTmp
->IsFootnoteFrame() )
453 static_cast<SwFootnoteFrame
*>(pTmp
)->ColUnlock();
454 else if ( pTmp
->IsInSct() )
456 SwSectionFrame
* pSctFrame
= pTmp
->FindSctFrame();
457 pSctFrame
->ColUnlock();
458 // #i18103# - invalidate size of section in order to
459 // assure, that the section is formatted, unless it was 'Collocked'
460 // from its 'collection' until its 'restoration'.
461 pSctFrame
->InvalidateSize_();
466 SwFrame
* SwNode2LayImpl::GetFrame( const Point
* pDocPos
) const
468 // test if change of member pIter -> pMod broke anything
469 std::pair
<Point
, bool> tmp
;
472 tmp
.first
= *pDocPos
;
475 return mpMod
? ::GetFrameOfModify(nullptr, *mpMod
, FRM_ALL
, nullptr, pDocPos
? &tmp
: nullptr) : nullptr;
478 SwNode2Layout::SwNode2Layout( const SwNode
& rNd
, SwNodeOffset nIdx
)
479 : m_pImpl( new SwNode2LayImpl( rNd
, nIdx
, false ) )
483 SwNode2LayoutSaveUpperFrames::SwNode2LayoutSaveUpperFrames(const SwNode
& rNd
)
484 : m_pImpl( new SwNode2LayImpl( rNd
, rNd
.GetIndex(), true ) )
486 m_pImpl
->SaveUpperFrames();
489 void SwNode2LayoutSaveUpperFrames::RestoreUpperFrames(
490 SwNodes
& rNds
, SwNodeOffset
const nStt
, SwNodeOffset
const nEnd
)
492 m_pImpl
->RestoreUpperFrames( rNds
, nStt
, nEnd
);
495 SwFrame
* SwNode2Layout::NextFrame()
497 return m_pImpl
->NextFrame();
500 SwLayoutFrame
* SwNode2Layout::UpperFrame( SwFrame
* &rpFrame
, const SwNode
&rNode
)
502 return m_pImpl
->UpperFrame( rpFrame
, rNode
);
505 SwNode2Layout::~SwNode2Layout()
509 SwNode2LayoutSaveUpperFrames::~SwNode2LayoutSaveUpperFrames()
513 SwFrame
* SwNode2Layout::GetFrame( const Point
* pDocPos
) const
515 return m_pImpl
->GetFrame( pDocPos
);
518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */