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 <hints.hxx>
21 #include <comphelper/flagguard.hxx>
22 #include <tools/line.hxx>
23 #include <editeng/opaqitem.hxx>
24 #include <editeng/protitem.hxx>
25 #include <vcl/settings.hxx>
26 #include <fmtpdsc.hxx>
27 #include <fmtsrnd.hxx>
28 #include <pagedesc.hxx>
29 #include <pagefrm.hxx>
30 #include <rootfrm.hxx>
31 #include <ftnfrm.hxx>
32 #include <flyfrm.hxx>
33 #include <tabfrm.hxx>
34 #include <rowfrm.hxx>
35 #include <cellfrm.hxx>
36 #include <txtfrm.hxx>
37 #include <notxtfrm.hxx>
38 #include <viewopt.hxx>
39 #include <DocumentSettingManager.hxx>
40 #include <viscrs.hxx>
41 #include <dflyobj.hxx>
42 #include <crstate.hxx>
43 #include <dcontact.hxx>
44 #include <sortedobjs.hxx>
45 #include <txatbase.hxx>
46 #include <fmtfld.hxx>
47 #include <fldbas.hxx>
48 #include <frmatr.hxx>
49 #include <frmtool.hxx>
50 #include <ndtxt.hxx>
51 #include <undobj.hxx>
52 
53 #include <swselectionlist.hxx>
54 #include <comphelper/lok.hxx>
55 #include <osl/diagnose.h>
56 
57 namespace {
lcl_GetModelPositionForViewPoint_Objects(const SwPageFrame * pPageFrame,bool bSearchBackground,SwPosition * pPos,Point const & rPoint,SwCursorMoveState * pCMS)58     bool lcl_GetModelPositionForViewPoint_Objects( const SwPageFrame* pPageFrame, bool bSearchBackground,
59            SwPosition *pPos, Point const & rPoint, SwCursorMoveState* pCMS  )
60     {
61         bool bRet = false;
62         Point aPoint( rPoint );
63         SwOrderIter aIter( pPageFrame );
64         aIter.Top();
65         while ( aIter() )
66         {
67             const SwVirtFlyDrawObj* pObj =
68                                 static_cast<const SwVirtFlyDrawObj*>(aIter());
69             const SwAnchoredObject* pAnchoredObj = GetUserCall( aIter() )->GetAnchoredObj( aIter() );
70             const SwFormatSurround& rSurround = pAnchoredObj->GetFrameFormat().GetSurround();
71             const SvxOpaqueItem& rOpaque = pAnchoredObj->GetFrameFormat().GetOpaque();
72             bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue();
73 
74             bool bBackgroundMatches = bInBackground == bSearchBackground;
75 
76             const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
77             if ( pFly && bBackgroundMatches &&
78                  ( ( pCMS && pCMS->m_bSetInReadOnly ) ||
79                    !pFly->IsProtected() ) &&
80                  pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) )
81             {
82                 bRet = true;
83                 break;
84             }
85 
86             if ( pCMS && pCMS->m_bStop )
87                 return false;
88             aIter.Prev();
89         }
90         return bRet;
91     }
92 
lcl_getDistance(const SwRect & rRect,const Point & rPoint)93     double lcl_getDistance( const SwRect& rRect, const Point& rPoint )
94     {
95         double nDist = 0.0;
96 
97         // If the point is inside the rectangle, then distance is 0
98         // Otherwise, compute the distance to the center of the rectangle.
99         if ( !rRect.IsInside( rPoint ) )
100         {
101             tools::Line aLine( rPoint, rRect.Center( ) );
102             nDist = aLine.GetLength( );
103         }
104 
105         return nDist;
106     }
107 }
108 
109 namespace {
110 
111 //For SwFlyFrame::GetModelPositionForViewPoint
112 class SwCursorOszControl
113 {
114 public:
115     // So the compiler can initialize the class already. No DTOR and member
116     // as public members
117     const SwFlyFrame* m_pEntry;
118     const SwFlyFrame* m_pStack1;
119     const SwFlyFrame* m_pStack2;
120 
ChkOsz(const SwFlyFrame * pFly)121     bool ChkOsz( const SwFlyFrame *pFly )
122         {
123             bool bRet = true;
124             if (pFly != m_pStack1 && pFly != m_pStack2)
125             {
126                 m_pStack1 = m_pStack2;
127                 m_pStack2 = pFly;
128                 bRet  = false;
129             }
130             return bRet;
131         }
132 
Entry(const SwFlyFrame * pFly)133     void Entry( const SwFlyFrame *pFly )
134         {
135             if (!m_pEntry)
136                 m_pEntry = m_pStack1 = pFly;
137         }
138 
Exit(const SwFlyFrame * pFly)139     void Exit( const SwFlyFrame *pFly )
140         {
141             if (pFly == m_pEntry)
142                 m_pEntry = m_pStack1 = m_pStack2 = nullptr;
143         }
144 };
145 
146 }
147 
148 static SwCursorOszControl g_OszCtrl = { nullptr, nullptr, nullptr };
149 
150 /** Searches the ContentFrame owning the PrtArea containing the point. */
GetModelPositionForViewPoint(SwPosition * pPos,Point & rPoint,SwCursorMoveState * pCMS,bool) const151 bool SwLayoutFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
152                                SwCursorMoveState* pCMS, bool ) const
153 {
154     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
155     bool bRet = false;
156     const SwFrame *pFrame = Lower();
157     while ( !bRet && pFrame )
158     {
159         pFrame->Calc(pRenderContext);
160 
161         // #i43742# New function
162         const bool bContentCheck = pFrame->IsTextFrame() && pCMS && pCMS->m_bContentCheck;
163         const SwRect aPaintRect( bContentCheck ?
164                                  pFrame->UnionFrame() :
165                                  pFrame->GetPaintArea() );
166 
167         if ( aPaintRect.IsInside( rPoint ) &&
168              ( bContentCheck || pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS ) ) )
169             bRet = true;
170         else
171             pFrame = pFrame->GetNext();
172         if ( pCMS && pCMS->m_bStop )
173             return false;
174     }
175     return bRet;
176 }
177 
178 /** Searches the page containing the searched point. */
179 
GetModelPositionForViewPoint(SwPosition * pPos,Point & rPoint,SwCursorMoveState * pCMS,bool bTestBackground) const180 bool SwPageFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
181                              SwCursorMoveState* pCMS, bool bTestBackground ) const
182 {
183     Point aPoint( rPoint );
184 
185     // check, if we have to adjust the point
186     if ( !getFrameArea().IsInside( aPoint ) )
187     {
188         aPoint.setX( std::max( aPoint.X(), getFrameArea().Left() ) );
189         aPoint.setX( std::min( aPoint.X(), getFrameArea().Right() ) );
190         aPoint.setY( std::max( aPoint.Y(), getFrameArea().Top() ) );
191         aPoint.setY( std::min( aPoint.Y(), getFrameArea().Bottom() ) );
192     }
193 
194     bool bRet = false;
195     //Could it be a free flying one?
196     //If his content should be protected, we can't set the Cursor in it, thus
197     //all changes should be impossible.
198     if ( GetSortedObjs() )
199     {
200         bRet = lcl_GetModelPositionForViewPoint_Objects( this, false, pPos, rPoint, pCMS );
201     }
202 
203     if ( !bRet )
204     {
205         SwPosition aBackPos( *pPos );
206         SwPosition aTextPos( *pPos );
207 
208         //We fix the StartPoint if no Content below the page 'answers' and then
209         //start all over again one page before the current one.
210         //However we can't use Flys in such a case.
211         if (!SwLayoutFrame::GetModelPositionForViewPoint(&aTextPos, aPoint, pCMS))
212         {
213             if ( pCMS && (pCMS->m_bStop || pCMS->m_bExactOnly) )
214             {
215                 pCMS->m_bStop = true;
216                 return false;
217             }
218 
219             const SwContentFrame *pCnt = GetContentPos( aPoint, false, false, pCMS, false );
220             // GetContentPos may have modified pCMS
221             if ( pCMS && pCMS->m_bStop )
222                 return false;
223 
224             bool bTextRet = false;
225 
226             OSL_ENSURE( pCnt, "Cursor is gone to a Black hole" );
227             if( pCMS && pCMS->m_pFill && pCnt->IsTextFrame() )
228                 bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, rPoint, pCMS );
229             else
230                 bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, aPoint, pCMS );
231 
232             if ( !bTextRet )
233             {
234                 // Set point to pCnt, delete mark
235                 // this may happen, if pCnt is hidden
236                 if (pCnt->IsTextFrame())
237                 {
238                     aTextPos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
239                 }
240                 else
241                 {
242                     assert(pCnt->IsNoTextFrame());
243                     aTextPos = SwPosition( *static_cast<SwNoTextFrame const*>(pCnt)->GetNode() );
244                 }
245             }
246         }
247 
248         SwContentNode* pContentNode = aTextPos.nNode.GetNode().GetContentNode();
249         bool bConsiderBackground = true;
250         // If the text position is a clickable field, then that should have priority.
251         if (pContentNode && pContentNode->IsTextNode())
252         {
253             SwTextNode* pTextNd = pContentNode->GetTextNode();
254             SwTextAttr* pTextAttr = pTextNd->GetTextAttrForCharAt(aTextPos.nContent.GetIndex(), RES_TXTATR_FIELD);
255             if (pTextAttr)
256             {
257                 const SwField* pField = pTextAttr->GetFormatField().GetField();
258                 if (pField->IsClickable())
259                     bConsiderBackground = false;
260             }
261         }
262 
263         bool bBackRet = false;
264         // Check objects in the background if nothing else matched
265         if ( GetSortedObjs() )
266         {
267             bBackRet = lcl_GetModelPositionForViewPoint_Objects( this, true, &aBackPos, rPoint, pCMS );
268         }
269 
270         if (bConsiderBackground && bTestBackground && bBackRet)
271         {
272             (*pPos) = aBackPos;
273         }
274         else if (!bBackRet)
275         {
276             (*pPos) = aTextPos;
277         }
278         else // bBackRet && !(bConsiderBackground && bTestBackground)
279         {
280             /* In order to provide a selection as accurate as possible when we have both
281              * text and background object, then we compute the distance between both
282              * would-be positions and the click point. The shortest distance wins.
283              */
284             double nTextDistance = 0;
285             bool bValidTextDistance = false;
286             if (pContentNode)
287             {
288                 SwContentFrame* pTextFrame = pContentNode->getLayoutFrame( getRootFrame( ) );
289 
290                 // try this again but prefer the "previous" position
291                 SwCursorMoveState aMoveState;
292                 SwCursorMoveState *const pState(pCMS ? pCMS : &aMoveState);
293                 comphelper::FlagRestorationGuard g(
294                         pState->m_bPosMatchesBounds, true);
295                 SwPosition prevTextPos(*pPos);
296                 if (SwLayoutFrame::GetModelPositionForViewPoint(&prevTextPos, aPoint, pState))
297                 {
298                     SwRect aTextRect;
299                     pTextFrame->GetCharRect(aTextRect, prevTextPos);
300 
301                     if (prevTextPos.nContent < pContentNode->Len())
302                     {
303                         // aRextRect is just a line on the left edge of the
304                         // previous character; to get a better measure from
305                         // lcl_getDistance, extend that to a rectangle over
306                         // the entire character.
307                         SwPosition const nextTextPos(prevTextPos.nNode,
308                                 SwIndex(prevTextPos.nContent, +1));
309                         SwRect nextTextRect;
310                         pTextFrame->GetCharRect(nextTextRect, nextTextPos);
311                         SwRectFnSet aRectFnSet(pTextFrame);
312                         if (aRectFnSet.GetTop(aTextRect) ==
313                             aRectFnSet.GetTop(nextTextRect)) // same line?
314                         {
315                             // need to handle mixed RTL/LTR portions somehow
316                             if (aRectFnSet.GetLeft(aTextRect) <
317                                 aRectFnSet.GetLeft(nextTextRect))
318                             {
319                                 aRectFnSet.SetRight( aTextRect,
320                                     aRectFnSet.GetLeft(nextTextRect));
321                             }
322                             else // RTL
323                             {
324                                 aRectFnSet.SetLeft( aTextRect,
325                                     aRectFnSet.GetLeft(nextTextRect));
326                             }
327                         }
328                     }
329 
330                     nTextDistance = lcl_getDistance(aTextRect, rPoint);
331                     bValidTextDistance = true;
332                 }
333             }
334 
335             double nBackDistance = 0;
336             bool bValidBackDistance = false;
337             SwContentNode* pBackNd = aBackPos.nNode.GetNode( ).GetContentNode( );
338             if ( pBackNd && bConsiderBackground)
339             {
340                 // FIXME There are still cases were we don't have the proper node here.
341                 SwContentFrame* pBackFrame = pBackNd->getLayoutFrame( getRootFrame( ) );
342                 if (pBackFrame)
343                 {
344                     SwRect rBackRect;
345                     pBackFrame->GetCharRect( rBackRect, aBackPos );
346 
347                     nBackDistance = lcl_getDistance( rBackRect, rPoint );
348                     bValidBackDistance = true;
349                 }
350             }
351 
352             if ( bValidTextDistance && bValidBackDistance && basegfx::fTools::more( nTextDistance, nBackDistance ) )
353             {
354                 (*pPos) = aBackPos;
355             }
356             else
357             {
358                 (*pPos) = aTextPos;
359             }
360         }
361     }
362 
363     rPoint = aPoint;
364     return true;
365 }
366 
FillSelection(SwSelectionList & rList,const SwRect & rRect) const367 bool SwLayoutFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
368 {
369     if( rRect.IsOver(GetPaintArea()) )
370     {
371         const SwFrame* pFrame = Lower();
372         while( pFrame )
373         {
374             pFrame->FillSelection( rList, rRect );
375             pFrame = pFrame->GetNext();
376         }
377     }
378     return false;
379 }
380 
FillSelection(SwSelectionList & rList,const SwRect & rRect) const381 bool SwPageFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
382 {
383     bool bRet = false;
384     if( rRect.IsOver(GetPaintArea()) )
385     {
386         bRet = SwLayoutFrame::FillSelection( rList, rRect );
387         if( GetSortedObjs() )
388         {
389             const SwSortedObjs &rObjs = *GetSortedObjs();
390             for (SwAnchoredObject* pAnchoredObj : rObjs)
391             {
392                 const SwFlyFrame* pFly = dynamic_cast<const SwFlyFrame*>(pAnchoredObj);
393                 if( !pFly )
394                     continue;
395                 if( pFly->FillSelection( rList, rRect ) )
396                     bRet = true;
397             }
398         }
399     }
400     return bRet;
401 }
402 
FillSelection(SwSelectionList & aSelList,const SwRect & rRect) const403 bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const
404 {
405     const SwFrame *pPage = Lower();
406     const tools::Long nBottom = rRect.Bottom();
407     while( pPage )
408     {
409         if( pPage->getFrameArea().Top() < nBottom )
410         {
411             if( pPage->getFrameArea().Bottom() > rRect.Top() )
412                 pPage->FillSelection( aSelList, rRect );
413             pPage = pPage->GetNext();
414         }
415         else
416             pPage = nullptr;
417     }
418     return !aSelList.isEmpty();
419 }
420 
421 /** Primary passes the call to the first page.
422  *
423  *  @return false, if the passed Point gets changed
424  */
GetModelPositionForViewPoint(SwPosition * pPos,Point & rPoint,SwCursorMoveState * pCMS,bool bTestBackground) const425 bool SwRootFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
426                              SwCursorMoveState* pCMS, bool bTestBackground ) const
427 {
428     const bool bOldAction = IsCallbackActionEnabled();
429     const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
430     OSL_ENSURE( (Lower() && Lower()->IsPageFrame()), "No PageFrame found." );
431     if( pCMS && pCMS->m_pFill )
432         pCMS->m_bFillRet = false;
433     Point aOldPoint = rPoint;
434 
435     // search for page containing rPoint. The borders around the pages are considered
436     const SwPageFrame* pPage = GetPageAtPos( rPoint, nullptr, true );
437 
438     // #i95626#
439     // special handling for <rPoint> beyond root frames area
440     if ( !pPage &&
441          rPoint.X() > getFrameArea().Right() &&
442          rPoint.Y() > getFrameArea().Bottom() )
443     {
444         pPage = dynamic_cast<const SwPageFrame*>(Lower());
445         while ( pPage && pPage->GetNext() )
446         {
447             pPage = dynamic_cast<const SwPageFrame*>(pPage->GetNext());
448         }
449     }
450     if ( pPage )
451     {
452         pPage->SwPageFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS, bTestBackground );
453     }
454 
455     const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
456     if( pCMS )
457     {
458         if( pCMS->m_bStop )
459             return false;
460         if( pCMS->m_pFill )
461             return pCMS->m_bFillRet;
462     }
463     return aOldPoint == rPoint;
464 }
465 
466 /**
467  * If this is about a Content-carrying cell the Cursor will be force inserted into one of the ContentFrames
468  * if there are no other options.
469  *
470  * There is no entry for protected cells.
471  */
GetModelPositionForViewPoint(SwPosition * pPos,Point & rPoint,SwCursorMoveState * pCMS,bool) const472 bool SwCellFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
473                              SwCursorMoveState* pCMS, bool ) const
474 {
475     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
476     // cell frame does not necessarily have a lower (split table cell)
477     if ( !Lower() )
478         return false;
479 
480     if ( !(pCMS && pCMS->m_bSetInReadOnly) &&
481          GetFormat()->GetProtect().IsContentProtected() )
482         return false;
483 
484     if ( pCMS && pCMS->m_eState == CursorMoveState::TableSel )
485     {
486         const SwTabFrame *pTab = FindTabFrame();
487         if ( pTab->IsFollow() && pTab->IsInHeadline( *this ) )
488         {
489             pCMS->m_bStop = true;
490             return false;
491         }
492     }
493 
494     if ( Lower() )
495     {
496         if ( Lower()->IsLayoutFrame() )
497             return SwLayoutFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS );
498         else
499         {
500             Calc(pRenderContext);
501             bool bRet = false;
502 
503             const SwFrame *pFrame = Lower();
504             while ( pFrame && !bRet )
505             {
506                 pFrame->Calc(pRenderContext);
507                 if ( pFrame->getFrameArea().IsInside( rPoint ) )
508                 {
509                     bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
510                     if ( pCMS && pCMS->m_bStop )
511                         return false;
512                 }
513                 pFrame = pFrame->GetNext();
514             }
515             if ( !bRet )
516             {
517                 const bool bFill = pCMS && pCMS->m_pFill;
518                 Point aPoint( rPoint );
519                 const SwContentFrame *pCnt = GetContentPos( rPoint, true );
520                 if( bFill && pCnt->IsTextFrame() )
521                 {
522                     rPoint = aPoint;
523                 }
524                 pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
525             }
526             return true;
527         }
528     }
529 
530     return false;
531 }
532 
533 //Problem: If two Flys have the same size and share the same position then
534 //they end inside each other.
535 //Because we recursively check if a Point doesn't randomly lie inside another
536 //fly which lies completely inside the current Fly we could trigger an endless
537 //loop with the mentioned situation above.
538 //Using the helper class SwCursorOszControl we prevent the recursion. During
539 //a recursion GetModelPositionForViewPoint picks the one which lies on top.
GetModelPositionForViewPoint(SwPosition * pPos,Point & rPoint,SwCursorMoveState * pCMS,bool) const540 bool SwFlyFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
541                             SwCursorMoveState* pCMS, bool ) const
542 {
543     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
544     g_OszCtrl.Entry( this );
545 
546     //If the Points lies inside the Fly, we try hard to set the Cursor inside it.
547     //However if the Point sits inside a Fly which is completely located inside
548     //the current one, we call GetModelPositionForViewPoint for it.
549     Calc(pRenderContext);
550     bool bInside = getFrameArea().IsInside( rPoint ) && Lower();
551     bool bRet = false;
552 
553     //If a Frame contains a graphic, but only text was requested, it basically
554     //won't accept the Cursor.
555     if ( bInside && pCMS && pCMS->m_eState == CursorMoveState::SetOnlyText &&
556          (!Lower() || Lower()->IsNoTextFrame()) )
557         bInside = false;
558 
559     const SwPageFrame *pPage = FindPageFrame();
560     if ( bInside && pPage && pPage->GetSortedObjs() )
561     {
562         SwOrderIter aIter( pPage );
563         aIter.Top();
564         while ( aIter() && !bRet )
565         {
566             const SwVirtFlyDrawObj* pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
567             const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
568             if ( pFly && pFly->getFrameArea().IsInside( rPoint ) &&
569                  getFrameArea().IsInside( pFly->getFrameArea() ) )
570             {
571                 if (g_OszCtrl.ChkOsz(pFly))
572                     break;
573                 bRet = pFly->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
574                 if ( bRet )
575                     break;
576                 if ( pCMS && pCMS->m_bStop )
577                     return false;
578             }
579             aIter.Next();
580         }
581     }
582 
583     while ( bInside && !bRet )
584     {
585         const SwFrame *pFrame = Lower();
586         while ( pFrame && !bRet )
587         {
588             pFrame->Calc(pRenderContext);
589             if ( pFrame->getFrameArea().IsInside( rPoint ) )
590             {
591                 bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
592                 if ( pCMS && pCMS->m_bStop )
593                     return false;
594             }
595             pFrame = pFrame->GetNext();
596         }
597         if ( !bRet )
598         {
599             const bool bFill = pCMS && pCMS->m_pFill;
600             Point aPoint( rPoint );
601             const SwContentFrame *pCnt = GetContentPos( rPoint, true, false, pCMS );
602             if ( pCMS && pCMS->m_bStop )
603                 return false;
604             if( bFill && pCnt->IsTextFrame() )
605             {
606                 rPoint = aPoint;
607             }
608             pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
609             bRet = true;
610         }
611     }
612     g_OszCtrl.Exit( this );
613     return bRet;
614 }
615 
616 /** Layout dependent cursor travelling */
LeftMargin(SwPaM * pPam) const617 bool SwNoTextFrame::LeftMargin(SwPaM *pPam) const
618 {
619     if( &pPam->GetNode() != GetNode() )
620         return false;
621     const_cast<SwContentNode*>(GetNode())->
622         MakeStartIndex(&pPam->GetPoint()->nContent);
623     return true;
624 }
625 
RightMargin(SwPaM * pPam,bool) const626 bool SwNoTextFrame::RightMargin(SwPaM *pPam, bool) const
627 {
628     if( &pPam->GetNode() != GetNode() )
629         return false;
630     const_cast<SwContentNode*>(GetNode())->
631         MakeEndIndex(&pPam->GetPoint()->nContent);
632     return true;
633 }
634 
lcl_GetNxtCnt(const SwContentFrame * pCnt)635 static const SwContentFrame *lcl_GetNxtCnt( const SwContentFrame* pCnt )
636 {
637     return pCnt->GetNextContentFrame();
638 }
639 
lcl_GetPrvCnt(const SwContentFrame * pCnt)640 static const SwContentFrame *lcl_GetPrvCnt( const SwContentFrame* pCnt )
641 {
642     return pCnt->GetPrevContentFrame();
643 }
644 
645 typedef const SwContentFrame *(*GetNxtPrvCnt)( const SwContentFrame* );
646 
647 /// Frame in repeated headline?
lcl_IsInRepeatedHeadline(const SwFrame * pFrame,const SwTabFrame ** ppTFrame=nullptr)648 static bool lcl_IsInRepeatedHeadline( const SwFrame *pFrame,
649                                     const SwTabFrame** ppTFrame = nullptr )
650 {
651     const SwTabFrame *pTab = pFrame->FindTabFrame();
652     if( ppTFrame )
653         *ppTFrame = pTab;
654     return pTab && pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
655 }
656 
657 /// Skip protected table cells. Optionally also skip repeated headlines.
658 //MA 1998-01-26: Chg also skip other protected areas
659 //FME: Skip follow flow cells
lcl_MissProtectedFrames(const SwContentFrame * pCnt,GetNxtPrvCnt fnNxtPrv,bool bMissHeadline,bool bInReadOnly,bool bMissFollowFlowLine)660 static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCnt,
661                                                    GetNxtPrvCnt fnNxtPrv,
662                                                    bool bMissHeadline,
663                                                    bool bInReadOnly,
664                                                    bool bMissFollowFlowLine )
665 {
666     if ( pCnt && pCnt->IsInTab() )
667     {
668         bool bProtect = true;
669         while ( pCnt && bProtect )
670         {
671             const SwLayoutFrame *pCell = pCnt->GetUpper();
672             while ( pCell && !pCell->IsCellFrame() )
673                 pCell = pCell->GetUpper();
674             if ( !pCell ||
675                     ( ( bInReadOnly || !pCell->GetFormat()->GetProtect().IsContentProtected() ) &&
676                       ( !bMissHeadline || !lcl_IsInRepeatedHeadline( pCell ) ) &&
677                       ( !bMissFollowFlowLine || !pCell->IsInFollowFlowRow() ) &&
678                         !pCell->IsCoveredCell() ) )
679                 bProtect = false;
680             else
681                 pCnt = (*fnNxtPrv)( pCnt );
682         }
683     }
684     else if ( !bInReadOnly )
685         while ( pCnt && pCnt->IsProtected() )
686             pCnt = (*fnNxtPrv)( pCnt );
687 
688     return pCnt;
689 }
690 
lcl_UpDown(SwPaM * pPam,const SwContentFrame * pStart,GetNxtPrvCnt fnNxtPrv,bool bInReadOnly)691 static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
692                     GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
693 {
694     OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetNode().GetIndex()),
695             "lcl_UpDown doesn't work for others." );
696 
697     const SwContentFrame *pCnt = nullptr;
698 
699     //We have to cheat a little bit during a table selection: Go to the
700     //beginning of the cell while going up and go to the end of the cell while
701     //going down.
702     bool bTableSel = false;
703     if ( pStart->IsInTab() &&
704         pPam->GetNode().StartOfSectionNode() !=
705         pPam->GetNode( false ).StartOfSectionNode() )
706     {
707         bTableSel = true;
708         const SwLayoutFrame  *pCell = pStart->GetUpper();
709         while ( !pCell->IsCellFrame() )
710             pCell = pCell->GetUpper();
711 
712         // Check, if cell has a Prev/Follow cell:
713         const bool bFwd = ( fnNxtPrv == lcl_GetNxtCnt );
714         const SwLayoutFrame* pTmpCell = bFwd ?
715             static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
716             static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
717 
718         const SwContentFrame* pTmpStart = pStart;
719         while ( pTmpCell && nullptr != ( pTmpStart = pTmpCell->ContainsContent() ) )
720         {
721             pCell = pTmpCell;
722             pTmpCell = bFwd ?
723                 static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
724                 static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
725         }
726         const SwContentFrame *pNxt = pCnt = pTmpStart;
727 
728         while ( pCell->IsAnLower( pNxt ) )
729         {
730             pCnt = pNxt;
731             pNxt = (*fnNxtPrv)( pNxt );
732         }
733     }
734 
735     pCnt = (*fnNxtPrv)( pCnt ? pCnt : pStart );
736     pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
737 
738     const SwTabFrame *pStTab = pStart->FindTabFrame();
739     const SwTabFrame *pTable = nullptr;
740     const bool bTab = pStTab || (pCnt && pCnt->IsInTab());
741     bool bEnd = !bTab;
742 
743     const SwFrame* pVertRefFrame = pStart;
744     if ( bTableSel && pStTab )
745         pVertRefFrame = pStTab;
746     SwRectFnSet aRectFnSet(pVertRefFrame);
747 
748     SwTwips nX = 0;
749     if ( bTab )
750     {
751         // pStart or pCnt is inside a table. nX will be used for travelling:
752         SwRect aRect( pStart->getFrameArea() );
753         pStart->GetCharRect( aRect, *pPam->GetPoint() );
754         Point aCenter = aRect.Center();
755         nX = aRectFnSet.IsVert() ? aCenter.Y() : aCenter.X();
756 
757         pTable = pCnt ? pCnt->FindTabFrame() : nullptr;
758         if ( !pTable )
759             pTable = pStTab;
760 
761         if ( pStTab &&
762             !pStTab->GetUpper()->IsInTab() &&
763             !pTable->GetUpper()->IsInTab() )
764         {
765             const SwFrame *pCell = pStart->GetUpper();
766             while ( pCell && !pCell->IsCellFrame() )
767                 pCell = pCell->GetUpper();
768             OSL_ENSURE( pCell, "could not find the cell" );
769             nX = aRectFnSet.XInc(aRectFnSet.GetLeft(pCell->getFrameArea()),
770                                  aRectFnSet.GetWidth(pCell->getFrameArea()) / 2);
771 
772             //The flow leads from one table to the next. The X-value needs to be
773             //corrected based on the middle of the starting cell by the amount
774             //of the offset of the tables.
775             if ( pStTab != pTable )
776             {
777                 nX += aRectFnSet.GetLeft(pTable->getFrameArea()) -
778                       aRectFnSet.GetLeft(pStTab->getFrameArea());
779             }
780         }
781 
782         // Restrict nX to the left and right borders of pTab:
783         // (is this really necessary?)
784         if (pTable && !pTable->GetUpper()->IsInTab())
785         {
786             const bool bRTL = pTable->IsRightToLeft();
787             const tools::Long nPrtLeft = bRTL ?
788                                 aRectFnSet.GetPrtRight(*pTable) :
789                                 aRectFnSet.GetPrtLeft(*pTable);
790             if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0))
791                 nX = nPrtLeft;
792             else
793             {
794                 const tools::Long nPrtRight = bRTL ?
795                                     aRectFnSet.GetPrtLeft(*pTable) :
796                                     aRectFnSet.GetPrtRight(*pTable);
797                 if (bRTL != (aRectFnSet.XDiff(nX, nPrtRight) > 0))
798                     nX = nPrtRight;
799             }
800         }
801     }
802 
803     do
804     {
805         //If I'm in the DocumentBody, I want to stay there.
806         if ( pStart->IsInDocBody() )
807         {
808             while ( pCnt && (!pCnt->IsInDocBody() ||
809                              (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
810             {
811                 pCnt = (*fnNxtPrv)( pCnt );
812                 pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
813             }
814         }
815 
816         //If I'm in the FootNoteArea, I try to reach the next FootNoteArea in
817         //case of necessity.
818         else if ( pStart->IsInFootnote() )
819         {
820             while ( pCnt && (!pCnt->IsInFootnote() ||
821                             (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
822             {
823                 pCnt = (*fnNxtPrv)( pCnt );
824                 pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
825             }
826         }
827 
828         //In Flys we can go ahead blindly as long as we find a Content.
829         else if ( pStart->IsInFly() )
830         {
831             if ( pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow() )
832             {
833                 pCnt = (*fnNxtPrv)( pCnt );
834                 pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
835             }
836         }
837 
838         //Otherwise I'll just refuse to leave to current area.
839         else if ( pCnt )
840         {
841             const SwFrame *pUp = pStart->GetUpper();
842             while (pUp && pUp->GetUpper() && !(pUp->GetType() & FRM_HEADFOOT))
843                 pUp = pUp->GetUpper();
844             bool bSame = false;
845             const SwFrame *pCntUp = pCnt->GetUpper();
846             while ( pCntUp && !bSame )
847             {
848                 if ( pUp == pCntUp )
849                     bSame = true;
850                 else
851                     pCntUp = pCntUp->GetUpper();
852             }
853             if ( !bSame )
854                 pCnt = nullptr;
855             else if (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()) // i73332
856             {
857                 pCnt = (*fnNxtPrv)( pCnt );
858                 pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
859             }
860         }
861 
862         if ( bTab )
863         {
864             if ( !pCnt )
865                 bEnd = true;
866             else
867             {
868                 const SwTabFrame *pTab = pCnt->FindTabFrame();
869                 if( !pTab )
870                     bEnd = true;
871                 else
872                 {
873                     if ( pTab != pTable )
874                     {
875                         //The flow leads from one table to the next. The X-value
876                         //needs to be corrected by the amount of the offset of
877                         //the tables
878                         if ( pTable &&
879                               !pTab->GetUpper()->IsInTab() &&
880                             !pTable->GetUpper()->IsInTab() )
881                             nX += pTab->getFrameArea().Left() - pTable->getFrameArea().Left();
882                         pTable = pTab;
883                     }
884                     const SwLayoutFrame *pCell = pCnt->GetUpper();
885                     while ( pCell && !pCell->IsCellFrame() )
886                         pCell = pCell->GetUpper();
887 
888                     Point aInsideCell;
889                     Point aInsideCnt;
890                     if ( pCell )
891                     {
892                         tools::Long nTmpTop = aRectFnSet.GetTop(pCell->getFrameArea());
893                         if ( aRectFnSet.IsVert() )
894                         {
895                             if ( nTmpTop )
896                                 nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
897 
898                             aInsideCell = Point( nTmpTop, nX );
899                         }
900                         else
901                             aInsideCell = Point( nX, nTmpTop );
902                     }
903 
904                     tools::Long nTmpTop = aRectFnSet.GetTop(pCnt->getFrameArea());
905                     if ( aRectFnSet.IsVert() )
906                     {
907                         if ( nTmpTop )
908                             nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
909 
910                         aInsideCnt = Point( nTmpTop, nX );
911                     }
912                     else
913                         aInsideCnt = Point( nX, nTmpTop );
914 
915                     if ( pCell && pCell->getFrameArea().IsInside( aInsideCell ) )
916                     {
917                         bEnd = true;
918                         //Get the right Content out of the cell.
919                         if ( !pCnt->getFrameArea().IsInside( aInsideCnt ) )
920                         {
921                             pCnt = pCell->ContainsContent();
922                             if ( fnNxtPrv == lcl_GetPrvCnt )
923                                 while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) )
924                                     pCnt = pCnt->GetNextContentFrame();
925                         }
926                     }
927                     else if ( pCnt->getFrameArea().IsInside( aInsideCnt ) )
928                         bEnd = true;
929                 }
930             }
931             if ( !bEnd )
932             {
933                 pCnt = (*fnNxtPrv)( pCnt );
934                 pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
935             }
936         }
937 
938     } while ( !bEnd ||
939               (pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()));
940 
941     if (pCnt == nullptr)
942     {
943         return false;
944     }
945     if (pCnt->IsTextFrame())
946     {
947         SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pCnt));
948         *pPam->GetPoint() = pFrame->MapViewToModelPos(TextFrameIndex(
949                 fnNxtPrv == lcl_GetPrvCnt
950                     ? pFrame->GetText().getLength()
951                     : 0));
952     }
953     else
954     {   // set the Point on the Content-Node
955         assert(pCnt->IsNoTextFrame());
956         SwContentNode *const pCNd = const_cast<SwContentNode*>(static_cast<SwNoTextFrame const*>(pCnt)->GetNode());
957         pPam->GetPoint()->nNode = *pCNd;
958         if ( fnNxtPrv == lcl_GetPrvCnt )
959             pCNd->MakeEndIndex( &pPam->GetPoint()->nContent );
960         else
961             pCNd->MakeStartIndex( &pPam->GetPoint()->nContent );
962     }
963     return true;
964 }
965 
UnitUp(SwPaM * pPam,const SwTwips,bool bInReadOnly) const966 bool SwContentFrame::UnitUp( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
967 {
968     return ::lcl_UpDown( pPam, this, lcl_GetPrvCnt, bInReadOnly );
969 }
970 
UnitDown(SwPaM * pPam,const SwTwips,bool bInReadOnly) const971 bool SwContentFrame::UnitDown( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
972 {
973     return ::lcl_UpDown( pPam, this, lcl_GetNxtCnt, bInReadOnly );
974 }
975 
976 /** Returns the number of the current page.
977  *
978  * If the method gets a PaM then the current page is the one in which the PaM sits. Otherwise the
979  * current page is the first one inside the VisibleArea. We only work on available pages!
980  */
GetCurrPage(const SwPaM * pActualCursor) const981 sal_uInt16 SwRootFrame::GetCurrPage( const SwPaM *pActualCursor ) const
982 {
983     OSL_ENSURE( pActualCursor, "got no page cursor" );
984     SwFrame const*const pActFrame = pActualCursor->GetPoint()->nNode.GetNode().
985                                     GetContentNode()->getLayoutFrame(this,
986                                                     pActualCursor->GetPoint());
987     return pActFrame->FindPageFrame()->GetPhyPageNum();
988 }
989 
990 /** Returns a PaM which sits at the beginning of the requested page.
991  *
992  * Formatting is done as far as necessary.
993  * The PaM sits on the last page, if the page number was chosen too big.
994  *
995  * @return Null, if the operation was not possible.
996  */
SetCurrPage(SwCursor * pToSet,sal_uInt16 nPageNum)997 sal_uInt16 SwRootFrame::SetCurrPage( SwCursor* pToSet, sal_uInt16 nPageNum )
998 {
999     vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
1000     OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
1001 
1002     SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
1003     bool bEnd =false;
1004     while ( !bEnd && pPage->GetPhyPageNum() != nPageNum )
1005     {   if ( pPage->GetNext() )
1006             pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1007         else
1008         {   //Search the first ContentFrame and format until a new page is started
1009             //or until the ContentFrame are all done.
1010             const SwContentFrame *pContent = pPage->ContainsContent();
1011             while ( pContent && pPage->IsAnLower( pContent ) )
1012             {
1013                 pContent->Calc(pRenderContext);
1014                 pContent = pContent->GetNextContentFrame();
1015             }
1016             //Either this is a new page or we found the last page.
1017             if ( pPage->GetNext() )
1018                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1019             else
1020                 bEnd = true;
1021         }
1022     }
1023     //pPage now points to the 'requested' page. Now we have to create the PaM
1024     //on the beginning of the first ContentFrame in the body-text.
1025     //If this is a footnote-page, the PaM will be set in the first footnote.
1026     const SwContentFrame *pContent = pPage->ContainsContent();
1027     if ( pPage->IsFootnotePage() )
1028         while ( pContent && !pContent->IsInFootnote() )
1029             pContent = pContent->GetNextContentFrame();
1030     else
1031         while ( pContent && !pContent->IsInDocBody() )
1032             pContent = pContent->GetNextContentFrame();
1033     if ( pContent )
1034     {
1035         assert(pContent->IsTextFrame());
1036         SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pContent));
1037         *pToSet->GetPoint() = pFrame->MapViewToModelPos(pFrame->GetOffset());
1038 
1039         SwShellCursor* pSCursor = dynamic_cast<SwShellCursor*>(pToSet);
1040         if( pSCursor )
1041         {
1042             Point &rPt = pSCursor->GetPtPos();
1043             rPt = pContent->getFrameArea().Pos();
1044             rPt += pContent->getFramePrintArea().Pos();
1045         }
1046         return pPage->GetPhyPageNum();
1047     }
1048     return 0;
1049 }
1050 
GetFirstSub(const SwLayoutFrame * pLayout)1051 SwContentFrame *GetFirstSub( const SwLayoutFrame *pLayout )
1052 {
1053     return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindFirstBodyContent();
1054 }
1055 
GetLastSub(const SwLayoutFrame * pLayout)1056 SwContentFrame *GetLastSub( const SwLayoutFrame *pLayout )
1057 {
1058     return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindLastBodyContent();
1059 }
1060 
GetNextFrame(const SwLayoutFrame * pFrame)1061 SwLayoutFrame *GetNextFrame( const SwLayoutFrame *pFrame )
1062 {
1063     SwLayoutFrame *pNext =
1064         (pFrame->GetNext() && pFrame->GetNext()->IsLayoutFrame()) ?
1065                                             const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetNext())) : nullptr;
1066     // #i39402# in case of an empty page
1067     if(pNext && !pNext->ContainsContent())
1068         pNext = (pNext->GetNext() && pNext->GetNext()->IsLayoutFrame()) ?
1069                                             static_cast<SwLayoutFrame*>(pNext->GetNext()) : nullptr;
1070     return pNext;
1071 }
1072 
GetThisFrame(const SwLayoutFrame * pFrame)1073 SwLayoutFrame *GetThisFrame( const SwLayoutFrame *pFrame )
1074 {
1075     return const_cast<SwLayoutFrame*>(pFrame);
1076 }
1077 
GetPrevFrame(const SwLayoutFrame * pFrame)1078 SwLayoutFrame *GetPrevFrame( const SwLayoutFrame *pFrame )
1079 {
1080     SwLayoutFrame *pPrev =
1081         (pFrame->GetPrev() && pFrame->GetPrev()->IsLayoutFrame()) ?
1082                                             const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetPrev())) : nullptr;
1083     // #i39402# in case of an empty page
1084     if(pPrev && !pPrev->ContainsContent())
1085         pPrev = (pPrev->GetPrev() && pPrev->GetPrev()->IsLayoutFrame()) ?
1086                                             static_cast<SwLayoutFrame*>(pPrev->GetPrev()) : nullptr;
1087     return pPrev;
1088 }
1089 
1090 /**
1091  * Returns the first/last Contentframe (controlled using the parameter fnPosPage)
1092  * of the current/previous/next page (controlled using the parameter fnWhichPage).
1093  */
GetFrameInPage(const SwContentFrame * pCnt,SwWhichPage fnWhichPage,SwPosPage fnPosPage,SwPaM * pPam)1094 bool GetFrameInPage( const SwContentFrame *pCnt, SwWhichPage fnWhichPage,
1095                    SwPosPage fnPosPage, SwPaM *pPam )
1096 {
1097     //First find the requested page, at first the current, then the one which
1098     //was requests through fnWichPage.
1099     const SwLayoutFrame *pLayoutFrame = pCnt->FindPageFrame();
1100     if ( !pLayoutFrame || (nullptr == (pLayoutFrame = (*fnWhichPage)(pLayoutFrame))) )
1101         return false;
1102 
1103     //Now the desired ContentFrame below the page
1104     pCnt = (*fnPosPage)(pLayoutFrame);
1105     if( nullptr == pCnt )
1106         return false;
1107     else
1108     {
1109         // repeated headlines in tables
1110         if ( pCnt->IsInTab() && fnPosPage == GetFirstSub )
1111         {
1112             const SwTabFrame* pTab = pCnt->FindTabFrame();
1113             if ( pTab->IsFollow() )
1114             {
1115                 if ( pTab->IsInHeadline( *pCnt ) )
1116                 {
1117                     SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
1118                     if ( pRow )
1119                     {
1120                         // We are in the first line of a follow table
1121                         // with repeated headings.
1122                         // To actually make a "real" move we take the first content
1123                         // of the next row
1124                         pCnt = pRow->ContainsContent();
1125                         if ( ! pCnt )
1126                             return false;
1127                     }
1128                 }
1129             }
1130         }
1131 
1132         assert(pCnt->IsTextFrame());
1133         SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pCnt));
1134         TextFrameIndex const nIdx((fnPosPage == GetFirstSub)
1135             ? pFrame->GetOffset()
1136             : (pFrame->GetFollow())
1137                 ? pFrame->GetFollow()->GetOffset() - TextFrameIndex(1)
1138                 : TextFrameIndex(pFrame->GetText().getLength()));
1139         *pPam->GetPoint() = pFrame->MapViewToModelPos(nIdx);
1140         return true;
1141     }
1142 }
1143 
CalcDiff(const Point & rPt1,const Point & rPt2)1144 static sal_uInt64 CalcDiff(const Point &rPt1, const Point &rPt2)
1145 {
1146     //Calculate the distance between the two points.
1147     //'delta' X^2 + 'delta'Y^2 = 'distance'^2
1148     sal_uInt64 dX = std::max( rPt1.X(), rPt2.X() ) -
1149                std::min( rPt1.X(), rPt2.X() ),
1150           dY = std::max( rPt1.Y(), rPt2.Y() ) -
1151                std::min( rPt1.Y(), rPt2.Y() );
1152     return (dX * dX) + (dY * dY);
1153 }
1154 
1155 /** Check if the point lies inside the page part in which also the ContentFrame lies.
1156  *
1157  * In this context header, page body, footer and footnote-container count as page part.
1158  * This will suit the purpose that the ContentFrame which lies in the "right" page part will be
1159  * accepted instead of one which doesn't lie there although his distance to the point is shorter.
1160  */
lcl_Inside(const SwContentFrame * pCnt,Point const & rPt)1161 static const SwLayoutFrame* lcl_Inside( const SwContentFrame *pCnt, Point const & rPt )
1162 {
1163     const SwLayoutFrame* pUp = pCnt->GetUpper();
1164     while( pUp )
1165     {
1166         if( pUp->IsPageBodyFrame() || pUp->IsFooterFrame() || pUp->IsHeaderFrame() )
1167         {
1168             if( rPt.Y() >= pUp->getFrameArea().Top() && rPt.Y() <= pUp->getFrameArea().Bottom() )
1169                 return pUp;
1170             return nullptr;
1171         }
1172         if( pUp->IsFootnoteContFrame() )
1173             return pUp->getFrameArea().IsInside( rPt ) ? pUp : nullptr;
1174         pUp = pUp->GetUpper();
1175     }
1176     return nullptr;
1177 }
1178 
1179 /** Search for the nearest Content to pass.
1180  *
1181  * Considers the previous, the current and the next page.
1182  * If no content is found, the area gets expanded until one is found.
1183  *
1184  * @return The 'semantically correct' position inside the PrtArea of the found ContentFrame.
1185  */
GetContentPos(Point & rPoint,const bool bDontLeave,const bool bBodyOnly,SwCursorMoveState * pCMS,const bool bDefaultExpand) const1186 const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint,
1187                                             const bool bDontLeave,
1188                                             const bool bBodyOnly,
1189                                             SwCursorMoveState *pCMS,
1190                                             const bool bDefaultExpand ) const
1191 {
1192     //Determine the first ContentFrame.
1193     const SwLayoutFrame *pStart = (!bDontLeave && bDefaultExpand && GetPrev()) ?
1194                                     static_cast<const SwLayoutFrame*>(GetPrev()) : this;
1195     const SwContentFrame *pContent = pStart->ContainsContent();
1196 
1197     if ( !pContent && (GetPrev() && !bDontLeave) )
1198         pContent = ContainsContent();
1199 
1200     if ( bBodyOnly && pContent && !pContent->IsInDocBody() )
1201         while ( pContent && !pContent->IsInDocBody() )
1202             pContent = pContent->GetNextContentFrame();
1203 
1204     const SwContentFrame *pActual= pContent;
1205     const SwLayoutFrame *pInside = nullptr;
1206     sal_uInt16 nMaxPage = GetPhyPageNum() + (bDefaultExpand ? 1 : 0);
1207     Point aPoint = rPoint;
1208     sal_uInt64 nDistance = SAL_MAX_UINT64;
1209 
1210     while ( true )  //A loop to be sure we always find one.
1211     {
1212         while ( pContent &&
1213                 ((!bDontLeave || IsAnLower( pContent )) &&
1214                 (pContent->GetPhyPageNum() <= nMaxPage)) )
1215         {
1216             if ( pContent->getFrameArea().Width() &&
1217                  ( !bBodyOnly || pContent->IsInDocBody() ) )
1218             {
1219                 //If the Content lies in a protected area (cell, Footnote, section),
1220                 //we search the next Content which is not protected.
1221                 const SwContentFrame *pComp = pContent;
1222                 pContent = ::lcl_MissProtectedFrames( pContent, lcl_GetNxtCnt, false,
1223                                         pCMS && pCMS->m_bSetInReadOnly, false );
1224                 if ( pComp != pContent )
1225                     continue;
1226 
1227                 if ( !pContent->IsTextFrame() || !static_cast<const SwTextFrame*>(pContent)->IsHiddenNow() )
1228                 {
1229                     SwRect aContentFrame( pContent->UnionFrame() );
1230                     if ( aContentFrame.IsInside( rPoint ) )
1231                     {
1232                         pActual = pContent;
1233                         aPoint = rPoint;
1234                         break;
1235                     }
1236                     //The distance from rPoint to the nearest Point of pContent
1237                     //will now be calculated.
1238                     Point aContentPoint( rPoint );
1239 
1240                     //First set the vertical position
1241                     if ( aContentFrame.Top() > aContentPoint.Y() )
1242                         aContentPoint.setY( aContentFrame.Top() );
1243                     else if ( aContentFrame.Bottom() < aContentPoint.Y() )
1244                         aContentPoint.setY( aContentFrame.Bottom() );
1245 
1246                     //Now the horizontal position
1247                     if ( aContentFrame.Left() > aContentPoint.X() )
1248                         aContentPoint.setX( aContentFrame.Left() );
1249                     else if ( aContentFrame.Right() < aContentPoint.X() )
1250                         aContentPoint.setX( aContentFrame.Right() );
1251 
1252                     // pInside is a page area in which the point lies. As soon
1253                     // as pInside != 0 only frames are accepted which are
1254                     // placed inside.
1255                     if( !pInside || ( pInside->IsAnLower( pContent ) &&
1256                         ( !pContent->IsInFootnote() || pInside->IsFootnoteContFrame() ) ) )
1257                     {
1258                         const sal_uInt64 nDiff = ::CalcDiff(aContentPoint, rPoint);
1259                         bool bBetter = nDiff < nDistance;  // This one is nearer
1260                         if( !pInside )
1261                         {
1262                             pInside = lcl_Inside( pContent, rPoint );
1263                             if( pInside )  // In the "right" page area
1264                                 bBetter = true;
1265                         }
1266                         if( bBetter )
1267                         {
1268                             aPoint = aContentPoint;
1269                             nDistance = nDiff;
1270                             pActual = pContent;
1271                         }
1272                     }
1273                 }
1274             }
1275             pContent = pContent->GetNextContentFrame();
1276             if ( bBodyOnly )
1277                 while ( pContent && !pContent->IsInDocBody() )
1278                     pContent = pContent->GetNextContentFrame();
1279         }
1280         if ( !pActual )
1281         {   //If we not yet found one we have to expand the searched
1282             //area, sometime we will find one!
1283             //MA 1997-01-09: Opt for many empty pages - if we only search inside
1284             //the body, we can expand the searched area sufficiently in one step.
1285             if ( bBodyOnly )
1286             {
1287                 while ( !pContent && pStart->GetPrev() )
1288                 {
1289                     ++nMaxPage;
1290                     if( !pStart->GetPrev()->IsLayoutFrame() )
1291                         return nullptr;
1292                     pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
1293                     pContent = pStart->IsInDocBody()
1294                                 ? pStart->ContainsContent()
1295                                 : pStart->FindPageFrame()->FindFirstBodyContent();
1296                 }
1297                 if ( !pContent )  // Somewhere down the road we have to start with one!
1298                 {
1299                     pContent = pStart->FindPageFrame()->GetUpper()->ContainsContent();
1300                     while ( pContent && !pContent->IsInDocBody() )
1301                         pContent = pContent->GetNextContentFrame();
1302                     if ( !pContent )
1303                         return nullptr;   // There is no document content yet!
1304                 }
1305             }
1306             else
1307             {
1308                 ++nMaxPage;
1309                 if ( pStart->GetPrev() )
1310                 {
1311                     if( !pStart->GetPrev()->IsLayoutFrame() )
1312                         return nullptr;
1313                     pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
1314                     pContent = pStart->ContainsContent();
1315                 }
1316                 else // Somewhere down the road we have to start with one!
1317                     pContent = pStart->FindPageFrame()->GetUpper()->ContainsContent();
1318             }
1319             pActual = pContent;
1320         }
1321         else
1322             break;
1323     }
1324 
1325     OSL_ENSURE( pActual, "no Content found." );
1326     OSL_ENSURE( !bBodyOnly || pActual->IsInDocBody(), "Content not in Body." );
1327 
1328     //Special case for selecting tables not in repeated TableHeadlines.
1329     if ( pActual->IsInTab() && pCMS && pCMS->m_eState == CursorMoveState::TableSel )
1330     {
1331         const SwTabFrame *pTab = pActual->FindTabFrame();
1332         if ( pTab->IsFollow() && pTab->IsInHeadline( *pActual ) )
1333         {
1334             pCMS->m_bStop = true;
1335             return nullptr;
1336         }
1337     }
1338 
1339     //A small correction at the first/last
1340     Size aActualSize( pActual->getFramePrintArea().SSize() );
1341     if ( aActualSize.Height() > pActual->GetUpper()->getFramePrintArea().Height() )
1342         aActualSize.setHeight( pActual->GetUpper()->getFramePrintArea().Height() );
1343 
1344     SwRectFnSet aRectFnSet(pActual);
1345     if ( !pActual->GetPrev() &&
1346          aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pActual),
1347                               aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) > 0 )
1348     {
1349         aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Top() );
1350         aPoint.setX( pActual->getFrameArea().Left() +
1351                         ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
1352                           pActual->getFramePrintArea().Right() :
1353                           pActual->getFramePrintArea().Left() ) );
1354     }
1355     else if ( !pActual->GetNext() &&
1356               aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pActual),
1357                                    aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) < 0 )
1358     {
1359         aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Bottom() );
1360         aPoint.setX( pActual->getFrameArea().Left() +
1361                         ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
1362                           pActual->getFramePrintArea().Left() :
1363                           pActual->getFramePrintArea().Right() ) );
1364     }
1365 
1366     //Bring the Point into the PrtArea
1367     const SwRect aRect( pActual->getFrameArea().Pos() + pActual->getFramePrintArea().Pos(),
1368                         aActualSize );
1369     if ( aPoint.Y() < aRect.Top() )
1370         aPoint.setY( aRect.Top() );
1371     else if ( aPoint.Y() > aRect.Bottom() )
1372         aPoint.setY( aRect.Bottom() );
1373     if ( aPoint.X() < aRect.Left() )
1374         aPoint.setX( aRect.Left() );
1375     else if ( aPoint.X() > aRect.Right() )
1376         aPoint.setX( aRect.Right() );
1377     rPoint = aPoint;
1378     return pActual;
1379 }
1380 
1381 /** Same as SwLayoutFrame::GetContentPos(). Specialized for fields and border. */
GetContentPosition(const Point & rPt,SwPosition & rPos) const1382 void SwPageFrame::GetContentPosition( const Point &rPt, SwPosition &rPos ) const
1383 {
1384     //Determine the first ContentFrame.
1385     const SwContentFrame *pContent = ContainsContent();
1386     if ( pContent )
1387     {
1388         //Look back one more (if possible).
1389         const SwContentFrame *pTmp = pContent->GetPrevContentFrame();
1390         while ( pTmp && !pTmp->IsInDocBody() )
1391             pTmp = pTmp->GetPrevContentFrame();
1392         if ( pTmp )
1393             pContent = pTmp;
1394     }
1395     else
1396         pContent = GetUpper()->ContainsContent();
1397 
1398     const SwContentFrame *pAct = pContent;
1399     Point aAct       = rPt;
1400     sal_uInt64 nDist = SAL_MAX_UINT64;
1401 
1402     while ( pContent )
1403     {
1404         SwRect aContentFrame( pContent->UnionFrame() );
1405         if ( aContentFrame.IsInside( rPt ) )
1406         {
1407             //This is the nearest one.
1408             pAct = pContent;
1409             break;
1410         }
1411 
1412         //Calculate the distance from rPt to the nearest point of pContent.
1413         Point aPoint( rPt );
1414 
1415         //Calculate the vertical position first
1416         if ( aContentFrame.Top() > rPt.Y() )
1417             aPoint.setY( aContentFrame.Top() );
1418         else if ( aContentFrame.Bottom() < rPt.Y() )
1419             aPoint.setY( aContentFrame.Bottom() );
1420 
1421         //And now the horizontal position
1422         if ( aContentFrame.Left() > rPt.X() )
1423             aPoint.setX( aContentFrame.Left() );
1424         else if ( aContentFrame.Right() < rPt.X() )
1425             aPoint.setX( aContentFrame.Right() );
1426 
1427         const sal_uInt64 nDiff = ::CalcDiff( aPoint, rPt );
1428         if ( nDiff < nDist )
1429         {
1430             aAct    = aPoint;
1431             nDist   = nDiff;
1432             pAct    = pContent;
1433         }
1434         else if ( aContentFrame.Top() > getFrameArea().Bottom() )
1435             //In terms of fields, it's not possible to be closer any more!
1436             break;
1437 
1438         pContent = pContent->GetNextContentFrame();
1439         while ( pContent && !pContent->IsInDocBody() )
1440             pContent = pContent->GetNextContentFrame();
1441     }
1442 
1443     //Bring the point into the PrtArea.
1444     const SwRect aRect( pAct->getFrameArea().Pos() + pAct->getFramePrintArea().Pos(), pAct->getFramePrintArea().SSize() );
1445     if ( aAct.Y() < aRect.Top() )
1446         aAct.setY( aRect.Top() );
1447     else if ( aAct.Y() > aRect.Bottom() )
1448         aAct.setY( aRect.Bottom() );
1449     if ( aAct.X() < aRect.Left() )
1450         aAct.setX( aRect.Left() );
1451     else if ( aAct.X() > aRect.Right() )
1452         aAct.setX( aRect.Right() );
1453 
1454     if (!pAct->isFrameAreaDefinitionValid() ||
1455         (pAct->IsTextFrame() && !static_cast<SwTextFrame const*>(pAct)->HasPara()))
1456     {
1457         // ContentFrame not formatted -> always on node-beginning
1458         // tdf#100635 also if the SwTextFrame would require reformatting,
1459         // which is unwanted in case this is called from text formatting code
1460         rPos = static_cast<SwTextFrame const*>(pAct)->MapViewToModelPos(TextFrameIndex(0));
1461     }
1462     else
1463     {
1464         SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
1465         pAct->GetModelPositionForViewPoint( &rPos, aAct, &aTmpState );
1466     }
1467 }
1468 
1469 /** Search the nearest Content to the passed point.
1470  *
1471  * Only search inside the BodyText.
1472  * @note Only the nearest vertically one will be searched.
1473  * @note JP 11.10.2001: only in tables we try to find the right column - Bug 72294
1474  */
GetNextPrevContentPos(const Point & rPoint,bool bNext) const1475 Point SwRootFrame::GetNextPrevContentPos( const Point& rPoint, bool bNext ) const
1476 {
1477     vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
1478     // #123110# - disable creation of an action by a callback
1479     // event during processing of this method. Needed because formatting is
1480     // triggered by this method.
1481     DisableCallbackAction aDisableCallbackAction(const_cast<SwRootFrame&>(*this));
1482     //Search the first ContentFrame and his successor in the body area.
1483     //To be efficient (and not formatting too much) we'll start at the correct
1484     //page.
1485     const SwLayoutFrame *pPage = static_cast<const SwLayoutFrame*>(Lower());
1486     if( pPage )
1487         while( pPage->GetNext() && pPage->getFrameArea().Bottom() < rPoint.Y() )
1488             pPage = static_cast<const SwLayoutFrame*>(pPage->GetNext());
1489 
1490     const SwContentFrame *pCnt = pPage ? pPage->ContainsContent() : ContainsContent();
1491     while ( pCnt && !pCnt->IsInDocBody() )
1492         pCnt = pCnt->GetNextContentFrame();
1493 
1494     if ( !pCnt )
1495         return Point( 0, 0 );
1496 
1497     pCnt->Calc(pRenderContext);
1498     if( !bNext )
1499     {
1500         // As long as the point lies before the first ContentFrame and there are
1501         // still precedent pages I'll go to the next page.
1502         while ( rPoint.Y() < pCnt->getFrameArea().Top() && pPage->GetPrev() )
1503         {
1504             pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
1505             pCnt = pPage->ContainsContent();
1506             while ( !pCnt )
1507             {
1508                 pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
1509                 if ( pPage )
1510                     pCnt = pPage->ContainsContent();
1511                 else
1512                     return ContainsContent()->UnionFrame().Pos();
1513             }
1514             pCnt->Calc(pRenderContext);
1515         }
1516     }
1517 
1518     //Does the point lie above the first ContentFrame?
1519     if ( rPoint.Y() < pCnt->getFrameArea().Top() && !lcl_IsInRepeatedHeadline( pCnt ) )
1520         return pCnt->UnionFrame().Pos();
1521 
1522     Point aRet(0, 0);
1523     do
1524     {
1525         //Does the point lie in the current ContentFrame?
1526         SwRect aContentFrame( pCnt->UnionFrame() );
1527         if ( aContentFrame.IsInside( rPoint ) && !lcl_IsInRepeatedHeadline( pCnt ))
1528         {
1529             aRet = rPoint;
1530             break;
1531         }
1532 
1533         //Is the current one the last ContentFrame?
1534         //If the next ContentFrame lies behind the point, then the current on is the
1535         //one we searched.
1536         const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
1537         while ( pNxt && !pNxt->IsInDocBody() )
1538             pNxt = pNxt->GetNextContentFrame();
1539 
1540         //Does the point lie behind the last ContentFrame?
1541         if ( !pNxt )
1542         {
1543             aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
1544             break;
1545         }
1546 
1547         //If the next ContentFrame lies behind the point then it is the one we
1548         //searched.
1549         const SwTabFrame* pTFrame;
1550         pNxt->Calc(pRenderContext);
1551         if( pNxt->getFrameArea().Top() > rPoint.Y() &&
1552             !lcl_IsInRepeatedHeadline( pCnt, &pTFrame ) &&
1553             ( !pTFrame || pNxt->getFrameArea().Left() > rPoint.X() ))
1554         {
1555             if (bNext)
1556                 aRet = pNxt->getFrameArea().Pos();
1557             else
1558                 aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
1559             break;
1560         }
1561         pCnt = pNxt;
1562     }
1563     while (pCnt);
1564     return aRet;
1565 }
1566 
1567 /** Returns the absolute document position of the desired page.
1568  *
1569  * Formatting is done only as far as needed and only if bFormat=true.
1570  * Pos is set to the one of the last page, if the page number was chosen too big.
1571  *
1572  * @return Null, if the operation failed.
1573  */
GetPagePos(sal_uInt16 nPageNum) const1574 Point SwRootFrame::GetPagePos( sal_uInt16 nPageNum ) const
1575 {
1576     OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
1577 
1578     const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
1579     while ( true )
1580     {
1581         if ( pPage->GetPhyPageNum() >= nPageNum || !pPage->GetNext() )
1582             break;
1583         pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
1584     }
1585     return pPage->getFrameArea().Pos();
1586 }
1587 
1588 /** get page frame by physical page number
1589  *
1590  * @return pointer to the page frame with the given physical page number
1591  */
GetPageByPageNum(sal_uInt16 _nPageNum) const1592 SwPageFrame* SwRootFrame::GetPageByPageNum( sal_uInt16 _nPageNum ) const
1593 {
1594     const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>( Lower() );
1595     while ( pPageFrame && pPageFrame->GetPhyPageNum() < _nPageNum )
1596     {
1597           pPageFrame = static_cast<const SwPageFrame*>( pPageFrame->GetNext() );
1598     }
1599 
1600     if ( pPageFrame && pPageFrame->GetPhyPageNum() == _nPageNum )
1601     {
1602         return const_cast<SwPageFrame*>( pPageFrame );
1603     }
1604     else
1605     {
1606         return nullptr;
1607     }
1608 }
1609 
1610 /**
1611  * @return true, when the given physical pagenumber doesn't exist or this page is an empty page.
1612  */
IsDummyPage(sal_uInt16 nPageNum) const1613 bool SwRootFrame::IsDummyPage( sal_uInt16 nPageNum ) const
1614 {
1615     if( !Lower() || !nPageNum || nPageNum > GetPageNum() )
1616         return true;
1617 
1618     const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
1619     while( pPage && nPageNum < pPage->GetPhyPageNum() )
1620         pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
1621     return !pPage || pPage->IsEmptyPage();
1622 }
1623 
1624 /** Is the Frame or rather the Section in which it lies protected?
1625  *
1626  * Also Fly in Fly in ... and Footnotes
1627  */
IsProtected() const1628 bool SwFrame::IsProtected() const
1629 {
1630     if (IsTextFrame())
1631     {
1632         const SwDoc *pDoc = &static_cast<const SwTextFrame*>(this)->GetDoc();
1633         bool isFormProtected=pDoc->GetDocumentSettingManager().get(DocumentSettingId::PROTECT_FORM );
1634         if (isFormProtected)
1635         {
1636             return false; // TODO a hack for now, well deal with it later, I we return true here we have a "double" locking
1637         }
1638     }
1639     //The Frame can be protected in borders, cells or sections.
1640     //Also goes up FlyFrames recursive and from footnote to anchor.
1641     const SwFrame *pFrame = this;
1642     do
1643     {
1644         if (pFrame->IsTextFrame())
1645         {   // sw_redlinehide: redlines can't overlap section nodes, so any node will do
1646             if (static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()->IsInProtectSect())
1647             {
1648                 return true;
1649             }
1650         }
1651         else if ( pFrame->IsContentFrame() )
1652         {
1653             assert(pFrame->IsNoTextFrame());
1654             if (static_cast<const SwNoTextFrame*>(pFrame)->GetNode() &&
1655                 static_cast<const SwNoTextFrame*>(pFrame)->GetNode()->IsInProtectSect())
1656             {
1657                 return true;
1658             }
1659         }
1660         else
1661         {
1662             if ( static_cast<const SwLayoutFrame*>(pFrame)->GetFormat() &&
1663                  static_cast<const SwLayoutFrame*>(pFrame)->GetFormat()->
1664                  GetProtect().IsContentProtected() )
1665                 return true;
1666             if ( pFrame->IsCoveredCell() )
1667                 return true;
1668         }
1669         if ( pFrame->IsFlyFrame() )
1670         {
1671             //In a chain the protection of the content can be specified by the
1672             //master of the chain.
1673             if ( static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() )
1674             {
1675                 const SwFlyFrame *pMaster = static_cast<const SwFlyFrame*>(pFrame);
1676                 do
1677                 {   pMaster = pMaster->GetPrevLink();
1678                 } while ( pMaster->GetPrevLink() );
1679                 if ( pMaster->IsProtected() )
1680                     return true;
1681             }
1682             pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
1683         }
1684         else if ( pFrame->IsFootnoteFrame() )
1685             pFrame = static_cast<const SwFootnoteFrame*>(pFrame)->GetRef();
1686         else
1687             pFrame = pFrame->GetUpper();
1688 
1689     } while ( pFrame );
1690 
1691     return false;
1692 }
1693 
1694 /** @return the physical page number */
GetPhyPageNum() const1695 sal_uInt16 SwFrame::GetPhyPageNum() const
1696 {
1697     const SwPageFrame *pPage = FindPageFrame();
1698     return pPage ? pPage->GetPhyPageNum() : 0;
1699 }
1700 
1701 /** Decides if the page want to be a right page or not.
1702  *
1703  * If the first content of the page has a page descriptor, we take the follow
1704  * of the page descriptor of the last not empty page. If this descriptor allows
1705  * only right(left) pages and the page isn't an empty page then it wants to be
1706  * such right(left) page. If the descriptor allows right and left pages, we
1707  * look for a number offset in the first content. If there is one, odd number
1708  * results right pages (or left pages if document starts with even number),
1709  * even number results left pages (or right pages if document starts with even
1710  * number).
1711  * If there is no number offset, we take the physical page number instead,
1712  * but a previous empty page doesn't count.
1713  */
WannaRightPage() const1714 bool SwFrame::WannaRightPage() const
1715 {
1716     const SwPageFrame *pPage = FindPageFrame();
1717     if ( !pPage || !pPage->GetUpper() )
1718         return true;
1719 
1720     const SwFrame *pFlow = pPage->FindFirstBodyContent();
1721     const SwPageDesc *pDesc = nullptr;
1722     ::std::optional<sal_uInt16> oPgNum;
1723     if ( pFlow )
1724     {
1725         if ( pFlow->IsInTab() )
1726             pFlow = pFlow->FindTabFrame();
1727         const SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
1728         if ( !pTmp->IsFollow() )
1729         {
1730             const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
1731             pDesc = rPgDesc.GetPageDesc();
1732             oPgNum = rPgDesc.GetNumOffset();
1733         }
1734     }
1735     if ( !pDesc )
1736     {
1737         SwPageFrame *pPrv = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pPage->GetPrev()));
1738         if( pPrv && pPrv->IsEmptyPage() )
1739             pPrv = static_cast<SwPageFrame*>(pPrv->GetPrev());
1740         if( pPrv )
1741             pDesc = pPrv->GetPageDesc()->GetFollow();
1742         else
1743         {
1744             const SwDoc* pDoc = pPage->GetFormat()->GetDoc();
1745             pDesc = &pDoc->GetPageDesc( 0 );
1746         }
1747     }
1748     OSL_ENSURE( pDesc, "No pagedescriptor" );
1749     bool isRightPage;
1750     if( oPgNum )
1751         isRightPage = sw::IsRightPageByNumber(*mpRoot, *oPgNum);
1752     else
1753     {
1754         isRightPage = pPage->OnRightPage();
1755         if( pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
1756             isRightPage = !isRightPage;
1757     }
1758     if( !pPage->IsEmptyPage() )
1759     {
1760         if( !pDesc->GetRightFormat() )
1761             isRightPage = false;
1762         else if( !pDesc->GetLeftFormat() )
1763             isRightPage = true;
1764     }
1765     return isRightPage;
1766 }
1767 
OnFirstPage() const1768 bool SwFrame::OnFirstPage() const
1769 {
1770     bool bRet = false;
1771     const SwPageFrame *pPage = FindPageFrame();
1772 
1773     if (pPage)
1774     {
1775         const SwPageFrame* pPrevFrame = dynamic_cast<const SwPageFrame*>(pPage->GetPrev());
1776         if (pPrevFrame)
1777         {
1778             // first page of layout may be empty page, but only if it starts with "Left Page" style
1779             const SwPageDesc* pDesc = pPage->GetPageDesc();
1780             bRet = pPrevFrame->GetPageDesc() != pDesc;
1781         }
1782         else
1783             bRet = true;
1784     }
1785     return bRet;
1786 }
1787 
Calc(vcl::RenderContext * pRenderContext) const1788 void SwFrame::Calc(vcl::RenderContext* pRenderContext) const
1789 {
1790     if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
1791     {
1792         const_cast<SwFrame*>(this)->PrepareMake(pRenderContext);
1793     }
1794 }
1795 
GetRelPos() const1796 Point SwFrame::GetRelPos() const
1797 {
1798     Point aRet( getFrameArea().Pos() );
1799     // here we cast since SwLayoutFrame is declared only as forwarded
1800     aRet -= GetUpper()->getFramePrintArea().Pos();
1801     aRet -= GetUpper()->getFrameArea().Pos();
1802     return aRet;
1803 }
1804 
1805 /** @return the virtual page number with the offset. */
GetVirtPageNum() const1806 sal_uInt16 SwFrame::GetVirtPageNum() const
1807 {
1808     const SwPageFrame *pPage = FindPageFrame();
1809     if ( !pPage || !pPage->GetUpper() )
1810         return 0;
1811 
1812     sal_uInt16 nPhyPage = pPage->GetPhyPageNum();
1813     if ( !static_cast<const SwRootFrame*>(pPage->GetUpper())->IsVirtPageNum() )
1814         return nPhyPage;
1815 
1816     //Search the nearest section using the virtual page number.
1817     //Because searching backwards needs a lot of time we search specific using
1818     //the dependencies. From the PageDescs we get the attributes and from the
1819     //attributes we get the sections.
1820     const SwPageFrame *pVirtPage = nullptr;
1821     const SwFrame *pFrame = nullptr;
1822     const SfxItemPool &rPool = pPage->GetFormat()->GetDoc()->GetAttrPool();
1823     for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_PAGEDESC))
1824     {
1825         const SwFormatPageDesc *pDesc = dynamic_cast<const SwFormatPageDesc*>(pItem);
1826         if ( !pDesc )
1827             continue;
1828 
1829         if ( pDesc->GetNumOffset() && pDesc->GetDefinedIn() )
1830         {
1831             const sw::BroadcastingModify *pMod = pDesc->GetDefinedIn();
1832             SwVirtPageNumInfo aInfo( pPage );
1833             pMod->GetInfo( aInfo );
1834             if ( aInfo.GetPage() )
1835             {
1836                 if( !pVirtPage || aInfo.GetPage()->GetPhyPageNum() > pVirtPage->GetPhyPageNum() )
1837                 {
1838                     pVirtPage = aInfo.GetPage();
1839                     pFrame = aInfo.GetFrame();
1840                 }
1841             }
1842         }
1843     }
1844     if ( pFrame )
1845     {
1846         ::std::optional<sal_uInt16> oNumOffset = pFrame->GetPageDescItem().GetNumOffset();
1847         if (oNumOffset)
1848         {
1849             return nPhyPage - pFrame->GetPhyPageNum() + *oNumOffset;
1850         }
1851         else
1852         {
1853             return nPhyPage - pFrame->GetPhyPageNum();
1854         }
1855     }
1856     return nPhyPage;
1857 }
1858 
1859 /** Determines and sets those cells which are enclosed by the selection. */
MakeTableCursors(SwTableCursor & rTableCursor)1860 bool SwRootFrame::MakeTableCursors( SwTableCursor& rTableCursor )
1861 {
1862     //Find Union-Rects and tables (Follows) of the selection.
1863     OSL_ENSURE( rTableCursor.GetContentNode() && rTableCursor.GetContentNode( false ),
1864             "Tabselection not on Cnt." );
1865 
1866     bool bRet = false;
1867 
1868     // For new table models there's no need to ask the layout...
1869     if( rTableCursor.NewTableSelection() )
1870         return true;
1871 
1872     Point aPtPt, aMkPt;
1873     {
1874         SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rTableCursor);
1875 
1876         if( pShCursor )
1877         {
1878             aPtPt = pShCursor->GetPtPos();
1879             aMkPt = pShCursor->GetMkPos();
1880         }
1881     }
1882 
1883     // #151012# Made code robust here
1884     const SwContentNode* pTmpStartNode = rTableCursor.GetContentNode();
1885     const SwContentNode* pTmpEndNode   = rTableCursor.GetContentNode(false);
1886 
1887     std::pair<Point, bool> tmp(aPtPt, false);
1888     const SwFrame *const pTmpStartFrame = pTmpStartNode ? pTmpStartNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
1889     tmp.first = aMkPt;
1890     const SwFrame *const pTmpEndFrame = pTmpEndNode ? pTmpEndNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
1891 
1892     const SwLayoutFrame* pStart = pTmpStartFrame ? pTmpStartFrame->GetUpper() : nullptr;
1893     const SwLayoutFrame* pEnd   = pTmpEndFrame   ? pTmpEndFrame->GetUpper() : nullptr;
1894 
1895     OSL_ENSURE( pStart && pEnd, "MakeTableCursors: Good to have the code robust here!" );
1896 
1897     /* #109590# Only change table boxes if the frames are
1898         valid. Needed because otherwise the table cursor after moving
1899         table cells by dnd resulted in an empty tables cursor.  */
1900     if ( pStart && pEnd && pStart->isFrameAreaDefinitionValid() && pEnd->isFrameAreaDefinitionValid())
1901     {
1902         SwSelUnions aUnions;
1903         ::MakeSelUnions( aUnions, pStart, pEnd );
1904 
1905         SwSelBoxes aNew;
1906 
1907         const bool bReadOnlyAvailable = rTableCursor.IsReadOnlyAvailable();
1908 
1909         for (SwSelUnion & rUnion : aUnions)
1910         {
1911             const SwTabFrame *pTable = rUnion.GetTable();
1912 
1913             // Skip any repeated headlines in the follow:
1914             SwLayoutFrame* pRow = pTable->IsFollow() ?
1915                                 pTable->GetFirstNonHeadlineRow() :
1916                                 const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pTable->Lower()));
1917 
1918             while ( pRow )
1919             {
1920                 if ( pRow->getFrameArea().IsOver( rUnion.GetUnion() ) )
1921                 {
1922                     const SwLayoutFrame *pCell = pRow->FirstCell();
1923 
1924                     while ( pCell && pRow->IsAnLower( pCell ) )
1925                     {
1926                         OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
1927                         if( IsFrameInTableSel( rUnion.GetUnion(), pCell ) &&
1928                             (bReadOnlyAvailable ||
1929                              !pCell->GetFormat()->GetProtect().IsContentProtected()))
1930                         {
1931                             SwTableBox* pInsBox = const_cast<SwTableBox*>(
1932                                 static_cast<const SwCellFrame*>(pCell)->GetTabBox());
1933                             aNew.insert( pInsBox );
1934                         }
1935                         if ( pCell->GetNext() )
1936                         {
1937                             pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
1938                             if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
1939                                 pCell = pCell->FirstCell();
1940                         }
1941                         else
1942                         {
1943                             const SwLayoutFrame* pLastCell = pCell;
1944                             do
1945                             {
1946                                 pCell = pCell->GetNextLayoutLeaf();
1947                             } while ( pCell && pLastCell->IsAnLower( pCell ) );
1948                             // For sections with columns
1949                             if( pCell && pCell->IsInTab() )
1950                             {
1951                                 while( !pCell->IsCellFrame() )
1952                                 {
1953                                     pCell = pCell->GetUpper();
1954                                     OSL_ENSURE( pCell, "Where's my cell?" );
1955                                 }
1956                             }
1957                         }
1958                     }
1959                 }
1960                 pRow = static_cast<SwLayoutFrame*>(pRow->GetNext());
1961             }
1962         }
1963 
1964         rTableCursor.ActualizeSelection( aNew );
1965         bRet = true;
1966     }
1967 
1968     return bRet;
1969 }
1970 
Sub(SwRegionRects & rRegion,const SwRect & rRect)1971 static void Sub( SwRegionRects& rRegion, const SwRect& rRect )
1972 {
1973     if( rRect.Width() > 1 && rRect.Height() > 1 &&
1974         rRect.IsOver( rRegion.GetOrigin() ))
1975         rRegion -= rRect;
1976 }
1977 
Add(SwRegionRects & rRegion,const SwRect & rRect)1978 static void Add( SwRegionRects& rRegion, const SwRect& rRect )
1979 {
1980     if( rRect.Width() > 1 && rRect.Height() > 1 )
1981         rRegion += rRect;
1982 }
1983 
1984 /*
1985  * The following situations can happen:
1986  *  1. Start and end lie in one screen-row and in the same node
1987  *     -> one rectangle out of start and end; and we're okay
1988  *  2. Start and end lie in one frame (therefore in the same node!)
1989  *     -> expand start to the right, end to the left and if more than two
1990  *        screen-rows are involved - calculate the in-between
1991  *  3. Start and end lie in different frames
1992  *     -> expand start to the right until frame-end, calculate Rect
1993  *        expand end to the left until frame-start, calculate Rect
1994  *        and if more than two frames are involved add the PrtArea of all
1995  *        frames which lie in between
1996  *
1997  * Big reorganization because of the FlyFrame - those need to be locked out.
1998  * Exceptions:  - The Fly in which the selection took place (if it took place
1999  *                 in a Fly)
2000  *              - The Flys which are underrun by the text
2001  *              - The Flys which are anchored to somewhere inside the selection.
2002  * Functioning: First a SwRegion with a root gets initialized.
2003  *              Out of the region the inverted sections are cut out. The
2004  *              section gets compressed and finally inverted and thereby the
2005  *              inverted rectangles are available.
2006  *              In the end the Flys are cut out of the section.
2007  */
CalcFrameRects(SwShellCursor & rCursor)2008 void SwRootFrame::CalcFrameRects(SwShellCursor &rCursor)
2009 {
2010     SwPosition *pStartPos = rCursor.Start(),
2011                *pEndPos   = rCursor.GetPoint() == pStartPos ? rCursor.GetMark() : rCursor.GetPoint();
2012 
2013     SwViewShell *pSh = GetCurrShell();
2014 
2015     bool bIgnoreVisArea = true;
2016     if (pSh)
2017         bIgnoreVisArea = pSh->GetViewOptions()->IsPDFExport() || comphelper::LibreOfficeKit::isActive();
2018 
2019     // #i12836# enhanced pdf
2020     SwRegionRects aRegion( !bIgnoreVisArea ?
2021                            pSh->VisArea() :
2022                            getFrameArea() );
2023     if( !pStartPos->nNode.GetNode().IsContentNode() ||
2024         !pStartPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ||
2025         ( pStartPos->nNode != pEndPos->nNode &&
2026           ( !pEndPos->nNode.GetNode().IsContentNode() ||
2027             !pEndPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ) ) )
2028     {
2029         return;
2030     }
2031 
2032     DisableCallbackAction a(*this); // the GetCharRect below may format
2033 
2034     //First obtain the ContentFrames for the start and the end - those are needed
2035     //anyway.
2036     std::pair<Point, bool> tmp(rCursor.GetSttPos(), true);
2037     SwContentFrame* pStartFrame = pStartPos->nNode.GetNode().
2038         GetContentNode()->getLayoutFrame(this, pStartPos, &tmp);
2039 
2040     tmp.first = rCursor.GetEndPos();
2041     SwContentFrame* pEndFrame   = pEndPos->nNode.GetNode().
2042         GetContentNode()->getLayoutFrame(this, pEndPos, &tmp);
2043 
2044     assert(pStartFrame && pEndFrame && "No ContentFrames found.");
2045     //tdf#119224 start and end are expected to exist for the scope of this function
2046     SwFrameDeleteGuard aStartFrameGuard(pStartFrame), aEndFrameGuard(pEndFrame);
2047 
2048     //Do not subtract the FlyFrames in which selected Frames lie.
2049     SwSortedObjs aSortObjs;
2050     if ( pStartFrame->IsInFly() )
2051     {
2052         const SwAnchoredObject* pObj = pStartFrame->FindFlyFrame();
2053         OSL_ENSURE( pObj, "No Start Object." );
2054         if (pObj) aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
2055         const SwAnchoredObject* pObj2 = pEndFrame->FindFlyFrame();
2056         OSL_ENSURE( pObj2, "SwRootFrame::CalcFrameRects(..) - FlyFrame missing - looks like an invalid selection" );
2057         if ( pObj2 != nullptr && pObj2 != pObj )
2058         {
2059             aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj2) );
2060         }
2061     }
2062 
2063     // if a selection which is not allowed exists, we correct what is not
2064     // allowed (header/footer/table-headline) for two pages.
2065     do {    // middle check loop
2066         const SwLayoutFrame* pSttLFrame = pStartFrame->GetUpper();
2067         const SwFrameType cHdFtTableHd = SwFrameType::Header | SwFrameType::Footer | SwFrameType::Tab;
2068         while( pSttLFrame &&
2069             ! (cHdFtTableHd & pSttLFrame->GetType() ))
2070             pSttLFrame = pSttLFrame->GetUpper();
2071         if( !pSttLFrame )
2072             break;
2073         const SwLayoutFrame* pEndLFrame = pEndFrame->GetUpper();
2074         while( pEndLFrame &&
2075             ! (cHdFtTableHd & pEndLFrame->GetType() ))
2076             pEndLFrame = pEndLFrame->GetUpper();
2077         if( !pEndLFrame )
2078             break;
2079 
2080         OSL_ENSURE( pEndLFrame->GetType() == pSttLFrame->GetType(),
2081             "Selection over different content" );
2082         switch( pSttLFrame->GetType() )
2083         {
2084         case SwFrameType::Header:
2085         case SwFrameType::Footer:
2086             // On different pages? Then always on the start-page
2087             if( pEndLFrame->FindPageFrame() != pSttLFrame->FindPageFrame() )
2088             {
2089                 // Set end- to the start-ContentFrame
2090                 if( pStartPos == rCursor.GetPoint() )
2091                     pEndFrame = pStartFrame;
2092                 else
2093                     pStartFrame = pEndFrame;
2094             }
2095             break;
2096         case SwFrameType::Tab:
2097             // On different pages? Then check for table-headline
2098             {
2099                 const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pSttLFrame);
2100                 if( ( pTabFrame->GetFollow() ||
2101                     static_cast<const SwTabFrame*>(pEndLFrame)->GetFollow() ) &&
2102                     pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
2103                     pTabFrame->GetLower() != static_cast<const SwTabFrame*>(pEndLFrame)->GetLower() &&
2104                     ( lcl_IsInRepeatedHeadline( pStartFrame ) ||
2105                     lcl_IsInRepeatedHeadline( pEndFrame ) ) )
2106                 {
2107                     // Set end- to the start-ContentFrame
2108                     if( pStartPos == rCursor.GetPoint() )
2109                         pEndFrame = pStartFrame;
2110                     else
2111                         pStartFrame = pEndFrame;
2112                 }
2113             }
2114             break;
2115         default: break;
2116         }
2117     } while( false );
2118 
2119     SwCursorMoveState aTmpState( CursorMoveState::NONE );
2120     aTmpState.m_b2Lines = true;
2121     aTmpState.m_bNoScroll = true;
2122     aTmpState.m_nCursorBidiLevel = pStartFrame->IsRightToLeft() ? 1 : 0;
2123 
2124     //ContentRects to Start- and EndFrames.
2125     SwRect aStRect, aEndRect;
2126     pStartFrame->GetCharRect( aStRect, *pStartPos, &aTmpState );
2127     std::optional<Sw2LinesPos> pSt2Pos = std::move(aTmpState.m_x2Lines);
2128     aTmpState.m_nCursorBidiLevel = pEndFrame->IsRightToLeft() ? 1 : 0;
2129 
2130     pEndFrame->GetCharRect( aEndRect, *pEndPos, &aTmpState );
2131     std::optional<Sw2LinesPos> pEnd2Pos = std::move(aTmpState.m_x2Lines);
2132 
2133     SwRect aStFrame ( pStartFrame->UnionFrame( true ) );
2134     aStFrame.Intersection( pStartFrame->GetPaintArea() );
2135     SwRect aEndFrame( pStartFrame == pEndFrame ? aStFrame : pEndFrame->UnionFrame( true ) );
2136     if( pStartFrame != pEndFrame )
2137     {
2138         aEndFrame.Intersection( pEndFrame->GetPaintArea() );
2139     }
2140     SwRectFnSet aRectFnSet(pStartFrame);
2141     const bool bR2L = pStartFrame->IsRightToLeft();
2142     const bool bEndR2L = pEndFrame->IsRightToLeft();
2143     const bool bB2T = pStartFrame->IsVertLRBT();
2144 
2145     // If there's no doubleline portion involved or start and end are both
2146     // in the same doubleline portion, all works fine, but otherwise
2147     // we need the following...
2148     if( (!pSt2Pos && !pEnd2Pos) && ( !pSt2Pos || !pEnd2Pos ||
2149         pSt2Pos->aPortion != pEnd2Pos->aPortion ) )
2150     {
2151         // If we have a start(end) position inside a doubleline portion
2152         // the surrounded part of the doubleline portion is subtracted
2153         // from the region and the aStRect(aEndRect) is set to the
2154         // end(start) of the doubleline portion.
2155         if( pSt2Pos )
2156         {
2157             SwRect aTmp( aStRect );
2158 
2159             // BiDi-Portions are swimming against the current.
2160             const bool bPorR2L = ( MultiPortionType::BIDI == pSt2Pos->nMultiType ) ?
2161                 ! bR2L :
2162             bR2L;
2163 
2164             if( MultiPortionType::BIDI == pSt2Pos->nMultiType &&
2165                 aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
2166             {
2167                 // nested bidi portion
2168                 tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
2169                 nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
2170                 tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
2171 
2172                 aRectFnSet.SetRight( aTmp, nRightAbs );
2173 
2174                 if ( ! pEnd2Pos || pEnd2Pos->aPortion != pSt2Pos->aPortion )
2175                 {
2176                     SwRect aTmp2( pSt2Pos->aPortion );
2177                     aRectFnSet.SetRight( aTmp2, nLeftAbs );
2178                     aTmp2.Intersection( aEndFrame );
2179                     Sub( aRegion, aTmp2 );
2180                 }
2181             }
2182             else
2183             {
2184                 if( bPorR2L )
2185                     aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
2186                 else
2187                     aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
2188             }
2189 
2190             if( MultiPortionType::ROT_90 == pSt2Pos->nMultiType ||
2191                 aRectFnSet.GetTop(pSt2Pos->aPortion) ==
2192                 aRectFnSet.GetTop(aTmp) )
2193             {
2194                 aRectFnSet.SetTop( aTmp, aRectFnSet.GetTop(pSt2Pos->aLine) );
2195             }
2196 
2197             aTmp.Intersection( aStFrame );
2198             Sub( aRegion, aTmp );
2199 
2200             SwTwips nTmp = aRectFnSet.GetBottom(pSt2Pos->aLine);
2201             if( MultiPortionType::ROT_90 != pSt2Pos->nMultiType &&
2202                 aRectFnSet.BottomDist( aStRect, nTmp ) > 0 )
2203             {
2204                 aRectFnSet.SetTop( aTmp, aRectFnSet.GetBottom(aTmp) );
2205                 aRectFnSet.SetBottom( aTmp, nTmp );
2206                 if( aRectFnSet.BottomDist( aStRect, aRectFnSet.GetBottom(pSt2Pos->aPortion) ) > 0 )
2207                 {
2208                     if( bPorR2L )
2209                         aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
2210                     else
2211                         aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
2212                 }
2213                 aTmp.Intersection( aStFrame );
2214                 Sub( aRegion, aTmp );
2215             }
2216 
2217             aStRect = pSt2Pos->aLine;
2218             aRectFnSet.SetLeft( aStRect, bR2L ?
2219                     aRectFnSet.GetLeft(pSt2Pos->aPortion) :
2220                     aRectFnSet.GetRight(pSt2Pos->aPortion) );
2221             aRectFnSet.SetWidth( aStRect, 1 );
2222         }
2223 
2224         if( pEnd2Pos )
2225         {
2226             SwRectFnSet fnRectX(pEndFrame);
2227             SwRect aTmp( aEndRect );
2228 
2229             // BiDi-Portions are swimming against the current.
2230             const bool bPorR2L = ( MultiPortionType::BIDI == pEnd2Pos->nMultiType ) ?
2231                                        ! bEndR2L :
2232                                          bEndR2L;
2233 
2234             if( MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
2235                 fnRectX.GetWidth(pEnd2Pos->aPortion2) )
2236             {
2237                 // nested bidi portion
2238                 tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
2239                 nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2);
2240                 tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
2241 
2242                 fnRectX.SetLeft( aTmp, nLeftAbs );
2243 
2244                 if ( ! pSt2Pos || pSt2Pos->aPortion != pEnd2Pos->aPortion )
2245                 {
2246                     SwRect aTmp2( pEnd2Pos->aPortion );
2247                     fnRectX.SetLeft( aTmp2, nRightAbs );
2248                     aTmp2.Intersection( aEndFrame );
2249                     Sub( aRegion, aTmp2 );
2250                 }
2251             }
2252             else
2253             {
2254                 if ( bPorR2L )
2255                     fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
2256                 else
2257                     fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
2258             }
2259 
2260             if( MultiPortionType::ROT_90 == pEnd2Pos->nMultiType ||
2261                 fnRectX.GetBottom(pEnd2Pos->aPortion) ==
2262                 fnRectX.GetBottom(aEndRect) )
2263             {
2264                 fnRectX.SetBottom( aTmp, fnRectX.GetBottom(pEnd2Pos->aLine) );
2265             }
2266 
2267             aTmp.Intersection( aEndFrame );
2268             Sub( aRegion, aTmp );
2269 
2270             // The next statement means neither ruby nor rotate(90):
2271             if( MultiPortionType::RUBY != pEnd2Pos->nMultiType && MultiPortionType::ROT_90 != pEnd2Pos->nMultiType )
2272             {
2273                 SwTwips nTmp = fnRectX.GetTop(pEnd2Pos->aLine);
2274                 if( fnRectX.GetTop(aEndRect) != nTmp )
2275                 {
2276                     fnRectX.SetBottom( aTmp, fnRectX.GetTop(aTmp) );
2277                     fnRectX.SetTop( aTmp, nTmp );
2278                     if( fnRectX.GetTop(aEndRect) !=
2279                         fnRectX.GetTop(pEnd2Pos->aPortion) )
2280                     {
2281                         if( bPorR2L )
2282                             fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
2283                         else
2284                             fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
2285                     }
2286                     aTmp.Intersection( aEndFrame );
2287                     Sub( aRegion, aTmp );
2288                 }
2289             }
2290 
2291             aEndRect = pEnd2Pos->aLine;
2292             fnRectX.SetLeft( aEndRect, bEndR2L ?
2293                     fnRectX.GetRight(pEnd2Pos->aPortion) :
2294                     fnRectX.GetLeft(pEnd2Pos->aPortion) );
2295             fnRectX.SetWidth( aEndRect, 1 );
2296         }
2297     }
2298     else if( pSt2Pos && pEnd2Pos &&
2299              MultiPortionType::BIDI == pSt2Pos->nMultiType &&
2300              MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
2301              pSt2Pos->aPortion == pEnd2Pos->aPortion &&
2302              pSt2Pos->aPortion2 != pEnd2Pos->aPortion2 )
2303     {
2304         // This is the ugly special case, where the selection starts and
2305         // ends in the same bidi portion but one start or end is inside a
2306         // nested bidi portion.
2307 
2308         if ( aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
2309         {
2310             SwRect aTmp( aStRect );
2311             tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
2312             nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
2313             tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
2314 
2315             aRectFnSet.SetRight( aTmp, nRightAbs );
2316             aTmp.Intersection( aStFrame );
2317             Sub( aRegion, aTmp );
2318 
2319             aStRect = pSt2Pos->aLine;
2320             aRectFnSet.SetLeft( aStRect, bR2L ? nRightAbs : nLeftAbs );
2321             aRectFnSet.SetWidth( aStRect, 1 );
2322         }
2323 
2324         SwRectFnSet fnRectX(pEndFrame);
2325         if ( fnRectX.GetWidth(pEnd2Pos->aPortion2) )
2326         {
2327             SwRect aTmp( aEndRect );
2328             tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
2329             nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2);
2330             tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
2331 
2332             fnRectX.SetLeft( aTmp, nLeftAbs );
2333             aTmp.Intersection( aEndFrame );
2334             Sub( aRegion, aTmp );
2335 
2336             aEndRect = pEnd2Pos->aLine;
2337             fnRectX.SetLeft( aEndRect, bEndR2L ? nLeftAbs : nRightAbs );
2338             fnRectX.SetWidth( aEndRect, 1 );
2339         }
2340     }
2341 
2342     // The charrect may be outside the paintarea (for cursortravelling)
2343     // but the selection has to be restricted to the paintarea
2344     if( aStRect.Left() < aStFrame.Left() )
2345         aStRect.Left( aStFrame.Left() );
2346     else if( aStRect.Left() > aStFrame.Right() )
2347         aStRect.Left( aStFrame.Right() );
2348     SwTwips nTmp = aStRect.Right();
2349     if( nTmp < aStFrame.Left() )
2350         aStRect.Right( aStFrame.Left() );
2351     else if( nTmp > aStFrame.Right() )
2352         aStRect.Right( aStFrame.Right() );
2353     if( aEndRect.Left() < aEndFrame.Left() )
2354         aEndRect.Left( aEndFrame.Left() );
2355     else if( aEndRect.Left() > aEndFrame.Right() )
2356         aEndRect.Left( aEndFrame.Right() );
2357     nTmp = aEndRect.Right();
2358     if( nTmp < aEndFrame.Left() )
2359         aEndRect.Right( aEndFrame.Left() );
2360     else if( nTmp > aEndFrame.Right() )
2361         aEndRect.Right( aEndFrame.Right() );
2362 
2363     if( pStartFrame == pEndFrame )
2364     {
2365         bool bSameRotatedOrBidi = pSt2Pos && pEnd2Pos &&
2366             ( MultiPortionType::BIDI == pSt2Pos->nMultiType  ||
2367               MultiPortionType::ROT_270 == pSt2Pos->nMultiType ||
2368               MultiPortionType::ROT_90  == pSt2Pos->nMultiType ) &&
2369             pSt2Pos->aPortion == pEnd2Pos->aPortion;
2370         //case 1: (Same frame and same row)
2371         if( bSameRotatedOrBidi ||
2372             aRectFnSet.GetTop(aStRect) == aRectFnSet.GetTop(aEndRect) )
2373         {
2374             Point aTmpSt( aStRect.Pos() );
2375             Point aTmpEnd( aEndRect.Right(), aEndRect.Bottom() );
2376             if (bSameRotatedOrBidi || bR2L || bB2T)
2377             {
2378                 if( aTmpSt.Y() > aTmpEnd.Y() )
2379                 {
2380                     tools::Long nTmpY = aTmpEnd.Y();
2381                     aTmpEnd.setY( aTmpSt.Y() );
2382                     aTmpSt.setY( nTmpY );
2383                 }
2384                 if( aTmpSt.X() > aTmpEnd.X() )
2385                 {
2386                     tools::Long nTmpX = aTmpEnd.X();
2387                     aTmpEnd.setX( aTmpSt.X() );
2388                     aTmpSt.setX( nTmpX );
2389                 }
2390             }
2391 
2392             SwRect aTmp( aTmpSt, aTmpEnd );
2393             // Bug 34888: If content is selected which doesn't take space
2394             //            away (i.e. PostIts, RefMarks, TOXMarks), then at
2395             //            least set the width of the Cursor.
2396             if( 1 == aRectFnSet.GetWidth(aTmp) &&
2397                 pStartPos->nContent.GetIndex() !=
2398                 pEndPos->nContent.GetIndex() )
2399             {
2400                 OutputDevice* pOut = pSh->GetOut();
2401                 tools::Long nCursorWidth = pOut->GetSettings().GetStyleSettings().
2402                     GetCursorSize();
2403                 aRectFnSet.SetWidth( aTmp, pOut->PixelToLogic(
2404                     Size( nCursorWidth, 0 ) ).Width() );
2405             }
2406             aTmp.Intersection( aStFrame );
2407             Sub( aRegion, aTmp );
2408         }
2409         //case 2: (Same frame, but not the same line)
2410         else
2411         {
2412             SwTwips lLeft, lRight;
2413             if( pSt2Pos && pEnd2Pos && pSt2Pos->aPortion == pEnd2Pos->aPortion )
2414             {
2415                 lLeft = aRectFnSet.GetLeft(pSt2Pos->aPortion);
2416                 lRight = aRectFnSet.GetRight(pSt2Pos->aPortion);
2417             }
2418             else
2419             {
2420                 lLeft = aRectFnSet.GetLeft(pStartFrame->getFrameArea()) +
2421                     aRectFnSet.GetLeft(pStartFrame->getFramePrintArea());
2422                 lRight = aRectFnSet.GetRight(aEndFrame);
2423             }
2424             if( lLeft < aRectFnSet.GetLeft(aStFrame) )
2425                 lLeft = aRectFnSet.GetLeft(aStFrame);
2426             if( lRight > aRectFnSet.GetRight(aStFrame) )
2427                 lRight = aRectFnSet.GetRight(aStFrame);
2428             SwRect aSubRect( aStRect );
2429             //First line
2430             if( bR2L )
2431                 aRectFnSet.SetLeft( aSubRect, lLeft );
2432             else
2433                 aRectFnSet.SetRight( aSubRect, lRight );
2434             Sub( aRegion, aSubRect );
2435 
2436             //If there's at least a twips between start- and endline,
2437             //so the whole area between will be added.
2438             SwTwips aTmpBottom = aRectFnSet.GetBottom(aStRect);
2439             SwTwips aTmpTop = aRectFnSet.GetTop(aEndRect);
2440             if( aTmpBottom != aTmpTop )
2441             {
2442                 aRectFnSet.SetLeft( aSubRect, lLeft );
2443                 aRectFnSet.SetRight( aSubRect, lRight );
2444                 aRectFnSet.SetTop( aSubRect, aTmpBottom );
2445                 aRectFnSet.SetBottom( aSubRect, aTmpTop );
2446                 Sub( aRegion, aSubRect );
2447             }
2448             //and the last line
2449             aSubRect = aEndRect;
2450             if( bR2L )
2451                 aRectFnSet.SetRight( aSubRect, lRight );
2452             else
2453                 aRectFnSet.SetLeft( aSubRect, lLeft );
2454             Sub( aRegion, aSubRect );
2455         }
2456     }
2457     //case 3: (Different frames, maybe with other frames between)
2458     else
2459     {
2460         //The startframe first...
2461         SwRect aSubRect( aStRect );
2462         if( bR2L )
2463             aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aStFrame));
2464         else
2465             aRectFnSet.SetRight( aSubRect, aRectFnSet.GetRight(aStFrame));
2466         Sub( aRegion, aSubRect );
2467         SwTwips nTmpTwips = aRectFnSet.GetBottom(aStRect);
2468         if( aRectFnSet.GetBottom(aStFrame) != nTmpTwips )
2469         {
2470             aSubRect = aStFrame;
2471             aRectFnSet.SetTop( aSubRect, nTmpTwips );
2472             Sub( aRegion, aSubRect );
2473         }
2474 
2475         //Now the frames between, if there are any
2476         bool const bBody = pStartFrame->IsInDocBody();
2477         const SwTableBox* pCellBox = pStartFrame->GetUpper()->IsCellFrame() ?
2478             static_cast<const SwCellFrame*>(pStartFrame->GetUpper())->GetTabBox() : nullptr;
2479         if (pSh->IsSelectAll())
2480             pCellBox = nullptr;
2481 
2482         const SwContentFrame *pContent = pStartFrame->GetNextContentFrame();
2483         SwRect aPrvRect;
2484 
2485         OSL_ENSURE( pContent,
2486             "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect" );
2487         while ( pContent && pContent != pEndFrame )
2488         {
2489             if ( pContent->IsInFly() )
2490             {
2491                 const SwAnchoredObject* pObj = pContent->FindFlyFrame();
2492                 if (!aSortObjs.Contains(*pObj))
2493                 {   // is this even possible, assuming valid cursor pos.?
2494                     aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
2495                 }
2496             }
2497 
2498             // Consider only frames which have the same IsInDocBody value like pStartFrame
2499             // If pStartFrame is inside a SwCellFrame, consider only frames which are inside the
2500             // same cell frame (or its follow cell)
2501             const SwTableBox* pTmpCellBox = pContent->GetUpper()->IsCellFrame() ?
2502                 static_cast<const SwCellFrame*>(pContent->GetUpper())->GetTabBox() : nullptr;
2503             if (pSh->IsSelectAll())
2504                 pTmpCellBox = nullptr;
2505             if ( bBody == pContent->IsInDocBody() &&
2506                 ( !pCellBox || pCellBox == pTmpCellBox ) )
2507             {
2508                 SwRect aCRect( pContent->UnionFrame( true ) );
2509                 aCRect.Intersection( pContent->GetPaintArea() );
2510                 if( aCRect.IsOver( aRegion.GetOrigin() ))
2511                 {
2512                     SwRect aTmp( aPrvRect );
2513                     aTmp.Union( aCRect );
2514                     if ( (aPrvRect.Height() * aPrvRect.Width() +
2515                         aCRect.Height()   * aCRect.Width()) ==
2516                         (aTmp.Height() * aTmp.Width()) )
2517                     {
2518                         aPrvRect.Union( aCRect );
2519                     }
2520                     else
2521                     {
2522                         if ( aPrvRect.HasArea() )
2523                             Sub( aRegion, aPrvRect );
2524                         aPrvRect = aCRect;
2525                     }
2526                 }
2527             }
2528             pContent = pContent->GetNextContentFrame();
2529             OSL_ENSURE( pContent,
2530                 "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect!" );
2531         }
2532         if ( aPrvRect.HasArea() )
2533             Sub( aRegion, aPrvRect );
2534 
2535         //At least the endframe...
2536         aRectFnSet.Refresh(pEndFrame);
2537         nTmpTwips = aRectFnSet.GetTop(aEndRect);
2538         if( aRectFnSet.GetTop(aEndFrame) != nTmpTwips )
2539         {
2540             aSubRect = aEndFrame;
2541             aRectFnSet.SetBottom( aSubRect, nTmpTwips );
2542             Sub( aRegion, aSubRect );
2543         }
2544         aSubRect = aEndRect;
2545         if( bEndR2L )
2546             aRectFnSet.SetRight(aSubRect, aRectFnSet.GetRight(aEndFrame));
2547         else
2548             aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aEndFrame) );
2549         Sub( aRegion, aSubRect );
2550     }
2551 
2552     aRegion.Invert();
2553     pSt2Pos.reset();
2554     pEnd2Pos.reset();
2555 
2556     // Cut out Flys during loop. We don't cut out Flys when:
2557     // - the Lower is StartFrame/EndFrame (FlyInCnt and all other Flys which again
2558     //   sit in it)
2559     // - if in the Z-order we have Flys above those in which the StartFrame is
2560     //   placed
2561     // - if they are anchored to inside the selection and thus part of it
2562     const SwPageFrame *pPage      = pStartFrame->FindPageFrame();
2563     const SwPageFrame *pEndPage   = pEndFrame->FindPageFrame();
2564 
2565     while ( pPage )
2566     {
2567         if ( pPage->GetSortedObjs() )
2568         {
2569             const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
2570             for (SwAnchoredObject* pAnchoredObj : rObjs)
2571             {
2572                 const SwFlyFrame* pFly = dynamic_cast<const SwFlyFrame*>(pAnchoredObj);
2573                 if ( !pFly )
2574                     continue;
2575                 const SwVirtFlyDrawObj* pObj = pFly->GetVirtDrawObj();
2576                 const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
2577                 SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat().GetAnchor());
2578                 const SwPosition* anchoredAt = rAnchor.GetContentAnchor();
2579                 bool inSelection = (
2580                             anchoredAt != nullptr
2581                         && (   (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
2582                                 && IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
2583                             || (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
2584                                 && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
2585                 if( inSelection )
2586                         Add( aRegion, pFly->getFrameArea() );
2587                 else if ( !pFly->IsAnLower( pStartFrame ) &&
2588                     (rSur.GetSurround() != css::text::WrapTextMode_THROUGH &&
2589                     !rSur.IsContour()) )
2590                 {
2591                     if ( aSortObjs.Contains( *pAnchoredObj ) )
2592                         continue;
2593 
2594                     bool bSub = true;
2595                     const sal_uInt32 nPos = pObj->GetOrdNum();
2596                     for ( size_t k = 0; bSub && k < aSortObjs.size(); ++k )
2597                     {
2598                         OSL_ENSURE( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) !=  nullptr,
2599                             "<SwRootFrame::CalcFrameRects(..)> - object in <aSortObjs> of unexpected type" );
2600                         const SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(aSortObjs[k]);
2601                         do
2602                         {
2603                             if ( nPos < pTmp->GetVirtDrawObj()->GetOrdNumDirect() )
2604                             {
2605                                 bSub = false;
2606                             }
2607                             else
2608                             {
2609                                 pTmp = pTmp->GetAnchorFrame()->FindFlyFrame();
2610                             }
2611                         } while ( bSub && pTmp );
2612                     }
2613                     if ( bSub )
2614                         Sub( aRegion, pFly->getFrameArea() );
2615                 }
2616             }
2617         }
2618         if ( pPage == pEndPage )
2619             break;
2620         else
2621             pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
2622     }
2623 
2624     //Because it looks better, we close the DropCaps.
2625     SwRect aDropRect;
2626     if ( pStartFrame->IsTextFrame() )
2627     {
2628         if ( static_cast<const SwTextFrame*>(pStartFrame)->GetDropRect( aDropRect ) )
2629             Sub( aRegion, aDropRect );
2630     }
2631     if ( pEndFrame != pStartFrame && pEndFrame->IsTextFrame() )
2632     {
2633         if ( static_cast<const SwTextFrame*>(pEndFrame)->GetDropRect( aDropRect ) )
2634             Sub( aRegion, aDropRect );
2635     }
2636 
2637     rCursor.assign( aRegion.begin(), aRegion.end() );
2638 }
2639 
2640 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2641