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