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 
20 #include <memory>
21 #include <layouter.hxx>
22 #include <doc.hxx>
23 #include <sectfrm.hxx>
24 #include <pagefrm.hxx>
25 #include <ftnfrm.hxx>
26 #include <txtfrm.hxx>
27 #include <IDocumentLayoutAccess.hxx>
28 
29 #include <movedfwdfrmsbyobjpos.hxx>
30 #include "objstmpconsiderwrapinfl.hxx"
31 
32 #define LOOP_DETECT 250
33 
34 class SwLooping
35 {
36     sal_uInt16 nMinPage;
37     sal_uInt16 nMaxPage;
38     sal_uInt16 nCount;
39     sal_uInt16 mnLoopControlStage;
40 public:
41     explicit SwLooping( SwPageFrame const * pPage );
42     void Control( SwPageFrame* pPage );
43     void Drastic( SwFrame* pFrame );
IsLoopingLouieLight() const44     bool IsLoopingLouieLight() const { return nCount > LOOP_DETECT - 30; };
45 };
46 
47 class SwEndnoter
48 {
49     SwLayouter* const                  pMaster;
50     SwSectionFrame*                    pSect;
51     std::unique_ptr<SwFootnoteFrames>  pEndArr;
52 public:
SwEndnoter(SwLayouter * pLay)53     explicit SwEndnoter( SwLayouter* pLay )
54         : pMaster( pLay ), pSect( nullptr ) {}
55     void CollectEndnotes( SwSectionFrame* pSct );
56     void CollectEndnote( SwFootnoteFrame* pFootnote );
GetSect() const57     const SwSectionFrame* GetSect() const { return pSect; }
58     void InsertEndnotes();
HasEndnotes() const59     bool HasEndnotes() const { return pEndArr && !pEndArr->empty(); }
60 };
61 
CollectEndnotes(SwSectionFrame * pSct)62 void SwEndnoter::CollectEndnotes( SwSectionFrame* pSct )
63 {
64     OSL_ENSURE( pSct, "CollectEndnotes: Which section?" );
65     if( !pSect )
66         pSect = pSct;
67     else if( pSct != pSect )
68         return;
69     pSect->CollectEndnotes( pMaster );
70 }
71 
CollectEndnote(SwFootnoteFrame * pFootnote)72 void SwEndnoter::CollectEndnote( SwFootnoteFrame* pFootnote )
73 {
74     if( pEndArr && pEndArr->end() != std::find( pEndArr->begin(), pEndArr->end(), pFootnote ) )
75         return;
76 
77     if( pFootnote->GetUpper() )
78     {
79         // pFootnote is the master, he incorporates its follows
80         SwFootnoteFrame *pNxt = pFootnote->GetFollow();
81         while ( pNxt )
82         {
83             SwFrame *pCnt = pNxt->ContainsAny();
84             if ( pCnt )
85             {
86                 do
87                 {   SwFrame *pNxtCnt = pCnt->GetNext();
88                     pCnt->Cut();
89                     pCnt->Paste( pFootnote );
90                     pCnt = pNxtCnt;
91                 } while ( pCnt );
92             }
93             else
94             {
95                 OSL_ENSURE( pNxt->Lower() && pNxt->Lower()->IsSctFrame(),
96                         "Endnote without content?" );
97                 pNxt->Cut();
98                 SwFrame::DestroyFrame(pNxt);
99             }
100             pNxt = pFootnote->GetFollow();
101         }
102         if( pFootnote->GetMaster() )
103             return;
104         pFootnote->Cut();
105     }
106     else if( pEndArr )
107     {
108         for (SwFootnoteFrame* pEndFootnote : *pEndArr)
109         {
110             if( pEndFootnote->GetAttr() == pFootnote->GetAttr() )
111             {
112                 SwFrame::DestroyFrame(pFootnote);
113                 return;
114             }
115         }
116     }
117     if( !pEndArr )
118         pEndArr.reset( new SwFootnoteFrames );  // deleted from the SwLayouter
119     pEndArr->push_back( pFootnote );
120 }
121 
InsertEndnotes()122 void SwEndnoter::InsertEndnotes()
123 {
124     if( !pSect )
125         return;
126     if( !pEndArr || pEndArr->empty() )
127     {
128         pSect = nullptr;
129         return;
130     }
131     OSL_ENSURE( pSect->Lower() && pSect->Lower()->IsFootnoteBossFrame(),
132             "InsertEndnotes: Where's my column?" );
133     SwFrame* pRef = pSect->FindLastContent( SwFindMode::MyLast );
134     SwFootnoteBossFrame *pBoss = pRef ? pRef->FindFootnoteBossFrame()
135                                : static_cast<SwFootnoteBossFrame*>(pSect->Lower());
136     pBoss->MoveFootnotes_( *pEndArr );
137     pEndArr.reset();
138     pSect = nullptr;
139 }
140 
SwLooping(SwPageFrame const * pPage)141 SwLooping::SwLooping( SwPageFrame const * pPage )
142 {
143     OSL_ENSURE( pPage, "Where's my page?" );
144     nMinPage = pPage->GetPhyPageNum();
145     nMaxPage = nMinPage;
146     nCount = 0;
147     mnLoopControlStage = 0;
148 }
149 
Drastic(SwFrame * pFrame)150 void SwLooping::Drastic( SwFrame* pFrame )
151 {
152     while( pFrame )
153     {
154         pFrame->ValidateThisAndAllLowers( mnLoopControlStage );
155         pFrame = pFrame->GetNext();
156     }
157 }
158 
Control(SwPageFrame * pPage)159 void SwLooping::Control( SwPageFrame* pPage )
160 {
161     if( !pPage )
162         return;
163     const sal_uInt16 nNew = pPage->GetPhyPageNum();
164     if( nNew > nMaxPage )
165         nMaxPage = nNew;
166     if( nNew < nMinPage )
167     {
168         nMinPage = nNew;
169         nMaxPage = nNew;
170         nCount = 0;
171         mnLoopControlStage = 0;
172     }
173     else if( nNew > nMinPage + 2 )
174     {
175         nMinPage = nNew - 2;
176         nMaxPage = nNew;
177         nCount = 0;
178         mnLoopControlStage = 0;
179     }
180     else if( ++nCount > LOOP_DETECT )
181     {
182 #if OSL_DEBUG_LEVEL > 1
183         static bool bNoLouie = false;
184         if( bNoLouie )
185             return;
186 
187         // FME 2007-08-30 #i81146# new loop control
188         OSL_ENSURE( 0 != mnLoopControlStage, "Looping Louie: Stage 1!" );
189         OSL_ENSURE( 1 != mnLoopControlStage, "Looping Louie: Stage 2!!" );
190         OSL_ENSURE( 2 >  mnLoopControlStage, "Looping Louie: Stage 3!!!" );
191 #endif
192 
193         Drastic( pPage->Lower() );
194         if( nNew > nMinPage && pPage->GetPrev() )
195             Drastic( static_cast<SwPageFrame*>(pPage->GetPrev())->Lower() );
196         if( nNew < nMaxPage && pPage->GetNext() )
197             Drastic( static_cast<SwPageFrame*>(pPage->GetNext())->Lower() );
198 
199         ++mnLoopControlStage;
200         nCount = 0;
201     }
202 }
203 
SwLayouter()204 SwLayouter::SwLayouter()
205 {
206 }
207 
~SwLayouter()208 SwLayouter::~SwLayouter()
209 {
210 }
211 
CollectEndnotes_(SwSectionFrame * pSect)212 void SwLayouter::CollectEndnotes_( SwSectionFrame* pSect )
213 {
214     if( !mpEndnoter )
215         mpEndnoter.reset(new SwEndnoter( this ));
216     mpEndnoter->CollectEndnotes( pSect );
217 }
218 
HasEndnotes() const219 bool SwLayouter::HasEndnotes() const
220 {
221     return mpEndnoter->HasEndnotes();
222 }
223 
CollectEndnote(SwFootnoteFrame * pFootnote)224 void SwLayouter::CollectEndnote( SwFootnoteFrame* pFootnote )
225 {
226     mpEndnoter->CollectEndnote( pFootnote );
227 }
228 
InsertEndnotes(SwSectionFrame const * pSect)229 void SwLayouter::InsertEndnotes( SwSectionFrame const * pSect )
230 {
231     if( !mpEndnoter || mpEndnoter->GetSect() != pSect )
232         return;
233     mpEndnoter->InsertEndnotes();
234 }
235 
LoopControl(SwPageFrame * pPage)236 void SwLayouter::LoopControl( SwPageFrame* pPage )
237 {
238     OSL_ENSURE( mpLooping, "Looping: Lost control" );
239     mpLooping->Control( pPage );
240 }
241 
LoopingLouieLight(const SwDoc & rDoc,const SwTextFrame & rFrame)242 void SwLayouter::LoopingLouieLight( const SwDoc& rDoc, const SwTextFrame& rFrame )
243 {
244     if ( mpLooping && mpLooping->IsLoopingLouieLight() )
245     {
246 #if OSL_DEBUG_LEVEL > 1
247         OSL_FAIL( "Looping Louie (Light): Fixating fractious frame" );
248 #endif
249         SwLayouter::InsertMovedFwdFrame( rDoc, rFrame, rFrame.FindPageFrame()->GetPhyPageNum() );
250     }
251 }
252 
StartLooping(SwPageFrame const * pPage)253 bool SwLayouter::StartLooping( SwPageFrame const * pPage )
254 {
255     if( mpLooping )
256         return false;
257     mpLooping.reset(new SwLooping( pPage ));
258     return true;
259 }
260 
EndLoopControl()261 void SwLayouter::EndLoopControl()
262 {
263     mpLooping.reset();
264 }
265 
CollectEndnotes(SwDoc * pDoc,SwSectionFrame * pSect)266 void SwLayouter::CollectEndnotes( SwDoc* pDoc, SwSectionFrame* pSect )
267 {
268     assert(pDoc && "No doc, no fun");
269     if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
270         pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
271     pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnotes_( pSect );
272 }
273 
Collecting(SwDoc * pDoc,SwSectionFrame const * pSect,SwFootnoteFrame * pFootnote)274 bool SwLayouter::Collecting( SwDoc* pDoc, SwSectionFrame const * pSect, SwFootnoteFrame* pFootnote )
275 {
276     if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
277         return false;
278     SwLayouter *pLayouter = pDoc->getIDocumentLayoutAccess().GetLayouter();
279     if( pLayouter->mpEndnoter && pLayouter->mpEndnoter->GetSect() && pSect &&
280         ( pLayouter->mpEndnoter->GetSect()->IsAnFollow( pSect ) ||
281           pSect->IsAnFollow( pLayouter->mpEndnoter->GetSect() ) ) )
282     {
283         if( pFootnote )
284             pLayouter->CollectEndnote( pFootnote );
285         return true;
286     }
287     return false;
288 }
289 
StartLoopControl(SwDoc * pDoc,SwPageFrame const * pPage)290 bool SwLayouter::StartLoopControl( SwDoc* pDoc, SwPageFrame const *pPage )
291 {
292     OSL_ENSURE( pDoc, "No doc, no fun" );
293     if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
294         pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
295     return !pDoc->getIDocumentLayoutAccess().GetLayouter()->mpLooping &&
296             pDoc->getIDocumentLayoutAccess().GetLayouter()->StartLooping( pPage );
297 }
298 
299 // #i28701#
300 // methods to manage text frames, which are moved forward by the positioning
301 // of its anchored objects
ClearMovedFwdFrames(const SwDoc & _rDoc)302 void SwLayouter::ClearMovedFwdFrames( const SwDoc& _rDoc )
303 {
304     if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
305          _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
306     {
307         _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Clear();
308     }
309 }
310 
InsertMovedFwdFrame(const SwDoc & _rDoc,const SwTextFrame & _rMovedFwdFrameByObjPos,const sal_uInt32 _nToPageNum)311 void SwLayouter::InsertMovedFwdFrame( const SwDoc& _rDoc,
312                                     const SwTextFrame& _rMovedFwdFrameByObjPos,
313                                     const sal_uInt32 _nToPageNum )
314 {
315     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
316     {
317         const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
318     }
319 
320     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
321     {
322         const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames.reset(
323                                                 new SwMovedFwdFramesByObjPos());
324     }
325 
326     _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Insert( _rMovedFwdFrameByObjPos,
327                                                  _nToPageNum );
328 }
329 
330 // #i40155#
RemoveMovedFwdFrame(const SwDoc & _rDoc,const SwTextFrame & _rTextFrame)331 void SwLayouter::RemoveMovedFwdFrame( const SwDoc& _rDoc,
332                                     const SwTextFrame& _rTextFrame )
333 {
334     sal_uInt32 nDummy;
335     if ( SwLayouter::FrameMovedFwdByObjPos( _rDoc, _rTextFrame, nDummy ) )
336     {
337         _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Remove( _rTextFrame );
338     }
339 }
340 
FrameMovedFwdByObjPos(const SwDoc & _rDoc,const SwTextFrame & _rTextFrame,sal_uInt32 & _ornToPageNum)341 bool SwLayouter::FrameMovedFwdByObjPos( const SwDoc& _rDoc,
342                                       const SwTextFrame& _rTextFrame,
343                                       sal_uInt32& _ornToPageNum )
344 {
345     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
346     {
347         _ornToPageNum = 0;
348         return false;
349     }
350     else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
351     {
352         _ornToPageNum = 0;
353         return false;
354     }
355     else
356     {
357         return _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->
358                                 FrameMovedFwdByObjPos( _rTextFrame, _ornToPageNum );
359     }
360 }
361 
362 // #i26945#
DoesRowContainMovedFwdFrame(const SwDoc & _rDoc,const SwRowFrame & _rRowFrame)363 bool SwLayouter::DoesRowContainMovedFwdFrame( const SwDoc& _rDoc,
364                                             const SwRowFrame& _rRowFrame )
365 {
366     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
367     {
368         return false;
369     }
370     else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
371     {
372         return false;
373     }
374     else
375     {
376         return _rDoc.getIDocumentLayoutAccess().GetLayouter()->
377                         mpMovedFwdFrames->DoesRowContainMovedFwdFrame( _rRowFrame );
378     }
379 }
380 
381 // #i35911#
ClearObjsTmpConsiderWrapInfluence(const SwDoc & _rDoc)382 void SwLayouter::ClearObjsTmpConsiderWrapInfluence( const SwDoc& _rDoc )
383 {
384     if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
385          _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
386     {
387         _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Clear();
388     }
389 }
390 
InsertObjForTmpConsiderWrapInfluence(const SwDoc & _rDoc,SwAnchoredObject & _rAnchoredObj)391 void SwLayouter::InsertObjForTmpConsiderWrapInfluence(
392                                             const SwDoc& _rDoc,
393                                             SwAnchoredObject& _rAnchoredObj )
394 {
395     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
396     {
397         const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
398     }
399 
400     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
401     {
402         const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl.reset(
403                                 new SwObjsMarkedAsTmpConsiderWrapInfluence());
404     }
405 
406     _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Insert( _rAnchoredObj );
407 }
408 
RemoveObjForTmpConsiderWrapInfluence(const SwDoc & _rDoc,SwAnchoredObject & _rAnchoredObj)409 void SwLayouter::RemoveObjForTmpConsiderWrapInfluence(
410                                             const SwDoc& _rDoc,
411                                             SwAnchoredObject& _rAnchoredObj )
412 {
413     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
414         return;
415 
416     if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
417         return;
418 
419     _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Remove( _rAnchoredObj );
420 }
421 
422 
LOOPING_LOUIE_LIGHT(bool bCondition,const SwTextFrame & rTextFrame)423 void LOOPING_LOUIE_LIGHT( bool bCondition, const SwTextFrame& rTextFrame )
424 {
425     if ( bCondition )
426     {
427         const SwDoc& rDoc = *rTextFrame.GetAttrSet()->GetDoc();
428         if ( rDoc.getIDocumentLayoutAccess().GetLayouter() )
429         {
430             const_cast<SwDoc&>(rDoc).getIDocumentLayoutAccess().GetLayouter()->LoopingLouieLight( rDoc, rTextFrame );
431         }
432     }
433 }
434 
435 // #i65250#
MoveBwdSuppressed(const SwDoc & p_rDoc,const SwFlowFrame & p_rFlowFrame,const SwLayoutFrame & p_rNewUpperFrame)436 bool SwLayouter::MoveBwdSuppressed( const SwDoc& p_rDoc,
437                                     const SwFlowFrame& p_rFlowFrame,
438                                     const SwLayoutFrame& p_rNewUpperFrame )
439 {
440     bool bMoveBwdSuppressed( false );
441 
442     if ( !p_rDoc.getIDocumentLayoutAccess().GetLayouter() )
443     {
444         const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
445     }
446 
447     // create hash map key
448     tMoveBwdLayoutInfoKey aMoveBwdLayoutInfo;
449     aMoveBwdLayoutInfo.mnFrameId = p_rFlowFrame.GetFrame().GetFrameId();
450     aMoveBwdLayoutInfo.mnNewUpperPosX = p_rNewUpperFrame.getFrameArea().Pos().X();
451     aMoveBwdLayoutInfo.mnNewUpperPosY = p_rNewUpperFrame.getFrameArea().Pos().Y();
452     aMoveBwdLayoutInfo.mnNewUpperWidth = p_rNewUpperFrame.getFrameArea().Width();
453     aMoveBwdLayoutInfo.mnNewUpperHeight =  p_rNewUpperFrame.getFrameArea().Height();
454     SwRectFnSet aRectFnSet(&p_rNewUpperFrame);
455     const SwFrame* pLastLower( p_rNewUpperFrame.Lower() );
456     while ( pLastLower && pLastLower->GetNext() )
457     {
458         pLastLower = pLastLower->GetNext();
459     }
460     aMoveBwdLayoutInfo.mnFreeSpaceInNewUpper =
461             pLastLower
462             ? aRectFnSet.BottomDist( pLastLower->getFrameArea(), aRectFnSet.GetPrtBottom(p_rNewUpperFrame) )
463             : aRectFnSet.GetHeight(p_rNewUpperFrame.getFrameArea());
464 
465     // check for moving backward suppress threshold
466     const sal_uInt16 cMoveBwdCountSuppressThreshold = 20;
467     if ( ++const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo[ aMoveBwdLayoutInfo ] >
468                                                 cMoveBwdCountSuppressThreshold )
469     {
470         bMoveBwdSuppressed = true;
471     }
472 
473     return bMoveBwdSuppressed;
474 }
475 
ClearMoveBwdLayoutInfo(const SwDoc & _rDoc)476 void SwLayouter::ClearMoveBwdLayoutInfo( const SwDoc& _rDoc )
477 {
478     if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() )
479         const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo.clear();
480 }
481 
482 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
483