1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19 #include <DocumentLayoutManager.hxx>
20 #include <doc.hxx>
21 #include <IDocumentState.hxx>
22 #include <IDocumentUndoRedo.hxx>
23 #include <DocumentContentOperationsManager.hxx>
24 #include <IDocumentStylePoolAccess.hxx>
25 #include <undobj.hxx>
26 #include <viewsh.hxx>
27 #include <layouter.hxx>
28 #include <poolfmt.hxx>
29 #include <frmfmt.hxx>
30 #include <fmtcntnt.hxx>
31 #include <fmtcnct.hxx>
32 #include <ndole.hxx>
33 #include <fmtanchr.hxx>
34 #include <txtflcnt.hxx>
35 #include <fmtflcnt.hxx>
36 #include <ndtxt.hxx>
37 #include <unoframe.hxx>
38 #include <textboxhelper.hxx>
39 #include <ndindex.hxx>
40 #include <pam.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>
47
48 using namespace ::com::sun::star;
49
50 namespace sw
51 {
52
DocumentLayoutManager(SwDoc & i_rSwdoc)53 DocumentLayoutManager::DocumentLayoutManager( SwDoc& i_rSwdoc ) :
54 m_rDoc( i_rSwdoc ),
55 mpCurrentView( nullptr )
56 {
57 }
58
GetCurrentViewShell() const59 const SwViewShell *DocumentLayoutManager::GetCurrentViewShell() const
60 {
61 return mpCurrentView;
62 }
63
GetCurrentViewShell()64 SwViewShell *DocumentLayoutManager::GetCurrentViewShell()
65 {
66 return mpCurrentView;
67 }
68
SetCurrentViewShell(SwViewShell * pNew)69 void DocumentLayoutManager::SetCurrentViewShell( SwViewShell* pNew )
70 {
71 mpCurrentView = pNew;
72 }
73
74 // It must be able to communicate to a SwViewShell. This is going to be removed later.
GetCurrentLayout() const75 const SwRootFrame *DocumentLayoutManager::GetCurrentLayout() const
76 {
77 if(GetCurrentViewShell())
78 return GetCurrentViewShell()->GetLayout();
79 return nullptr;
80 }
81
GetCurrentLayout()82 SwRootFrame *DocumentLayoutManager::GetCurrentLayout()
83 {
84 if(GetCurrentViewShell())
85 return GetCurrentViewShell()->GetLayout();
86 return nullptr;
87 }
88
HasLayout() const89 bool DocumentLayoutManager::HasLayout() const
90 {
91 // if there is a view, there is always a layout
92 return (mpCurrentView != nullptr);
93 }
94
GetLayouter()95 SwLayouter* DocumentLayoutManager::GetLayouter()
96 {
97 return mpLayouter.get();
98 }
99
GetLayouter() const100 const SwLayouter* DocumentLayoutManager::GetLayouter() const
101 {
102 return mpLayouter.get();
103 }
104
SetLayouter(SwLayouter * pNew)105 void DocumentLayoutManager::SetLayouter( SwLayouter* pNew )
106 {
107 mpLayouter.reset( pNew );
108 }
109
110 /** Create a new format whose settings fit to the Request by default.
111
112 The format is put into the respective format array.
113 If there already is a fitting format, it is returned instead. */
MakeLayoutFormat(RndStdIds eRequest,const SfxItemSet * pSet)114 SwFrameFormat *DocumentLayoutManager::MakeLayoutFormat( RndStdIds eRequest, const SfxItemSet* pSet )
115 {
116 SwFrameFormat *pFormat = nullptr;
117 const bool bMod = m_rDoc.getIDocumentState().IsModified();
118 bool bHeader = false;
119
120 switch ( eRequest )
121 {
122 case RndStdIds::HEADER:
123 case RndStdIds::HEADERL:
124 case RndStdIds::HEADERR:
125 {
126 bHeader = true;
127 [[fallthrough]];
128 }
129 case RndStdIds::FOOTER:
130 {
131 pFormat = new SwFrameFormat( m_rDoc.GetAttrPool(),
132 (bHeader ? u"Right header"_ustr : u"Right footer"_ustr),
133 m_rDoc.GetDfltFrameFormat() );
134
135 const SwNode& rEndOfAutotext( m_rDoc.GetNodes().GetEndOfAutotext() );
136 SwStartNode* pSttNd =
137 m_rDoc.GetNodes().MakeTextSection
138 ( rEndOfAutotext,
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
147 ) ) );
148 pFormat->SetFormatAttr( SwFormatContent( pSttNd ));
149
150 if( pSet ) // Set a few more attributes
151 pFormat->SetFormatAttr( *pSet );
152
153 // Why set it back? Doc has changed, or not?
154 // In any case, wrong for the FlyFrames!
155 if ( !bMod )
156 m_rDoc.getIDocumentState().ResetModified();
157 }
158 break;
159
160 case RndStdIds::DRAW_OBJECT:
161 {
162 pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
163 if( pSet ) // Set a few more attributes
164 pFormat->SetFormatAttr( *pSet );
165
166 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
167 {
168 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
169 std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0));
170 }
171 }
172 break;
173
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!" );
181 break;
182 #endif
183
184 default:
185 OSL_ENSURE( false,
186 "LayoutFormat was requested with an invalid Request." );
187
188 }
189 return pFormat;
190 }
191
192 /// Deletes the denoted format and its content.
DelLayoutFormat(SwFrameFormat * pFormat)193 void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat *pFormat )
194 {
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() )
202 {
203 SwFormatChain aChain( rChain.GetPrev()->GetChain() );
204 aChain.SetNext( rChain.GetNext() );
205 m_rDoc.SetAttr( aChain, *rChain.GetPrev() );
206 }
207 if ( rChain.GetNext() )
208 {
209 SwFormatChain aChain( rChain.GetNext()->GetChain() );
210 aChain.SetPrev( rChain.GetPrev() );
211 m_rDoc.SetAttr( aChain, *rChain.GetNext() );
212 }
213
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())
219 {
220 // Disconnect if it's an OLE object
221 SwOLENode* pOLENd = m_rDoc.GetNodes()[ pCntIdx->GetIndex()+1 ]->GetOLENode();
222 if( pOLENd && pOLENd->GetOLEObj().IsOleRef() )
223 {
224 try
225 {
226 pOLENd->GetOLEObj().GetOleRef()->changeState( embed::EmbedStates::LOADED );
227 }
228 catch ( uno::Exception& )
229 {
230 }
231 }
232 }
233
234 // Destroy Frames
235 pFormat->DelFrames();
236
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))
241 {
242 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelLayFormat>( pFormat ));
243 }
244 else
245 {
246 // #i32089# - delete at-frame anchored objects
247 if ( nWh == RES_FLYFRMFMT )
248 {
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();
253 if (pContentIdx)
254 {
255 sw::SpzFrameFormats* pSpzs = pFormat->GetDoc()->GetSpzFrameFormats();
256 if ( pSpzs )
257 {
258 std::vector<SwFrameFormat*> aToDeleteFrameFormats;
259 const SwNodeOffset nNodeIdxOfFlyFormat( pContentIdx->GetIndex() );
260
261 for(sw::SpzFrameFormat* pSpz: *pSpzs)
262 {
263 const SwFormatAnchor &rAnch = pSpz->GetAnchor();
264 if ( rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY &&
265 rAnch.GetAnchorNode()->GetIndex() == nNodeIdxOfFlyFormat )
266 {
267 aToDeleteFrameFormats.push_back(pSpz);
268 }
269 }
270
271 // delete found frame formats
272 while ( !aToDeleteFrameFormats.empty() )
273 {
274 SwFrameFormat* pTmpFormat = aToDeleteFrameFormats.back();
275 pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pTmpFormat );
276
277 aToDeleteFrameFormats.pop_back();
278 }
279 }
280 }
281 }
282
283 // Delete content
284 if( pCntIdx )
285 {
286 SwNode *pNode = &pCntIdx->GetNode();
287 const_cast<SwFormatContent&>(pFormat->GetFormatAttr( RES_CNTNT )).SetNewContentIdx( nullptr );
288 m_rDoc.getIDocumentContentOperations().DeleteSection( pNode );
289 }
290
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())
294 {
295 SwTextNode *pTextNd = rAnchor.GetAnchorNode()->GetTextNode();
296
297 // attribute is still in text node, delete it
298 if ( pTextNd )
299 {
300 SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>(
301 pTextNd->GetTextAttrForCharAt( rAnchor.GetAnchorContentOffset(),
302 RES_TXTATR_FLYCNT ));
303 if ( pAttr && (pAttr->GetFlyCnt().GetFrameFormat() == pFormat) )
304 {
305 // don't delete, set pointer to 0
306 const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
307 pTextNd->EraseText( *rAnchor.GetContentAnchor(), 1 );
308 }
309 }
310 }
311
312 m_rDoc.DelFrameFormat( pFormat );
313 }
314 m_rDoc.getIDocumentState().SetModified();
315 }
316
317 /** Copies the stated format (pSrc) to pDest and returns pDest.
318
319 If there's no pDest, it is created.
320 If the source format is located in another document, also copy correctly
321 in this case.
322 The Anchor attribute's position is always set to 0! */
CopyLayoutFormat(const SwFrameFormat & rSource,const SwFormatAnchor & rNewAnchor,bool bSetTextFlyAtt,bool bMakeFrames)323 SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
324 const SwFrameFormat& rSource,
325 const SwFormatAnchor& rNewAnchor,
326 bool bSetTextFlyAtt,
327 bool bMakeFrames )
328 {
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" );
332
333 SwDoc* pSrcDoc = const_cast<SwDoc*>(rSource.GetDoc());
334
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);
342 if(bDraw)
343 {
344 bool bCheckControlLayer = false;
345 rSource.CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer));
346 bMayNotCopy =
347 bCheckControlLayer &&
348 ((RndStdIds::FLY_AT_PARA == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_FLY == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_CHAR == rNewAnchor.GetAnchorId())) &&
349 bInHeaderFooter;
350 }
351
352 // just return if we can't copy this
353 if( bMayNotCopy )
354 return nullptr;
355
356 SwFrameFormat* pDest = m_rDoc.GetDfltFrameFormat();
357 if( rSource.GetRegisteredIn() != pSrcDoc->GetDfltFrameFormat() )
358 pDest = m_rDoc.CopyFrameFormat( *static_cast<const SwFrameFormat*>(rSource.GetRegisteredIn()) );
359 if( bFly )
360 {
361 // #i11176#
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 );
368 pDest = pFormat;
369
370 SwXFrame::GetOrCreateSdrObject(*pFormat);
371 }
372 else
373 pDest = m_rDoc.MakeDrawFrameFormat( OUString(), pDest );
374
375 // Copy all other or new attributes
376 pDest->CopyAttrs( rSource );
377
378 // Do not copy chains
379 pDest->ResetFormatAttr( RES_CHAIN );
380
381 if( bFly )
382 {
383 // Duplicate the content.
384 const SwNode& rCSttNd = rSource.GetContent().GetContentIdx()->GetNode();
385 SwNodeRange aRg( rCSttNd, SwNodeOffset(1), *rCSttNd.EndOfSectionNode() );
386
387 SwStartNode* pSttNd = SwNodes::MakeEmptySection( m_rDoc.GetNodes().GetEndOfAutotext(), SwFlyStartNode );
388
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 );
396
397 if( !m_rDoc.IsCopyIsMove() || &m_rDoc != pSrcDoc )
398 {
399 if( (m_rDoc.IsInReading() && !bInHeaderFooter) || m_rDoc.IsInMailMerge() )
400 pDest->SetFormatName( OUString() );
401 else
402 {
403 // Test first if the name is already taken, if so generate a new one.
404 SwNodeType nNdTyp = aRg.aStart.GetNode().GetNodeType();
405
406 OUString sOld( pDest->GetName() );
407 pDest->SetFormatName( OUString() );
408 if( m_rDoc.FindFlyByName( sOld, nNdTyp ) ) // found one
409 switch( nNdTyp )
410 {
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;
414 }
415
416 pDest->SetFormatName( sOld );
417 }
418 }
419
420 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
421 {
422 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
423 }
424
425 // Make sure that FlyFrames in FlyFrames are copied
426 aIdx = *pSttNd->EndOfSectionNode();
427
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());
431
432 pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aIdx.GetNode(), nullptr, false, true, true);
433 }
434 else
435 {
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));
439
440 if(pDest->GetAnchor() == rNewAnchor)
441 {
442 // Do *not* connect to layout, if a <MakeFrames> will not be called.
443 if(bMakeFrames)
444 pDest->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES));
445
446 }
447 else
448 pDest->SetFormatAttr( rNewAnchor );
449
450 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
451 {
452 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
453 }
454 }
455
456 if (bSetTextFlyAtt && (RndStdIds::FLY_AS_CHAR == rNewAnchor.GetAnchorId()))
457 {
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 );
463 }
464
465 if( bMakeFrames )
466 pDest->MakeFrames();
467
468 if (pDest->GetName().isEmpty())
469 {
470 // Format name should have unique name. Let's use object name as a fallback
471 SdrObject *pObj = pDest->FindSdrObject();
472 if (pObj)
473 pDest->SetFormatName(pObj->GetName());
474 }
475
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);
479
480 return pDest;
481 }
482
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
486 //by the SwLayouter
ClearSwLayouterEntries()487 void DocumentLayoutManager::ClearSwLayouterEntries()
488 {
489 SwLayouter::ClearMovedFwdFrames( m_rDoc );
490 SwLayouter::ClearObjsTmpConsiderWrapInfluence( m_rDoc );
491 // #i65250#
492 SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc );
493 }
494
~DocumentLayoutManager()495 DocumentLayoutManager::~DocumentLayoutManager()
496 {
497 }
498
499 }
500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
501
502