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 "objectformattertxtfrm.hxx"
21 #include "objectformatterlayfrm.hxx"
22 #include <anchoreddrawobject.hxx>
23 #include <sortedobjs.hxx>
24 #include <rootfrm.hxx>
25 #include <pagefrm.hxx>
26 #include <flyfrms.hxx>
27 #include <txtfrm.hxx>
28 #include <layact.hxx>
29 #include <IDocumentSettingAccess.hxx>
30 #include <osl/diagnose.h>
31 
32 #include <vector>
33 
34 // --> #i26945# - Additionally the type of the anchor text frame
35 // is collected - by type is meant 'master' or 'follow'.
36 class SwPageNumAndTypeOfAnchors
37 {
38     private:
39         struct tEntry
40         {
41             SwAnchoredObject* mpAnchoredObj;
42             sal_uInt32 mnPageNumOfAnchor;
43             bool mbAnchoredAtMaster;
44         };
45 
46         std::vector< tEntry > maObjList;
47 
48     public:
SwPageNumAndTypeOfAnchors()49         SwPageNumAndTypeOfAnchors()
50         {
51         }
52 
Collect(SwAnchoredObject & _rAnchoredObj)53         void Collect( SwAnchoredObject& _rAnchoredObj )
54         {
55             tEntry aNewEntry;
56             aNewEntry.mpAnchoredObj = &_rAnchoredObj;
57             // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
58             // is replaced by method <FindPageFrameOfAnchor()>. It's return value
59             // have to be checked.
60             SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor();
61             if ( pPageFrameOfAnchor )
62             {
63                 aNewEntry.mnPageNumOfAnchor = pPageFrameOfAnchor->GetPhyPageNum();
64             }
65             else
66             {
67                 aNewEntry.mnPageNumOfAnchor = 0;
68             }
69             // --> #i26945# - collect type of anchor
70             SwTextFrame* pAnchorCharFrame = _rAnchoredObj.FindAnchorCharFrame();
71             if ( pAnchorCharFrame )
72             {
73                 aNewEntry.mbAnchoredAtMaster = !pAnchorCharFrame->IsFollow();
74             }
75             else
76             {
77                 aNewEntry.mbAnchoredAtMaster = true;
78             }
79             maObjList.push_back( aNewEntry );
80         }
81 
operator [](sal_uInt32 _nIndex)82         SwAnchoredObject* operator[]( sal_uInt32 _nIndex )
83         {
84             return maObjList[_nIndex].mpAnchoredObj;
85         }
86 
GetPageNum(sal_uInt32 _nIndex) const87         sal_uInt32 GetPageNum( sal_uInt32 _nIndex ) const
88         {
89             return maObjList[_nIndex].mnPageNumOfAnchor;
90         }
91 
92         // --> #i26945#
AnchoredAtMaster(sal_uInt32 _nIndex)93         bool AnchoredAtMaster( sal_uInt32 _nIndex )
94         {
95             return maObjList[_nIndex].mbAnchoredAtMaster;
96         }
97 
Count() const98         sal_uInt32 Count() const
99         {
100             return maObjList.size();
101         }
102 };
103 
SwObjectFormatter(const SwPageFrame & _rPageFrame,SwLayAction * _pLayAction,const bool _bCollectPgNumOfAnchors)104 SwObjectFormatter::SwObjectFormatter( const SwPageFrame& _rPageFrame,
105                                       SwLayAction* _pLayAction,
106                                       const bool _bCollectPgNumOfAnchors )
107     : mrPageFrame( _rPageFrame ),
108       mbConsiderWrapOnObjPos( _rPageFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ),
109       mpLayAction( _pLayAction ),
110       // --> #i26945#
111       mpPgNumAndTypeOfAnchors( _bCollectPgNumOfAnchors ? new SwPageNumAndTypeOfAnchors() : nullptr )
112 {
113 }
114 
~SwObjectFormatter()115 SwObjectFormatter::~SwObjectFormatter()
116 {
117 }
118 
CreateObjFormatter(SwFrame & _rAnchorFrame,const SwPageFrame & _rPageFrame,SwLayAction * _pLayAction)119 std::unique_ptr<SwObjectFormatter> SwObjectFormatter::CreateObjFormatter(
120                                                       SwFrame& _rAnchorFrame,
121                                                       const SwPageFrame& _rPageFrame,
122                                                       SwLayAction* _pLayAction )
123 {
124     std::unique_ptr<SwObjectFormatter> pObjFormatter;
125     if ( _rAnchorFrame.IsTextFrame() )
126     {
127         pObjFormatter = SwObjectFormatterTextFrame::CreateObjFormatter(
128                                             static_cast<SwTextFrame&>(_rAnchorFrame),
129                                             _rPageFrame, _pLayAction );
130     }
131     else if ( _rAnchorFrame.IsLayoutFrame() )
132     {
133         pObjFormatter = SwObjectFormatterLayFrame::CreateObjFormatter(
134                                         static_cast<SwLayoutFrame&>(_rAnchorFrame),
135                                         _rPageFrame, _pLayAction );
136     }
137     else
138     {
139         OSL_FAIL( "<SwObjectFormatter::CreateObjFormatter(..)> - unexpected type of anchor frame" );
140     }
141 
142     return pObjFormatter;
143 }
144 
145 /** method to format all floating screen objects at the given anchor frame
146 */
FormatObjsAtFrame(SwFrame & _rAnchorFrame,const SwPageFrame & _rPageFrame,SwLayAction * _pLayAction)147 bool SwObjectFormatter::FormatObjsAtFrame( SwFrame& _rAnchorFrame,
148                                          const SwPageFrame& _rPageFrame,
149                                          SwLayAction* _pLayAction )
150 {
151     bool bSuccess( true );
152 
153     // create corresponding object formatter
154     std::unique_ptr<SwObjectFormatter> pObjFormatter =
155         SwObjectFormatter::CreateObjFormatter( _rAnchorFrame, _rPageFrame, _pLayAction );
156 
157     if ( pObjFormatter )
158     {
159         // format anchored floating screen objects
160         bSuccess = pObjFormatter->DoFormatObjs();
161     }
162 
163     return bSuccess;
164 }
165 
166 /** method to format a given floating screen object
167 */
FormatObj(SwAnchoredObject & _rAnchoredObj,SwFrame * _pAnchorFrame,const SwPageFrame * _pPageFrame)168 bool SwObjectFormatter::FormatObj( SwAnchoredObject& _rAnchoredObj,
169                                    SwFrame* _pAnchorFrame,
170                                    const SwPageFrame* _pPageFrame )
171 {
172     bool bSuccess( true );
173 
174     OSL_ENSURE( _pAnchorFrame || _rAnchoredObj.GetAnchorFrame(),
175             "<SwObjectFormatter::FormatObj(..)> - missing anchor frame" );
176     SwFrame& rAnchorFrame = _pAnchorFrame ? *_pAnchorFrame : *(_rAnchoredObj.AnchorFrame());
177 
178     OSL_ENSURE( _pPageFrame || rAnchorFrame.FindPageFrame(),
179             "<SwObjectFormatter::FormatObj(..)> - missing page frame" );
180     const SwPageFrame& rPageFrame = _pPageFrame ? *_pPageFrame : *(rAnchorFrame.FindPageFrame());
181 
182     // create corresponding object formatter
183     std::unique_ptr<SwObjectFormatter> pObjFormatter =
184         SwObjectFormatter::CreateObjFormatter( rAnchorFrame, rPageFrame, nullptr/*_pLayAction*/ );
185 
186     if ( pObjFormatter )
187     {
188         // format given floating screen object
189         // --> #i40147# - check for moved forward anchor frame
190         bSuccess = pObjFormatter->DoFormatObj( _rAnchoredObj, true );
191     }
192 
193     return bSuccess;
194 }
195 
196 /** helper method for method <FormatObj_(..)> - performs the intrinsic format
197     of the layout of the given layout frame and all its lower layout frames.
198 
199     #i28701#
200     IMPORTANT NOTE:
201     Method corresponds to methods <SwLayAction::FormatLayoutFly(..)> and
202     <SwLayAction::FormatLayout(..)>. Thus, its code for the formatting have
203     to be synchronised.
204 */
FormatLayout_(SwLayoutFrame & _rLayoutFrame)205 void SwObjectFormatter::FormatLayout_( SwLayoutFrame& _rLayoutFrame )
206 {
207     _rLayoutFrame.Calc(_rLayoutFrame.getRootFrame()->GetCurrShell()->GetOut());
208 
209     SwFrame* pLowerFrame = _rLayoutFrame.Lower();
210     while ( pLowerFrame )
211     {
212         if ( pLowerFrame->IsLayoutFrame() )
213         {
214             FormatLayout_( *static_cast<SwLayoutFrame*>(pLowerFrame) );
215         }
216         pLowerFrame = pLowerFrame->GetNext();
217     }
218 }
219 
220 /** helper method for method <FormatObj_(..)> - performs the intrinsic
221     format of the content of the given floating screen object.
222 
223     #i28701#
224 */
FormatObjContent(SwAnchoredObject & _rAnchoredObj)225 void SwObjectFormatter::FormatObjContent( SwAnchoredObject& _rAnchoredObj )
226 {
227     if ( dynamic_cast<const SwFlyFrame*>( &_rAnchoredObj) ==  nullptr )
228     {
229         // only Writer fly frames have content
230         return;
231     }
232 
233     SwFlyFrame& rFlyFrame = static_cast<SwFlyFrame&>(_rAnchoredObj);
234     SwContentFrame* pContent = rFlyFrame.ContainsContent();
235 
236     while ( pContent )
237     {
238         // format content
239         pContent->OptCalc();
240 
241         // format floating screen objects at content text frame
242         // #i23129#, #i36347# - pass correct page frame to
243         // the object formatter
244         if ( pContent->IsTextFrame() &&
245              !SwObjectFormatter::FormatObjsAtFrame( *pContent,
246                                                   *(pContent->FindPageFrame()),
247                                                   GetLayAction() ) )
248         {
249             // restart format with first content
250             pContent = rFlyFrame.ContainsContent();
251             continue;
252         }
253 
254         // continue with next content
255         pContent = pContent->GetNextContentFrame();
256     }
257 }
258 
259 /** performs the intrinsic format of a given floating screen object and its content.
260 
261     #i28701#
262 */
FormatObj_(SwAnchoredObject & _rAnchoredObj)263 void SwObjectFormatter::FormatObj_( SwAnchoredObject& _rAnchoredObj )
264 {
265     // collect anchor object and its 'anchor' page number, if requested
266     if ( mpPgNumAndTypeOfAnchors )
267     {
268         mpPgNumAndTypeOfAnchors->Collect( _rAnchoredObj );
269     }
270 
271     if ( auto pFlyFrame = dynamic_cast<SwFlyFrame*>( &_rAnchoredObj) )
272     {
273         // --> #i34753# - reset flag, which prevents a positioning
274         if ( pFlyFrame->IsFlyLayFrame() )
275         {
276             static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( false );
277         }
278 
279         // #i81146# new loop control
280         int nLoopControlRuns = 0;
281         const int nLoopControlMax = 15;
282 
283         do {
284             if ( mpLayAction )
285             {
286                 mpLayAction->FormatLayoutFly( pFlyFrame );
287                 // --> consider, if the layout action
288                 // has to be restarted due to a delete of a page frame.
289                 if ( mpLayAction->IsAgain() )
290                 {
291                     break;
292                 }
293             }
294             else
295             {
296                 FormatLayout_( *pFlyFrame );
297             }
298             // --> #i34753# - prevent further positioning, if
299             // to-page|to-fly anchored Writer fly frame is already clipped.
300             if ( pFlyFrame->IsFlyLayFrame() && pFlyFrame->IsClipped() )
301             {
302                 static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( true );
303             }
304             // #i23129#, #i36347# - pass correct page frame
305             // to the object formatter
306             SwObjectFormatter::FormatObjsAtFrame( *pFlyFrame,
307                                                 *(pFlyFrame->FindPageFrame()),
308                                                 mpLayAction );
309             if ( mpLayAction )
310             {
311                 mpLayAction->FormatFlyContent( pFlyFrame );
312                 // --> consider, if the layout action
313                 // has to be restarted due to a delete of a page frame.
314                 if ( mpLayAction->IsAgain() )
315                 {
316                     break;
317                 }
318             }
319             else
320             {
321                 FormatObjContent( *pFlyFrame );
322             }
323 
324             if ( ++nLoopControlRuns >= nLoopControlMax )
325             {
326                 OSL_FAIL( "LoopControl in SwObjectFormatter::FormatObj_: Stage 3!!!" );
327                 pFlyFrame->ValidateThisAndAllLowers( 2 );
328                 nLoopControlRuns = 0;
329             }
330 
331         // --> #i57917#
332         // stop formatting of anchored object, if restart of layout process is requested.
333         } while ( !pFlyFrame->isFrameAreaDefinitionValid() &&
334                   !_rAnchoredObj.RestartLayoutProcess() &&
335                   pFlyFrame->GetAnchorFrame() == &GetAnchorFrame() );
336     }
337     else if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rAnchoredObj) !=  nullptr )
338     {
339         _rAnchoredObj.MakeObjPos();
340     }
341 }
342 
343 /** invokes the intrinsic format method for all floating screen objects,
344     anchored at anchor frame on the given page frame
345 
346     #i28701#
347     #i26945# - for format of floating screen objects for
348     follow text frames, the 'master' text frame is passed to the method.
349     Thus, the objects, whose anchor character is inside the follow text
350     frame can be formatted.
351 */
FormatObjsAtFrame_(SwTextFrame * _pMasterTextFrame)352 bool SwObjectFormatter::FormatObjsAtFrame_( SwTextFrame* _pMasterTextFrame )
353 {
354     // --> #i26945#
355     SwFrame* pAnchorFrame( nullptr );
356     if ( GetAnchorFrame().IsTextFrame() &&
357          static_cast<SwTextFrame&>(GetAnchorFrame()).IsFollow() &&
358          _pMasterTextFrame )
359     {
360         pAnchorFrame = _pMasterTextFrame;
361     }
362     else
363     {
364         pAnchorFrame = &GetAnchorFrame();
365     }
366     if ( !pAnchorFrame->GetDrawObjs() )
367     {
368         // nothing to do, if no floating screen object is registered at the anchor frame.
369         return true;
370     }
371 
372     bool bSuccess( true );
373 
374     for ( size_t i = 0; i < pAnchorFrame->GetDrawObjs()->size(); ++i )
375     {
376         SwAnchoredObject* pAnchoredObj = (*pAnchorFrame->GetDrawObjs())[i];
377 
378         // check, if object's anchor is on the given page frame or
379         // object is registered at the given page frame.
380         // --> #i26945# - check, if the anchor character of the
381         // anchored object is located in a follow text frame. If this anchor
382         // follow text frame differs from the given anchor frame, the given
383         // anchor frame is a 'master' text frame of the anchor follow text frame.
384         // If the anchor follow text frame is in the same body as its 'master'
385         // text frame, do not format the anchored object.
386         // E.g., this situation can occur during the table row splitting algorithm.
387         SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
388         const bool bAnchoredAtFollowInSameBodyAsMaster =
389                 pAnchorCharFrame && pAnchorCharFrame->IsFollow() &&
390                 pAnchorCharFrame != pAnchoredObj->GetAnchorFrame() &&
391                 pAnchorCharFrame->FindBodyFrame() ==
392                     static_cast<SwTextFrame*>(pAnchoredObj->AnchorFrame())->FindBodyFrame();
393         if ( bAnchoredAtFollowInSameBodyAsMaster )
394         {
395             continue;
396         }
397         // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
398         // is replaced by method <FindPageFrameOfAnchor()>. It's return value
399         // have to be checked.
400         SwPageFrame* pPageFrameOfAnchor = pAnchoredObj->FindPageFrameOfAnchor();
401         OSL_ENSURE( pPageFrameOfAnchor,
402                 "<SwObjectFormatter::FormatObjsAtFrame_()> - missing page frame." );
403         // --> #i26945#
404         if ( pPageFrameOfAnchor && pPageFrameOfAnchor == &mrPageFrame )
405         {
406             // if format of object fails, stop formatting and pass fail to
407             // calling method via the return value.
408             if ( !DoFormatObj( *pAnchoredObj ) )
409             {
410                 bSuccess = false;
411                 break;
412             }
413 
414             // considering changes at <pAnchorFrame->GetDrawObjs()> during
415             // format of the object.
416             if ( !pAnchorFrame->GetDrawObjs() ||
417                  i > pAnchorFrame->GetDrawObjs()->size() )
418             {
419                 break;
420             }
421             else
422             {
423                 const size_t nActPosOfObj =
424                     pAnchorFrame->GetDrawObjs()->ListPosOf( *pAnchoredObj );
425                 if ( nActPosOfObj == pAnchorFrame->GetDrawObjs()->size() ||
426                      nActPosOfObj > i )
427                 {
428                     --i;
429                 }
430                 else if ( nActPosOfObj < i )
431                 {
432                     i = nActPosOfObj;
433                 }
434             }
435         }
436     } // end of loop on <pAnchorFrame->.GetDrawObjs()>
437 
438     return bSuccess;
439 }
440 
441 /** accessor to collected anchored object
442 
443     #i28701#
444 */
GetCollectedObj(const sal_uInt32 _nIndex)445 SwAnchoredObject* SwObjectFormatter::GetCollectedObj( const sal_uInt32 _nIndex )
446 {
447     return mpPgNumAndTypeOfAnchors ? (*mpPgNumAndTypeOfAnchors)[_nIndex] : nullptr;
448 }
449 
450 /** accessor to 'anchor' page number of collected anchored object
451 
452     #i28701#
453 */
GetPgNumOfCollected(const sal_uInt32 _nIndex)454 sal_uInt32 SwObjectFormatter::GetPgNumOfCollected( const sal_uInt32 _nIndex )
455 {
456     return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->GetPageNum(_nIndex) : 0;
457 }
458 
459 /** accessor to 'anchor' type of collected anchored object
460 
461     #i26945#
462 */
IsCollectedAnchoredAtMaster(const sal_uInt32 _nIndex)463 bool SwObjectFormatter::IsCollectedAnchoredAtMaster( const sal_uInt32 _nIndex )
464 {
465     return mpPgNumAndTypeOfAnchors == nullptr
466            || mpPgNumAndTypeOfAnchors->AnchoredAtMaster(_nIndex);
467 }
468 
469 /** accessor to total number of collected anchored objects
470 
471     #i28701#
472 */
CountOfCollected()473 sal_uInt32 SwObjectFormatter::CountOfCollected()
474 {
475     return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->Count() : 0;
476 }
477 
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
479