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