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