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 .
19 #include <DocumentLayoutManager.hxx>
21 #include <IDocumentState.hxx>
22 #include <IDocumentUndoRedo.hxx>
23 #include <DocumentContentOperationsManager.hxx>
24 #include <IDocumentStylePoolAccess.hxx>
27 #include <layouter.hxx>
28 #include <poolfmt.hxx>
30 #include <fmtcntnt.hxx>
31 #include <fmtcnct.hxx>
33 #include <fmtanchr.hxx>
34 #include <txtflcnt.hxx>
35 #include <fmtflcnt.hxx>
37 #include <unoframe.hxx>
38 #include <textboxhelper.hxx>
39 #include <ndindex.hxx>
41 #include <frameformats.hxx>
42 #include <com/sun/star/embed/EmbedStates.hpp>
43 #include <svx/svdobj.hxx>
44 #include <svx/svdpage.hxx>
45 #include <osl/diagnose.h>
46 #include <vcl/scheduler.hxx>
48 using namespace ::com::sun::star
;
53 DocumentLayoutManager::DocumentLayoutManager( SwDoc
& i_rSwdoc
) :
55 mpCurrentView( nullptr )
59 const SwViewShell
*DocumentLayoutManager::GetCurrentViewShell() const
64 SwViewShell
*DocumentLayoutManager::GetCurrentViewShell()
69 void DocumentLayoutManager::SetCurrentViewShell( SwViewShell
* pNew
)
74 // It must be able to communicate to a SwViewShell. This is going to be removed later.
75 const SwRootFrame
*DocumentLayoutManager::GetCurrentLayout() const
77 if(GetCurrentViewShell())
78 return GetCurrentViewShell()->GetLayout();
82 SwRootFrame
*DocumentLayoutManager::GetCurrentLayout()
84 if(GetCurrentViewShell())
85 return GetCurrentViewShell()->GetLayout();
89 bool DocumentLayoutManager::HasLayout() const
91 // if there is a view, there is always a layout
92 return (mpCurrentView
!= nullptr);
95 SwLayouter
* DocumentLayoutManager::GetLayouter()
97 return mpLayouter
.get();
100 const SwLayouter
* DocumentLayoutManager::GetLayouter() const
102 return mpLayouter
.get();
105 void DocumentLayoutManager::SetLayouter( SwLayouter
* pNew
)
107 mpLayouter
.reset( pNew
);
110 /** Create a new format whose settings fit to the Request by default.
112 The format is put into the respective format array.
113 If there already is a fitting format, it is returned instead. */
114 SwFrameFormat
*DocumentLayoutManager::MakeLayoutFormat( RndStdIds eRequest
, const SfxItemSet
* pSet
)
116 SwFrameFormat
*pFormat
= nullptr;
117 const bool bMod
= m_rDoc
.getIDocumentState().IsModified();
118 bool bHeader
= false;
122 case RndStdIds::HEADER
:
123 case RndStdIds::HEADERL
:
124 case RndStdIds::HEADERR
:
129 case RndStdIds::FOOTER
:
131 pFormat
= new SwFrameFormat( m_rDoc
.GetAttrPool(),
132 (bHeader
? u
"Right header"_ustr
: u
"Right footer"_ustr
),
133 m_rDoc
.GetDfltFrameFormat() );
135 const SwNode
& rEndOfAutotext( m_rDoc
.GetNodes().GetEndOfAutotext() );
136 SwStartNode
* pSttNd
=
137 m_rDoc
.GetNodes().MakeTextSection
139 bHeader
? SwHeaderStartNode
: SwFooterStartNode
,
140 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing
<sal_uInt16
>( bHeader
141 ? ( eRequest
== RndStdIds::HEADERL
142 ? RES_POOLCOLL_HEADERL
143 : eRequest
== RndStdIds::HEADERR
144 ? RES_POOLCOLL_HEADERR
145 : RES_POOLCOLL_HEADER
)
146 : RES_POOLCOLL_FOOTER
148 pFormat
->SetFormatAttr( SwFormatContent( pSttNd
));
150 if( pSet
) // Set a few more attributes
151 pFormat
->SetFormatAttr( *pSet
);
153 // Why set it back? Doc has changed, or not?
154 // In any case, wrong for the FlyFrames!
156 m_rDoc
.getIDocumentState().ResetModified();
160 case RndStdIds::DRAW_OBJECT
:
162 pFormat
= m_rDoc
.MakeDrawFrameFormat( OUString(), m_rDoc
.GetDfltFrameFormat() );
163 if( pSet
) // Set a few more attributes
164 pFormat
->SetFormatAttr( *pSet
);
166 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
168 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
169 std::make_unique
<SwUndoInsLayFormat
>(pFormat
, SwNodeOffset(0), 0));
174 #if OSL_DEBUG_LEVEL > 0
175 case RndStdIds::FLY_AT_PAGE
:
176 case RndStdIds::FLY_AT_CHAR
:
177 case RndStdIds::FLY_AT_FLY
:
178 case RndStdIds::FLY_AT_PARA
:
179 case RndStdIds::FLY_AS_CHAR
:
180 OSL_FAIL( "use new interface instead: SwDoc::MakeFlySection!" );
186 "LayoutFormat was requested with an invalid Request." );
192 /// Deletes the denoted format and its content.
193 void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat
*pFormat
)
195 // Do not paint, until the destruction is complete. Paint may access the layout and nodes
196 // while it's in inconsistent state, and crash.
197 Scheduler::IdlesLockGuard g
;
198 // A chain of frames needs to be merged, if necessary,
199 // so that the Frame's contents are adjusted accordingly before we destroy the Frames.
200 const SwFormatChain
&rChain
= pFormat
->GetChain();
201 if ( rChain
.GetPrev() )
203 SwFormatChain
aChain( rChain
.GetPrev()->GetChain() );
204 aChain
.SetNext( rChain
.GetNext() );
205 m_rDoc
.SetAttr( aChain
, *rChain
.GetPrev() );
207 if ( rChain
.GetNext() )
209 SwFormatChain
aChain( rChain
.GetNext()->GetChain() );
210 aChain
.SetPrev( rChain
.GetPrev() );
211 m_rDoc
.SetAttr( aChain
, *rChain
.GetNext() );
214 const SwNodeIndex
* pCntIdx
= nullptr;
215 // The draw format doesn't own its content, it just has a pointer to it.
216 if (pFormat
->Which() != RES_DRAWFRMFMT
)
217 pCntIdx
= pFormat
->GetContent().GetContentIdx();
218 if (pCntIdx
&& !m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
220 // Disconnect if it's an OLE object
221 SwOLENode
* pOLENd
= m_rDoc
.GetNodes()[ pCntIdx
->GetIndex()+1 ]->GetOLENode();
222 if( pOLENd
&& pOLENd
->GetOLEObj().IsOleRef() )
226 pOLENd
->GetOLEObj().GetOleRef()->changeState( embed::EmbedStates::LOADED
);
228 catch ( uno::Exception
& )
235 pFormat
->DelFrames();
237 // Only FlyFrames are undoable at first
238 const sal_uInt16 nWh
= pFormat
->Which();
239 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo() &&
240 (RES_FLYFRMFMT
== nWh
|| RES_DRAWFRMFMT
== nWh
))
242 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoDelLayFormat
>( pFormat
));
246 // #i32089# - delete at-frame anchored objects
247 if ( nWh
== RES_FLYFRMFMT
)
249 // determine frame formats of at-frame anchored objects
250 const SwNodeIndex
* pContentIdx
= nullptr;
251 if (pFormat
->Which() != RES_DRAWFRMFMT
)
252 pContentIdx
= pFormat
->GetContent().GetContentIdx();
255 sw::SpzFrameFormats
* pSpzs
= pFormat
->GetDoc()->GetSpzFrameFormats();
258 std::vector
<SwFrameFormat
*> aToDeleteFrameFormats
;
259 const SwNodeOffset
nNodeIdxOfFlyFormat( pContentIdx
->GetIndex() );
261 for(sw::SpzFrameFormat
* pSpz
: *pSpzs
)
263 const SwFormatAnchor
&rAnch
= pSpz
->GetAnchor();
264 if ( rAnch
.GetAnchorId() == RndStdIds::FLY_AT_FLY
&&
265 rAnch
.GetAnchorNode()->GetIndex() == nNodeIdxOfFlyFormat
)
267 aToDeleteFrameFormats
.push_back(pSpz
);
271 // delete found frame formats
272 while ( !aToDeleteFrameFormats
.empty() )
274 SwFrameFormat
* pTmpFormat
= aToDeleteFrameFormats
.back();
275 pFormat
->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pTmpFormat
);
277 aToDeleteFrameFormats
.pop_back();
286 SwNode
*pNode
= &pCntIdx
->GetNode();
287 const_cast<SwFormatContent
&>(pFormat
->GetFormatAttr( RES_CNTNT
)).SetNewContentIdx( nullptr );
288 m_rDoc
.getIDocumentContentOperations().DeleteSection( pNode
);
291 // Delete the character for FlyFrames anchored as char (if necessary)
292 const SwFormatAnchor
& rAnchor
= pFormat
->GetAnchor();
293 if ((RndStdIds::FLY_AS_CHAR
== rAnchor
.GetAnchorId()) && rAnchor
.GetAnchorNode())
295 SwTextNode
*pTextNd
= rAnchor
.GetAnchorNode()->GetTextNode();
297 // attribute is still in text node, delete it
300 SwTextFlyCnt
* const pAttr
= static_cast<SwTextFlyCnt
*>(
301 pTextNd
->GetTextAttrForCharAt( rAnchor
.GetAnchorContentOffset(),
302 RES_TXTATR_FLYCNT
));
303 if ( pAttr
&& (pAttr
->GetFlyCnt().GetFrameFormat() == pFormat
) )
305 // don't delete, set pointer to 0
306 const_cast<SwFormatFlyCnt
&>(pAttr
->GetFlyCnt()).SetFlyFormat();
307 pTextNd
->EraseText( *rAnchor
.GetContentAnchor(), 1 );
312 m_rDoc
.DelFrameFormat( pFormat
);
314 m_rDoc
.getIDocumentState().SetModified();
317 /** Copies the stated format (pSrc) to pDest and returns pDest.
319 If there's no pDest, it is created.
320 If the source format is located in another document, also copy correctly
322 The Anchor attribute's position is always set to 0! */
323 SwFrameFormat
*DocumentLayoutManager::CopyLayoutFormat(
324 const SwFrameFormat
& rSource
,
325 const SwFormatAnchor
& rNewAnchor
,
329 const bool bFly
= RES_FLYFRMFMT
== rSource
.Which();
330 const bool bDraw
= RES_DRAWFRMFMT
== rSource
.Which();
331 OSL_ENSURE( bFly
|| bDraw
, "this method only works for fly or draw" );
333 SwDoc
* pSrcDoc
= const_cast<SwDoc
*>(rSource
.GetDoc());
335 // May we copy this object?
336 // We may, unless it's 1) it's a control (and therefore a draw)
337 // 2) anchored in a header/footer
338 // 3) anchored (to paragraph?)
339 bool bMayNotCopy
= false;
340 const SwNode
* pCAnchor
= rNewAnchor
.GetAnchorNode();
341 bool bInHeaderFooter
= pCAnchor
&& m_rDoc
.IsInHeaderFooter(*pCAnchor
);
344 bool bCheckControlLayer
= false;
345 rSource
.CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer
));
347 bCheckControlLayer
&&
348 ((RndStdIds::FLY_AT_PARA
== rNewAnchor
.GetAnchorId()) || (RndStdIds::FLY_AT_FLY
== rNewAnchor
.GetAnchorId()) || (RndStdIds::FLY_AT_CHAR
== rNewAnchor
.GetAnchorId())) &&
352 // just return if we can't copy this
356 SwFrameFormat
* pDest
= m_rDoc
.GetDfltFrameFormat();
357 if( rSource
.GetRegisteredIn() != pSrcDoc
->GetDfltFrameFormat() )
358 pDest
= m_rDoc
.CopyFrameFormat( *static_cast<const SwFrameFormat
*>(rSource
.GetRegisteredIn()) );
362 // To do a correct cloning concerning the ZOrder for all objects
363 // it is necessary to actually create a draw object for fly frames, too.
364 // These are then added to the DrawingLayer (which needs to exist).
365 // Together with correct sorting of all drawinglayer based objects
366 // before cloning ZOrder transfer works correctly then.
367 SwFlyFrameFormat
*pFormat
= m_rDoc
.MakeFlyFrameFormat( rSource
.GetName(), pDest
);
370 SwXFrame::GetOrCreateSdrObject(*pFormat
);
373 pDest
= m_rDoc
.MakeDrawFrameFormat( OUString(), pDest
);
375 // Copy all other or new attributes
376 pDest
->CopyAttrs( rSource
);
378 // Do not copy chains
379 pDest
->ResetFormatAttr( RES_CHAIN
);
383 // Duplicate the content.
384 const SwNode
& rCSttNd
= rSource
.GetContent().GetContentIdx()->GetNode();
385 SwNodeRange
aRg( rCSttNd
, SwNodeOffset(1), *rCSttNd
.EndOfSectionNode() );
387 SwStartNode
* pSttNd
= SwNodes::MakeEmptySection( m_rDoc
.GetNodes().GetEndOfAutotext(), SwFlyStartNode
);
389 // Set the Anchor/ContentIndex first.
390 // Within the copying part, we can access the values (DrawFormat in Headers and Footers)
391 SwNodeIndex
aIdx( *pSttNd
);
392 SwFormatContent
aAttr( rSource
.GetContent() );
393 aAttr
.SetNewContentIdx( &aIdx
);
394 pDest
->SetFormatAttr( aAttr
);
395 pDest
->SetFormatAttr( rNewAnchor
);
397 if( !m_rDoc
.IsCopyIsMove() || &m_rDoc
!= pSrcDoc
)
399 if( (m_rDoc
.IsInReading() && !bInHeaderFooter
) || m_rDoc
.IsInMailMerge() )
400 pDest
->SetFormatName( OUString() );
403 // Test first if the name is already taken, if so generate a new one.
404 SwNodeType nNdTyp
= aRg
.aStart
.GetNode().GetNodeType();
406 OUString
sOld( pDest
->GetName() );
407 pDest
->SetFormatName( OUString() );
408 if( m_rDoc
.FindFlyByName( sOld
, nNdTyp
) ) // found one
411 case SwNodeType::Grf
: sOld
= m_rDoc
.GetUniqueGrfName(sOld
); break;
412 case SwNodeType::Ole
: sOld
= m_rDoc
.GetUniqueOLEName(); break;
413 default: sOld
= m_rDoc
.GetUniqueFrameName(); break;
416 pDest
->SetFormatName( sOld
);
420 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
422 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoInsLayFormat
>(pDest
,SwNodeOffset(0),0));
425 // Make sure that FlyFrames in FlyFrames are copied
426 aIdx
= *pSttNd
->EndOfSectionNode();
428 //fdo#36631 disable (scoped) any undo operations associated with the
429 //contact object itself. They should be managed by SwUndoInsLayFormat.
430 const ::sw::DrawUndoGuard
drawUndoGuard(m_rDoc
.GetIDocumentUndoRedo());
432 pSrcDoc
->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg
, aIdx
.GetNode(), nullptr, false, true, true);
436 OSL_ENSURE( RES_DRAWFRMFMT
== rSource
.Which(), "Neither Fly nor Draw." );
437 // #i52780# - Note: moving object to visible layer not needed.
438 rSource
.CallSwClientNotify(sw::DrawFormatLayoutCopyHint(static_cast<SwDrawFrameFormat
&>(*pDest
), m_rDoc
));
440 if(pDest
->GetAnchor() == rNewAnchor
)
442 // Do *not* connect to layout, if a <MakeFrames> will not be called.
444 pDest
->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES
));
448 pDest
->SetFormatAttr( rNewAnchor
);
450 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
452 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoInsLayFormat
>(pDest
,SwNodeOffset(0),0));
456 if (bSetTextFlyAtt
&& (RndStdIds::FLY_AS_CHAR
== rNewAnchor
.GetAnchorId()))
458 SwNode
* pAnchorNode
= rNewAnchor
.GetAnchorNode();
459 SwFormatFlyCnt
aFormat( pDest
);
460 assert(pAnchorNode
->GetTextNode() && "sw.core: text node expected");
461 if (SwTextNode
*pTextNd
= pAnchorNode
->GetTextNode())
462 pTextNd
->InsertItem( aFormat
, rNewAnchor
.GetAnchorContentOffset(), 0 );
468 if (pDest
->GetName().isEmpty())
470 // Format name should have unique name. Let's use object name as a fallback
471 SdrObject
*pObj
= pDest
->FindSdrObject();
473 pDest
->SetFormatName(pObj
->GetName());
476 // If the draw format has a TextBox, then copy its fly format as well.
477 if (const auto& pTextBoxes
= rSource
.GetOtherTextBoxFormats())
478 pTextBoxes
->Clone(&m_rDoc
, rNewAnchor
, pDest
, bSetTextFlyAtt
, bMakeFrames
);
483 //Load document from fdo#42534 under valgrind, drag the scrollbar down so full
484 //document layout is triggered. Close document before layout has completed, and
485 //SwAnchoredObject objects deleted by the deletion of layout remain referenced
487 void DocumentLayoutManager::ClearSwLayouterEntries()
489 SwLayouter::ClearMovedFwdFrames( m_rDoc
);
490 SwLayouter::ClearObjsTmpConsiderWrapInfluence( m_rDoc
);
492 SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc
);
495 DocumentLayoutManager::~DocumentLayoutManager()
500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */