1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
23 #include <frmtool.hxx>
24 #include <viewopt.hxx>
26 #include <rootfrm.hxx>
27 #include <pagefrm.hxx>
29 #include <swtypes.hxx>
30 #include <editeng/lrspitem.hxx>
31 #include <editeng/tstpitem.hxx>
32 #include <editeng/ulspitem.hxx>
33 #include <editeng/lspcitem.hxx>
34 #include "pormulti.hxx"
36 #include <IDocumentDeviceAccess.hxx>
37 #include <sortedobjs.hxx>
39 #include <unicode/ubidi.h>
44 #include <crstate.hxx>
46 #include <swfntcch.hxx>
49 #define MIN_OFFSET_STEP 10
51 using namespace ::com::sun::star
;
54 * - SurvivalKit: For how long do we get past the last char of the line.
55 * - RightMargin abstains from adjusting position with -1
56 * - GetCharRect returns a GetEndCharRect for CursorMoveState::RightMargin
57 * - GetEndCharRect sets bRightMargin to true
58 * - SwTextCursor::bRightMargin is set to false by CharCursorToLine
64 SwTextFrame
*GetAdjFrameAtPos( SwTextFrame
*pFrame
, const SwPosition
&rPos
,
65 const bool bRightMargin
, const bool bNoScroll
= true )
67 // RightMargin in the last master line
68 TextFrameIndex
const nOffset
= pFrame
->MapModelToViewPos(rPos
);
69 SwTextFrame
*pFrameAtPos
= pFrame
;
70 if( !bNoScroll
|| pFrame
->GetFollow() )
72 pFrameAtPos
= pFrame
->GetFrameAtPos( rPos
);
73 if (nOffset
< pFrameAtPos
->GetOffset() &&
74 !pFrameAtPos
->IsFollow() )
76 assert(pFrameAtPos
->MapModelToViewPos(rPos
) == nOffset
);
77 TextFrameIndex
nNew(nOffset
);
78 if (nNew
< TextFrameIndex(MIN_OFFSET_STEP
))
79 nNew
= TextFrameIndex(0);
81 nNew
-= TextFrameIndex(MIN_OFFSET_STEP
);
82 sw_ChangeOffset( pFrameAtPos
, nNew
);
85 while( pFrame
!= pFrameAtPos
)
88 pFrame
->GetFormatted();
89 pFrameAtPos
= pFrame
->GetFrameAtPos( rPos
);
92 if( nOffset
&& bRightMargin
)
95 pFrameAtPos
->MapViewToModelPos(pFrameAtPos
->GetOffset()) == rPos
&&
96 pFrameAtPos
->IsFollow() )
98 pFrameAtPos
->GetFormatted();
99 pFrameAtPos
= pFrameAtPos
->FindMaster();
101 OSL_ENSURE( pFrameAtPos
, "+GetCharRect: no frame with my rightmargin" );
103 return pFrameAtPos
? pFrameAtPos
: pFrame
;
108 bool sw_ChangeOffset(SwTextFrame
* pFrame
, TextFrameIndex nNew
)
110 // Do not scroll in areas and outside of flies
111 OSL_ENSURE( !pFrame
->IsFollow(), "Illegal Scrolling by Follow!" );
112 if( pFrame
->GetOffset() != nNew
&& !pFrame
->IsInSct() )
114 SwFlyFrame
*pFly
= pFrame
->FindFlyFrame();
115 // Attention: if e.g. in a column frame the size is still invalid
116 // we must not scroll around just like that
117 if ( ( pFly
&& pFly
->isFrameAreaDefinitionValid() &&
118 !pFly
->GetNextLink() && !pFly
->GetPrevLink() ) ||
119 ( !pFly
&& pFrame
->IsInTab() ) )
121 SwViewShell
* pVsh
= pFrame
->getRootFrame()->GetCurrShell();
124 if( pVsh
->GetRingContainer().size() > 1 ||
125 ( pFrame
->GetDrawObjs() && pFrame
->GetDrawObjs()->size() ) )
127 if( !pFrame
->GetOffset() )
129 nNew
= TextFrameIndex(0);
131 pFrame
->SetOffset( nNew
);
132 pFrame
->SetPara( nullptr );
133 pFrame
->GetFormatted();
134 if( pFrame
->getFrameArea().HasArea() )
135 pFrame
->getRootFrame()->GetCurrShell()->InvalidateWindows( pFrame
->getFrameArea() );
143 SwTextFrame
& SwTextFrame::GetFrameAtOfst(TextFrameIndex
const nWhere
)
145 SwTextFrame
* pRet
= this;
146 while( pRet
->HasFollow() && nWhere
>= pRet
->GetFollow()->GetOffset() )
147 pRet
= pRet
->GetFollow();
151 SwTextFrame
*SwTextFrame::GetFrameAtPos( const SwPosition
&rPos
)
153 TextFrameIndex
const nPos(MapModelToViewPos(rPos
));
154 SwTextFrame
*pFoll
= this;
155 while( pFoll
->GetFollow() )
157 if (nPos
> pFoll
->GetFollow()->GetOffset())
158 pFoll
= pFoll
->GetFollow();
161 if (nPos
== pFoll
->GetFollow()->GetOffset()
162 && !SwTextCursor::IsRightMargin() )
163 pFoll
= pFoll
->GetFollow();
172 * GetCharRect() returns the char's char line described by aPos.
173 * GetModelPositionForViewPoint() does the reverse: It goes from a document coordinate to
175 * Both are virtual in the frame base class and thus are redefined here.
178 bool SwTextFrame::GetCharRect( SwRect
& rOrig
, const SwPosition
&rPos
,
179 SwCursorMoveState
*pCMS
, bool bAllowFarAway
) const
181 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::GetCharRect with swapped frame" );
186 // Find the right frame first. We need to keep in mind that:
187 // - the cached information could be invalid (GetPara() == 0)
188 // - we could have a Follow
189 // - the Follow chain grows dynamically; the one we end up in
190 // needs to be formatted
192 // Optimisation: reading ahead saves us a GetAdjFrameAtPos
193 const bool bRightMargin
= pCMS
&& ( CursorMoveState::RightMargin
== pCMS
->m_eState
);
194 const bool bNoScroll
= pCMS
&& pCMS
->m_bNoScroll
;
195 SwTextFrame
*pFrame
= GetAdjFrameAtPos( const_cast<SwTextFrame
*>(this), rPos
, bRightMargin
,
197 pFrame
->GetFormatted();
199 const SwFrame
* pTmpFrame
= pFrame
->GetUpper();
200 if (pTmpFrame
->getFrameArea().Top() == FAR_AWAY
&& !bAllowFarAway
)
203 SwRectFnSet
aRectFnSet(pFrame
);
204 const SwTwips nUpperMaxY
= aRectFnSet
.GetPrtBottom(*pTmpFrame
);
205 const SwTwips nFrameMaxY
= aRectFnSet
.GetPrtBottom(*pFrame
);
207 // nMaxY is an absolute value
208 SwTwips nMaxY
= aRectFnSet
.IsVert() ?
209 ( aRectFnSet
.IsVertL2R() ? std::min( nFrameMaxY
, nUpperMaxY
) : std::max( nFrameMaxY
, nUpperMaxY
) ) :
210 std::min( nFrameMaxY
, nUpperMaxY
);
214 if ( pFrame
->IsEmpty() || ! aRectFnSet
.GetHeight(pFrame
->getFramePrintArea()) )
216 Point aPnt1
= pFrame
->getFrameArea().Pos() + pFrame
->getFramePrintArea().Pos();
217 SwTextNode
const*const pTextNd(GetTextNodeForParaProps());
219 pTextNd
->GetFirstLineOfsWithNum( nFirstOffset
);
222 if ( aRectFnSet
.IsVert() )
224 if( nFirstOffset
> 0 )
225 aPnt1
.AdjustY(nFirstOffset
);
226 if ( aPnt1
.X() < nMaxY
&& !aRectFnSet
.IsVertL2R() )
228 aPnt2
.setX( aPnt1
.X() + pFrame
->getFramePrintArea().Width() );
229 aPnt2
.setY( aPnt1
.Y() );
230 if( aPnt2
.X() < nMaxY
)
235 if( nFirstOffset
> 0 )
236 aPnt1
.AdjustX(nFirstOffset
);
238 if( aPnt1
.Y() > nMaxY
)
240 aPnt2
.setX( aPnt1
.X() );
241 aPnt2
.setY( aPnt1
.Y() + pFrame
->getFramePrintArea().Height() );
242 if( aPnt2
.Y() > nMaxY
)
246 rOrig
= SwRect( aPnt1
, aPnt2
);
250 pCMS
->m_aRealHeight
.setX( 0 );
251 pCMS
->m_aRealHeight
.setY( aRectFnSet
.IsVert() ? -rOrig
.Width() : rOrig
.Height() );
254 if ( pFrame
->IsRightToLeft() )
255 pFrame
->SwitchLTRtoRTL( rOrig
);
261 if( !pFrame
->HasPara() )
264 SwFrameSwapper
aSwapper( pFrame
, true );
265 if ( aRectFnSet
.IsVert() )
266 nMaxY
= pFrame
->SwitchVerticalToHorizontal( nMaxY
);
269 TextFrameIndex
const nOffset
= MapModelToViewPos(rPos
);
270 assert(nOffset
!= TextFrameIndex(COMPLETE_STRING
)); // not going to end well
271 TextFrameIndex nNextOfst
;
276 SwTextSizeInfo
aInf( pFrame
);
277 SwTextCursor
aLine( pFrame
, &aInf
);
278 nNextOfst
= aLine
.GetEnd();
279 // See comment in AdjustFrame
280 // Include the line's last char?
282 aLine
.GetEndCharRect( &rOrig
, nOffset
, pCMS
, nMaxY
);
284 aLine
.GetCharRect( &rOrig
, nOffset
, pCMS
, nMaxY
);
288 if ( pFrame
->IsRightToLeft() )
289 pFrame
->SwitchLTRtoRTL( rOrig
);
291 if ( aRectFnSet
.IsVert() )
292 pFrame
->SwitchHorizontalToVertical( rOrig
);
294 if( pFrame
->IsUndersized() && pCMS
&& !pFrame
->GetNext() &&
295 aRectFnSet
.GetBottom(rOrig
) == nUpperMaxY
&&
296 pFrame
->GetOffset() < nOffset
&&
297 !pFrame
->IsFollow() && !bNoScroll
&&
298 TextFrameIndex(pFrame
->GetText().getLength()) != nNextOfst
)
300 bGoOn
= sw_ChangeOffset( pFrame
, nNextOfst
);
308 if ( pFrame
->IsRightToLeft() )
310 if( pCMS
->m_b2Lines
&& pCMS
->m_p2Lines
)
312 pFrame
->SwitchLTRtoRTL( pCMS
->m_p2Lines
->aLine
);
313 pFrame
->SwitchLTRtoRTL( pCMS
->m_p2Lines
->aPortion
);
317 if ( aRectFnSet
.IsVert() )
319 if ( pCMS
->m_bRealHeight
)
321 pCMS
->m_aRealHeight
.setY( -pCMS
->m_aRealHeight
.Y() );
322 if ( pCMS
->m_aRealHeight
.Y() < 0 )
324 // writing direction is from top to bottom
325 pCMS
->m_aRealHeight
.setX( rOrig
.Width() -
326 pCMS
->m_aRealHeight
.X() +
327 pCMS
->m_aRealHeight
.Y() );
330 if( pCMS
->m_b2Lines
&& pCMS
->m_p2Lines
)
332 pFrame
->SwitchHorizontalToVertical( pCMS
->m_p2Lines
->aLine
);
333 pFrame
->SwitchHorizontalToVertical( pCMS
->m_p2Lines
->aPortion
);
341 SwPageFrame
*pPage
= pFrame
->FindPageFrame();
342 OSL_ENSURE( pPage
, "Text escaped from page?" );
343 const SwTwips nOrigTop
= aRectFnSet
.GetTop(rOrig
);
344 const SwTwips nPageTop
= aRectFnSet
.GetTop(pPage
->getFrameArea());
345 const SwTwips nPageBott
= aRectFnSet
.GetBottom(pPage
->getFrameArea());
347 // We have the following situation: if the frame is in an invalid
348 // sectionframe, it's possible that the frame is outside the page.
349 // If we restrict the cursor position to the page area, we enforce
350 // the formatting of the page, of the section frame and the frame itself.
351 if( aRectFnSet
.YDiff( nPageTop
, nOrigTop
) > 0 )
352 aRectFnSet
.SetTop( rOrig
, nPageTop
);
354 if ( aRectFnSet
.YDiff( nOrigTop
, nPageBott
) > 0 )
355 aRectFnSet
.SetTop( rOrig
, nPageBott
);
362 * GetAutoPos() looks up the char's char line which is described by rPos
363 * and is used by the auto-positioned frame.
366 bool SwTextFrame::GetAutoPos( SwRect
& rOrig
, const SwPosition
&rPos
) const
371 TextFrameIndex
const nOffset
= MapModelToViewPos(rPos
);
372 SwTextFrame
* pFrame
= &(const_cast<SwTextFrame
*>(this)->GetFrameAtOfst( nOffset
));
374 pFrame
->GetFormatted();
375 const SwFrame
* pTmpFrame
= pFrame
->GetUpper();
377 SwRectFnSet
aRectFnSet(pTmpFrame
);
378 SwTwips nUpperMaxY
= aRectFnSet
.GetPrtBottom(*pTmpFrame
);
380 // nMaxY is in absolute value
382 if ( aRectFnSet
.IsVert() )
384 if ( aRectFnSet
.IsVertL2R() )
385 nMaxY
= std::min( SwTwips(aRectFnSet
.GetPrtBottom(*pFrame
)), nUpperMaxY
);
387 nMaxY
= std::max( SwTwips(aRectFnSet
.GetPrtBottom(*pFrame
)), nUpperMaxY
);
390 nMaxY
= std::min( SwTwips(aRectFnSet
.GetPrtBottom(*pFrame
)), nUpperMaxY
);
391 if ( pFrame
->IsEmpty() || ! aRectFnSet
.GetHeight(pFrame
->getFramePrintArea()) )
393 Point aPnt1
= pFrame
->getFrameArea().Pos() + pFrame
->getFramePrintArea().Pos();
395 if ( aRectFnSet
.IsVert() )
397 if ( aPnt1
.X() < nMaxY
&& !aRectFnSet
.IsVertL2R() )
400 aPnt2
.setX( aPnt1
.X() + pFrame
->getFramePrintArea().Width() );
401 aPnt2
.setY( aPnt1
.Y() );
402 if( aPnt2
.X() < nMaxY
)
407 if( aPnt1
.Y() > nMaxY
)
409 aPnt2
.setX( aPnt1
.X() );
410 aPnt2
.setY( aPnt1
.Y() + pFrame
->getFramePrintArea().Height() );
411 if( aPnt2
.Y() > nMaxY
)
414 rOrig
= SwRect( aPnt1
, aPnt2
);
419 if( !pFrame
->HasPara() )
422 SwFrameSwapper
aSwapper( pFrame
, true );
423 if ( aRectFnSet
.IsVert() )
424 nMaxY
= pFrame
->SwitchVerticalToHorizontal( nMaxY
);
426 SwTextSizeInfo
aInf( pFrame
);
427 SwTextCursor
aLine( pFrame
, &aInf
);
428 SwCursorMoveState
aTmpState( CursorMoveState::SetOnlyText
);
429 aTmpState
.m_bRealHeight
= true;
430 aLine
.GetCharRect( &rOrig
, nOffset
, &aTmpState
, nMaxY
);
431 if( aTmpState
.m_aRealHeight
.X() >= 0 )
433 rOrig
.Pos().AdjustY(aTmpState
.m_aRealHeight
.X() );
434 rOrig
.Height( aTmpState
.m_aRealHeight
.Y() );
437 if ( pFrame
->IsRightToLeft() )
438 pFrame
->SwitchLTRtoRTL( rOrig
);
440 if ( aRectFnSet
.IsVert() )
441 pFrame
->SwitchHorizontalToVertical( rOrig
);
447 /** determine top of line for given position in the text frame
449 - Top of first paragraph line is the top of the printing area of the text frame
450 - If a proportional line spacing is applied use top of anchor character as
453 bool SwTextFrame::GetTopOfLine( SwTwips
& _onTopOfLine
,
454 const SwPosition
& _rPos
) const
458 // get position offset
459 TextFrameIndex
const nOffset
= MapModelToViewPos(_rPos
);
461 if (TextFrameIndex(GetText().getLength()) < nOffset
)
467 SwRectFnSet
aRectFnSet(this);
468 if ( IsEmpty() || !aRectFnSet
.GetHeight(getFramePrintArea()) )
470 // consider upper space amount considered
471 // for previous frame and the page grid.
472 _onTopOfLine
= aRectFnSet
.GetPrtTop(*this);
476 // determine formatted text frame that contains the requested position
477 SwTextFrame
* pFrame
= &(const_cast<SwTextFrame
*>(this)->GetFrameAtOfst( nOffset
));
478 pFrame
->GetFormatted();
479 aRectFnSet
.Refresh(pFrame
);
480 // If proportional line spacing is applied
481 // to the text frame, the top of the anchor character is also the
483 // Otherwise the line layout determines the top of the line
484 const SvxLineSpacingItem
& rSpace
= GetAttrSet()->GetLineSpacing();
485 if ( rSpace
.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop
)
488 if ( GetAutoPos( aCharRect
, _rPos
) )
490 _onTopOfLine
= aRectFnSet
.GetTop(aCharRect
);
499 // assure that text frame is in a horizontal layout
500 SwFrameSwapper
aSwapper( pFrame
, true );
501 // determine text line that contains the requested position
502 SwTextSizeInfo
aInf( pFrame
);
503 SwTextCursor
aLine( pFrame
, &aInf
);
504 aLine
.CharCursorToLine( nOffset
);
505 // determine top of line
506 _onTopOfLine
= aLine
.Y();
507 if ( aRectFnSet
.IsVert() )
509 _onTopOfLine
= pFrame
->SwitchHorizontalToVertical( _onTopOfLine
);
518 // Minimum distance of non-empty lines is a little less than 2 cm
519 #define FILL_MIN_DIST 1100
524 const SwCursorMoveState
*pCMS
;
532 SwFillData( const SwCursorMoveState
*pC
, SwPosition
* pP
, const SwRect
& rR
,
533 const Point
& rPt
) : aFrame( rR
), pCMS( pC
), pPos( pP
), rPoint( rPt
),
534 nLineWidth( 0 ), bFirstLine( true ), bInner( false ), bColumn( false ),
536 SwFillMode
Mode() const { return pCMS
->m_pFill
->eMode
; }
537 tools::Long
X() const { return rPoint
.X(); }
538 tools::Long
Y() const { return rPoint
.Y(); }
539 tools::Long
Left() const { return aFrame
.Left(); }
540 tools::Long
Right() const { return aFrame
.Right(); }
541 tools::Long
Bottom() const { return aFrame
.Bottom(); }
542 SwFillCursorPos
&Fill() const { return *pCMS
->m_pFill
; }
543 void SetTab( sal_uInt16 nNew
) { pCMS
->m_pFill
->nTabCnt
= nNew
; }
544 void SetSpace( sal_uInt16 nNew
) { pCMS
->m_pFill
->nSpaceCnt
= nNew
; }
545 void SetSpaceOnly( sal_uInt16 nNew
) { pCMS
->m_pFill
->nSpaceOnlyCnt
= nNew
; }
546 void SetOrient( const sal_Int16 eNew
){ pCMS
->m_pFill
->eOrient
= eNew
; }
549 bool SwTextFrame::GetModelPositionForViewPoint_(SwPosition
* pPos
, const Point
& rPoint
,
550 const bool bChgFrame
, SwCursorMoveState
* pCMS
) const
552 // GetModelPositionForViewPoint_ is called by GetModelPositionForViewPoint and GetKeyCursorOfst.
553 // Never just a return false.
555 if( IsLocked() || IsHiddenNow() )
558 const_cast<SwTextFrame
*>(this)->GetFormatted();
560 Point
aOldPoint( rPoint
);
564 SwitchVerticalToHorizontal( const_cast<Point
&>(rPoint
) );
565 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
568 if ( IsRightToLeft() )
569 SwitchRTLtoLTR( const_cast<Point
&>(rPoint
) );
571 std::unique_ptr
<SwFillData
> pFillData
;
572 if ( pCMS
&& pCMS
->m_pFill
)
573 pFillData
.reset(new SwFillData( pCMS
, pPos
, getFrameArea(), rPoint
));
577 *pPos
= MapViewToModelPos(TextFrameIndex(0));
578 if( pCMS
&& pCMS
->m_bFieldInfo
)
580 SwTwips nDiff
= rPoint
.X() - getFrameArea().Left() - getFramePrintArea().Left();
581 if( nDiff
> 50 || nDiff
< 0 )
582 pCMS
->m_bPosCorr
= true;
587 SwTextSizeInfo
aInf( const_cast<SwTextFrame
*>(this) );
588 SwTextCursor
aLine( const_cast<SwTextFrame
*>(this), &aInf
);
590 // See comment in AdjustFrame()
591 SwTwips nMaxY
= getFrameArea().Top() + getFramePrintArea().Top() + getFramePrintArea().Height();
592 aLine
.TwipsToLine( rPoint
.Y() );
593 while( aLine
.Y() + aLine
.GetLineHeight() > nMaxY
)
599 if( aLine
.GetDropLines() >= aLine
.GetLineNr() && 1 != aLine
.GetLineNr()
600 && rPoint
.X() < aLine
.FirstLeft() + aLine
.GetDropLeft() )
601 while( aLine
.GetLineNr() > 1 )
604 TextFrameIndex nOffset
= aLine
.GetModelPositionForViewPoint(pPos
, rPoint
, bChgFrame
, pCMS
);
606 if( pCMS
&& pCMS
->m_eState
== CursorMoveState::NONE
&& aLine
.GetEnd() == nOffset
)
607 pCMS
->m_eState
= CursorMoveState::RightMargin
;
609 // pPos is a pure IN parameter and must not be evaluated.
610 // pIter->GetModelPositionForViewPoint returns from a nesting with COMPLETE_STRING.
611 // If SwTextIter::GetModelPositionForViewPoint calls GetModelPositionForViewPoint further by itself
612 // nNode changes the position.
613 // In such cases, pPos must not be calculated.
614 if (TextFrameIndex(COMPLETE_STRING
) != nOffset
)
616 *pPos
= MapViewToModelPos(nOffset
);
619 if (TextFrameIndex(GetText().getLength()) > nOffset
||
620 rPoint
.Y() < getFrameArea().Top() )
621 pFillData
->bInner
= true;
622 pFillData
->bFirstLine
= aLine
.GetLineNr() < 2;
623 if (GetText().getLength())
625 pFillData
->bEmpty
= false;
626 pFillData
->nLineWidth
= aLine
.GetCurr()->Width();
631 bool bChgFillData
= false;
632 if( pFillData
&& FindPageFrame()->getFrameArea().Contains( aOldPoint
) )
634 FillCursorPos( *pFillData
);
641 SwitchHorizontalToVertical( pFillData
->Fill().aCursor
.Pos() );
642 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
645 if ( IsRightToLeft() && bChgFillData
)
647 SwitchLTRtoRTL( pFillData
->Fill().aCursor
.Pos() );
648 const sal_Int16 eOrient
= pFillData
->pCMS
->m_pFill
->eOrient
;
650 if ( text::HoriOrientation::LEFT
== eOrient
)
651 pFillData
->SetOrient( text::HoriOrientation::RIGHT
);
652 else if ( text::HoriOrientation::RIGHT
== eOrient
)
653 pFillData
->SetOrient( text::HoriOrientation::LEFT
);
656 const_cast<Point
&>(rPoint
) = aOldPoint
;
661 bool SwTextFrame::GetModelPositionForViewPoint(SwPosition
* pPos
, Point
& rPoint
,
662 SwCursorMoveState
* pCMS
, bool ) const
664 const bool bChgFrame
= !(pCMS
&& CursorMoveState::UpDown
== pCMS
->m_eState
);
665 return GetModelPositionForViewPoint_( pPos
, rPoint
, bChgFrame
, pCMS
);
669 * Layout-oriented cursor movement to the line start.
672 bool SwTextFrame::LeftMargin(SwPaM
*pPam
) const
674 assert(GetMergedPara() || &pPam
->GetPointNode() == static_cast<SwContentNode
const*>(GetDep()));
676 SwTextFrame
*pFrame
= GetAdjFrameAtPos( const_cast<SwTextFrame
*>(this), *pPam
->GetPoint(),
677 SwTextCursor::IsRightMargin() );
678 pFrame
->GetFormatted();
679 TextFrameIndex nIndx
;
680 if ( pFrame
->IsEmpty() )
681 nIndx
= TextFrameIndex(0);
684 SwTextSizeInfo
aInf( pFrame
);
685 SwTextCursor
aLine( pFrame
, &aInf
);
687 aLine
.CharCursorToLine(pFrame
->MapModelToViewPos(*pPam
->GetPoint()));
688 nIndx
= aLine
.GetStart();
689 if( pFrame
->GetOffset() && !pFrame
->IsFollow() && !aLine
.GetPrev() )
691 sw_ChangeOffset(pFrame
, TextFrameIndex(0));
692 nIndx
= TextFrameIndex(0);
695 *pPam
->GetPoint() = pFrame
->MapViewToModelPos(nIndx
);
696 SwTextCursor::SetRightMargin( false );
701 * To the line end: That's the position before the last char of the line.
702 * Exception: In the last line, it should be able to place the cursor after
703 * the last char in order to append text.
706 bool SwTextFrame::RightMargin(SwPaM
*pPam
, bool bAPI
) const
708 assert(GetMergedPara() || &pPam
->GetPointNode() == static_cast<SwContentNode
const*>(GetDep()));
710 SwTextFrame
*pFrame
= GetAdjFrameAtPos( const_cast<SwTextFrame
*>(this), *pPam
->GetPoint(),
711 SwTextCursor::IsRightMargin() );
712 pFrame
->GetFormatted();
713 TextFrameIndex
nRightMargin(0);
716 SwTextSizeInfo
aInf( pFrame
);
717 SwTextCursor
aLine( pFrame
, &aInf
);
719 aLine
.CharCursorToLine(MapModelToViewPos(*pPam
->GetPoint()));
720 nRightMargin
= aLine
.GetStart() + aLine
.GetCurr()->GetLen();
722 // We skip hard line breaks
723 if( aLine
.GetCurr()->GetLen() &&
724 CH_BREAK
== aInf
.GetText()[sal_Int32(nRightMargin
) - 1])
726 else if( !bAPI
&& (aLine
.GetNext() || pFrame
->GetFollow()) )
728 while( nRightMargin
> aLine
.GetStart() &&
729 ' ' == aInf
.GetText()[sal_Int32(nRightMargin
) - 1])
733 *pPam
->GetPoint() = pFrame
->MapViewToModelPos(nRightMargin
);
734 SwTextCursor::SetRightMargin( !bAPI
);
738 // The following two methods try to put the Cursor into the next/successive
739 // line. If we do not have a preceding/successive line we forward the call
740 // to the base class.
741 // The Cursor's horizontal justification is done afterwards by the CursorShell.
745 class SwSetToRightMargin
754 ~SwSetToRightMargin() { SwTextCursor::SetRightMargin(m_bRight
); }
755 void SetRight(const bool bNew
) { m_bRight
= bNew
; }
760 bool SwTextFrame::UnitUp_( SwPaM
*pPam
, const SwTwips nOffset
,
761 bool bSetInReadOnly
) const
763 // Set the RightMargin if needed
764 SwSetToRightMargin aSet
;
767 pPam
->GetPointNode().StartOfSectionNode() !=
768 pPam
->GetMarkNode().StartOfSectionNode() )
770 // If the PaM is located within different boxes, we have a table selection,
771 // which is handled by the base class.
772 return SwContentFrame::UnitUp( pPam
, nOffset
, bSetInReadOnly
);
775 const_cast<SwTextFrame
*>(this)->GetFormatted();
776 const TextFrameIndex nPos
= MapModelToViewPos(*pPam
->GetPoint());
779 if( !IsEmpty() && !IsHiddenNow() )
781 TextFrameIndex
nFormat(COMPLETE_STRING
);
784 if (nFormat
!= TextFrameIndex(COMPLETE_STRING
) && !IsFollow())
785 sw_ChangeOffset( const_cast<SwTextFrame
*>(this), nFormat
);
787 SwTextSizeInfo
aInf( const_cast<SwTextFrame
*>(this) );
788 SwTextCursor
aLine( const_cast<SwTextFrame
*>(this), &aInf
);
790 // Optimize away flys with no flow and IsDummy()
792 aLine
.CharCursorToLine( nPos
);
796 const SwLineLayout
*pPrevLine
= aLine
.GetPrevLine();
797 const TextFrameIndex nStart
= aLine
.GetStart();
798 aLine
.GetCharRect( &aCharBox
, nPos
);
800 bool bSecondOfDouble
= ( aInf
.IsMulti() && ! aInf
.IsFirstMulti() );
801 bool bPrevLine
= ( pPrevLine
&& pPrevLine
!= aLine
.GetCurr() );
803 if( !pPrevLine
&& !bSecondOfDouble
&& GetOffset() && !IsFollow() )
805 nFormat
= GetOffset();
806 TextFrameIndex nDiff
= aLine
.GetLength();
808 nDiff
= TextFrameIndex(MIN_OFFSET_STEP
);
809 if( nFormat
> nDiff
)
810 nFormat
= nFormat
- nDiff
;
812 nFormat
= TextFrameIndex(0);
816 // We select the target line for the cursor, in case we are in a
817 // double line portion, prev line = curr line
818 if( bPrevLine
&& !bSecondOfDouble
)
821 while ( aLine
.GetStart() == nStart
&&
822 nullptr != ( pPrevLine
= aLine
.GetPrevLine() ) &&
823 pPrevLine
!= aLine
.GetCurr() )
827 if ( bPrevLine
|| bSecondOfDouble
)
829 aCharBox
.Width( aCharBox
.SSize().Width() / 2 );
830 aCharBox
.Pos().setX( aCharBox
.Pos().X() - 150 );
832 // See comment in SwTextFrame::GetModelPositionForViewPoint()
833 #if OSL_DEBUG_LEVEL > 0
834 const SwNodeOffset nOldNode
= pPam
->GetPoint()->GetNodeIndex();
836 // The node should not be changed
837 TextFrameIndex nTmpOfst
= aLine
.GetModelPositionForViewPoint(pPam
->GetPoint(),
838 aCharBox
.Pos(), false );
839 #if OSL_DEBUG_LEVEL > 0
840 OSL_ENSURE( nOldNode
== pPam
->GetPoint()->GetNodeIndex(),
841 "SwTextFrame::UnitUp: illegal node change" );
844 // We make sure that we move up.
845 if( nTmpOfst
>= nStart
&& nStart
&& !bSecondOfDouble
)
848 aSet
.SetRight( true );
850 *pPam
->GetPoint() = MapViewToModelPos(nTmpOfst
);
856 aLine
.GetCharRect( &aCharBox
, nPos
);
857 aCharBox
.Width( aCharBox
.SSize().Width() / 2 );
862 /* If 'this' is a follow and a prev failed, we need to go to the
863 * last line of the master, which is us.
864 * Or: If we are a follow with follow, we need to get the master.
868 const SwTextFrame
*pTmpPrev
= FindMaster();
869 TextFrameIndex nOffs
= GetOffset();
872 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
873 const bool bProtectedAllowed
= pSh
&& pSh
->GetViewOptions()->IsCursorInProtectedArea();
874 const SwTextFrame
*pPrevPrev
= pTmpPrev
;
875 // We skip protected frames and frames without content here
876 while( pPrevPrev
&& ( pPrevPrev
->GetOffset() == nOffs
||
877 ( !bProtectedAllowed
&& pPrevPrev
->IsProtected() ) ) )
879 pTmpPrev
= pPrevPrev
;
880 nOffs
= pTmpPrev
->GetOffset();
881 if ( pPrevPrev
->IsFollow() )
882 pPrevPrev
= pTmpPrev
->FindMaster();
887 return pTmpPrev
->SwContentFrame::UnitUp( pPam
, nOffset
, bSetInReadOnly
);
888 aCharBox
.Pos().setY( pPrevPrev
->getFrameArea().Bottom() - 1 );
889 return pPrevPrev
->GetKeyCursorOfst( pPam
->GetPoint(), aCharBox
.Pos() );
892 return SwContentFrame::UnitUp( pPam
, nOffset
, bSetInReadOnly
);
895 // Used for Bidi. nPos is the logical position in the string, bLeft indicates
896 // if left arrow or right arrow was pressed. The return values are:
897 // nPos: the new visual position
898 // bLeft: whether the break iterator has to add or subtract from the
900 static void lcl_VisualMoveRecursion(const SwLineLayout
& rCurrLine
, TextFrameIndex nIdx
,
901 TextFrameIndex
& nPos
, bool& bRight
,
902 sal_uInt8
& nCursorLevel
, sal_uInt8 nDefaultDir
)
904 const SwLinePortion
* pPor
= rCurrLine
.GetFirstPortion();
905 const SwLinePortion
* pLast
= nullptr;
907 // What's the current portion?
908 while ( pPor
&& nIdx
+ pPor
->GetLen() <= nPos
)
910 nIdx
= nIdx
+ pPor
->GetLen();
912 pPor
= pPor
->GetNextPortion();
917 bool bRecurse
= pPor
&& pPor
->IsMultiPortion() &&
918 static_cast<const SwMultiPortion
*>(pPor
)->IsBidi();
920 // 1. special case: at beginning of bidi portion
921 if ( bRecurse
&& nIdx
== nPos
)
923 nPos
= nPos
+ pPor
->GetLen();
925 // leave bidi portion
926 if ( nCursorLevel
!= nDefaultDir
)
932 // buffer: abcXYZ123 in LTR paragraph
934 // cursor is between c and X in the buffer and cursor level = 0
938 // 2. special case: at beginning of portion after bidi portion
939 else if ( pLast
&& pLast
->IsMultiPortion() &&
940 static_cast<const SwMultiPortion
*>(pLast
)->IsBidi() && nIdx
== nPos
)
942 // enter bidi portion
943 if ( nCursorLevel
!= nDefaultDir
)
946 nIdx
= nIdx
- pLast
->GetLen();
954 const SwLineLayout
& rLine
= static_cast<const SwMultiPortion
*>(pPor
)->GetRoot();
955 TextFrameIndex nTmpPos
= nPos
- nIdx
;
956 bool bTmpForward
= ! bRight
;
957 sal_uInt8 nTmpCursorLevel
= nCursorLevel
;
958 lcl_VisualMoveRecursion(rLine
, TextFrameIndex(0), nTmpPos
, bTmpForward
,
959 nTmpCursorLevel
, nDefaultDir
+ 1 );
961 nPos
= nTmpPos
+ nIdx
;
962 bRight
= bTmpForward
;
963 nCursorLevel
= nTmpCursorLevel
;
970 nCursorLevel
= nDefaultDir
;
976 bool bRecurse
= pPor
&& pPor
->IsMultiPortion() && static_cast<const SwMultiPortion
*>(pPor
)->IsBidi();
978 // 1. special case: at beginning of bidi portion
979 if ( bRecurse
&& nIdx
== nPos
)
981 // leave bidi portion
982 if ( nCursorLevel
== nDefaultDir
)
988 // 2. special case: at beginning of portion after bidi portion
989 else if ( pLast
&& pLast
->IsMultiPortion() &&
990 static_cast<const SwMultiPortion
*>(pLast
)->IsBidi() && nIdx
== nPos
)
992 nPos
= nPos
- pLast
->GetLen();
994 // enter bidi portion
995 if ( nCursorLevel
% 2 == nDefaultDir
% 2 )
998 nIdx
= nIdx
- pLast
->GetLen();
1002 // buffer: abcXYZ123 in LTR paragraph
1004 // cursor is behind 3 in the buffer and cursor level = 2
1005 if ( nDefaultDir
+ 2 == nCursorLevel
)
1006 nPos
= nPos
+ pLast
->GetLen();
1013 const SwLineLayout
& rLine
= static_cast<const SwMultiPortion
*>(pPor
)->GetRoot();
1014 TextFrameIndex nTmpPos
= nPos
- nIdx
;
1015 bool bTmpForward
= ! bRight
;
1016 sal_uInt8 nTmpCursorLevel
= nCursorLevel
;
1017 lcl_VisualMoveRecursion(rLine
, TextFrameIndex(0), nTmpPos
, bTmpForward
,
1018 nTmpCursorLevel
, nDefaultDir
+ 1 );
1021 // buffer: abcXYZ123 in LTR paragraph
1023 // cursor is between Z and 1 in the buffer and cursor level = 2
1024 if ( nTmpPos
== pPor
->GetLen() && nTmpCursorLevel
== nDefaultDir
+ 1 )
1026 nTmpPos
= nTmpPos
- pPor
->GetLen();
1027 nTmpCursorLevel
= nDefaultDir
;
1028 bTmpForward
= ! bTmpForward
;
1031 nPos
= nTmpPos
+ nIdx
;
1032 bRight
= bTmpForward
;
1033 nCursorLevel
= nTmpCursorLevel
;
1040 nCursorLevel
= nDefaultDir
;
1045 void SwTextFrame::PrepareVisualMove(TextFrameIndex
& nPos
, sal_uInt8
& nCursorLevel
,
1046 bool& bForward
, bool bInsertCursor
)
1048 if( IsEmpty() || IsHiddenNow() )
1053 SwTextSizeInfo
aInf(this);
1054 SwTextCursor
aLine(this, &aInf
);
1057 aLine
.CharCursorToLine( nPos
);
1061 const SwLineLayout
* pLine
= aLine
.GetCurr();
1062 const TextFrameIndex nStt
= aLine
.GetStart();
1063 const TextFrameIndex nLen
= pLine
->GetLen();
1065 // We have to distinguish between an insert and overwrite cursor:
1066 // The insert cursor position depends on the cursor level:
1067 // buffer: abcXYZdef in LTR paragraph
1068 // display: abcZYXdef
1069 // If cursor is between c and X in the buffer and cursor level is 0,
1070 // the cursor blinks between c and Z and -> sets the cursor between Z and Y.
1071 // If the cursor level is 1, the cursor blinks between X and d and
1072 // -> sets the cursor between d and e.
1073 // The overwrite cursor simply travels to the next visual character.
1074 if ( bInsertCursor
)
1076 lcl_VisualMoveRecursion( *pLine
, nStt
, nPos
, bForward
,
1077 nCursorLevel
, IsRightToLeft() ? 1 : 0 );
1081 const sal_uInt8 nDefaultDir
= static_cast<sal_uInt8
>(IsRightToLeft() ? UBIDI_RTL
: UBIDI_LTR
);
1082 const bool bVisualRight
= ( nDefaultDir
== UBIDI_LTR
&& bForward
) ||
1083 ( nDefaultDir
== UBIDI_RTL
&& ! bForward
);
1085 // Bidi functions from icu 2.0
1087 const sal_Unicode
* pLineString
= GetText().getStr();
1089 UErrorCode nError
= U_ZERO_ERROR
;
1090 UBiDi
* pBidi
= ubidi_openSized( sal_Int32(nLen
), 0, &nError
);
1091 ubidi_setPara( pBidi
, reinterpret_cast<const UChar
*>(pLineString
),
1092 sal_Int32(nLen
), nDefaultDir
, nullptr, &nError
);
1094 TextFrameIndex
nTmpPos(0);
1095 bool bOutOfBounds
= false;
1097 if ( nPos
< nStt
+ nLen
)
1099 nTmpPos
= TextFrameIndex(ubidi_getVisualIndex( pBidi
, sal_Int32(nPos
), &nError
));
1101 // visual indices are always LTR aligned
1104 if (nTmpPos
+ TextFrameIndex(1) < nStt
+ nLen
)
1108 nPos
= nDefaultDir
== UBIDI_RTL
? TextFrameIndex(0) : nStt
+ nLen
;
1109 bOutOfBounds
= true;
1118 nPos
= nDefaultDir
== UBIDI_RTL
? nStt
+ nLen
: TextFrameIndex(0);
1119 bOutOfBounds
= true;
1125 nTmpPos
= nDefaultDir
== UBIDI_LTR
? nPos
- TextFrameIndex(1) : TextFrameIndex(0);
1128 if ( ! bOutOfBounds
)
1130 nPos
= TextFrameIndex(ubidi_getLogicalIndex( pBidi
, sal_Int32(nTmpPos
), &nError
));
1139 bForward
= ! bForward
;
1146 ubidi_close( pBidi
);
1149 bool SwTextFrame::UnitDown_(SwPaM
*pPam
, const SwTwips nOffset
,
1150 bool bSetInReadOnly
) const
1154 pPam
->GetPointNode().StartOfSectionNode() !=
1155 pPam
->GetMarkNode().StartOfSectionNode() )
1157 // If the PaM is located within different boxes, we have a table selection,
1158 // which is handled by the base class.
1159 return SwContentFrame::UnitDown( pPam
, nOffset
, bSetInReadOnly
);
1161 const_cast<SwTextFrame
*>(this)->GetFormatted();
1162 const TextFrameIndex nPos
= MapModelToViewPos(*pPam
->GetPoint());
1164 const SwContentFrame
*pTmpFollow
= nullptr;
1167 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
1169 if ( !IsEmpty() && !IsHiddenNow() )
1171 TextFrameIndex
nFormat(COMPLETE_STRING
);
1174 if (nFormat
!= TextFrameIndex(COMPLETE_STRING
) && !IsFollow() &&
1175 !sw_ChangeOffset( const_cast<SwTextFrame
*>(this), nFormat
) )
1178 SwTextSizeInfo
aInf( const_cast<SwTextFrame
*>(this) );
1179 SwTextCursor
aLine( const_cast<SwTextFrame
*>(this), &aInf
);
1180 nFormat
= aLine
.GetEnd();
1182 aLine
.CharCursorToLine( nPos
);
1184 const SwLineLayout
* pNextLine
= aLine
.GetNextLine();
1185 const TextFrameIndex nStart
= aLine
.GetStart();
1186 aLine
.GetCharRect( &aCharBox
, nPos
);
1188 bool bFirstOfDouble
= ( aInf
.IsMulti() && aInf
.IsFirstMulti() );
1190 if( pNextLine
|| bFirstOfDouble
)
1192 aCharBox
.Width( aCharBox
.SSize().Width() / 2 );
1193 #if OSL_DEBUG_LEVEL > 0
1194 // See comment in SwTextFrame::GetModelPositionForViewPoint()
1195 const SwNodeOffset nOldNode
= pPam
->GetPoint()->GetNodeIndex();
1197 if ( pNextLine
&& ! bFirstOfDouble
)
1200 TextFrameIndex nTmpOfst
= aLine
.GetModelPositionForViewPoint( pPam
->GetPoint(),
1201 aCharBox
.Pos(), false );
1202 #if OSL_DEBUG_LEVEL > 0
1203 OSL_ENSURE( nOldNode
== pPam
->GetPoint()->GetNodeIndex(),
1204 "SwTextFrame::UnitDown: illegal node change" );
1207 // We make sure that we move down.
1208 if( nTmpOfst
<= nStart
&& ! bFirstOfDouble
)
1209 nTmpOfst
= nStart
+ TextFrameIndex(1);
1210 *pPam
->GetPoint() = MapViewToModelPos(nTmpOfst
);
1213 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
1217 pTmpFollow
= GetFollow();
1218 if( nullptr != pTmpFollow
)
1219 { // Skip protected follows
1220 const SwContentFrame
* pTmp
= pTmpFollow
;
1221 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
1222 if( !pSh
|| !pSh
->GetViewOptions()->IsCursorInProtectedArea() )
1224 while( pTmpFollow
&& pTmpFollow
->IsProtected() )
1227 pTmpFollow
= pTmpFollow
->GetFollow();
1230 if( !pTmpFollow
) // Only protected ones left
1233 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
1234 return pTmp
->SwContentFrame::UnitDown( pPam
, nOffset
, bSetInReadOnly
);
1237 aLine
.GetCharRect( &aCharBox
, nPos
);
1238 aCharBox
.Width( aCharBox
.SSize().Width() / 2 );
1240 else if( !IsFollow() )
1242 TextFrameIndex
nTmpLen(aInf
.GetText().getLength());
1243 if( aLine
.GetEnd() < nTmpLen
)
1245 if( nFormat
<= GetOffset() )
1247 nFormat
= std::min(GetOffset() + TextFrameIndex(MIN_OFFSET_STEP
),
1249 if( nFormat
<= GetOffset() )
1259 pTmpFollow
= GetFollow();
1262 const_cast<SwTextFrame
*>(this)->SwapWidthAndHeight();
1264 // We take a shortcut for follows
1267 aCharBox
.Pos().setY( pTmpFollow
->getFrameArea().Top() + 1 );
1268 return static_cast<const SwTextFrame
*>(pTmpFollow
)->GetKeyCursorOfst( pPam
->GetPoint(),
1271 return SwContentFrame::UnitDown( pPam
, nOffset
, bSetInReadOnly
);
1274 bool SwTextFrame::UnitUp(SwPaM
*pPam
, const SwTwips nOffset
,
1275 bool bSetInReadOnly
) const
1277 /* We call ContentNode::GertFrame() in CursorSh::Up().
1278 * This _always returns the master.
1279 * In order to not mess with cursor travelling, we correct here
1281 * We calculate UnitUp for pFrame. pFrame is either a master (= this) or a
1284 const SwTextFrame
*pFrame
= GetAdjFrameAtPos( const_cast<SwTextFrame
*>(this), *(pPam
->GetPoint()),
1285 SwTextCursor::IsRightMargin() );
1286 const bool bRet
= pFrame
->UnitUp_( pPam
, nOffset
, bSetInReadOnly
);
1288 // No SwTextCursor::SetRightMargin( false );
1289 // Instead we have a SwSetToRightMargin in UnitUp_
1293 bool SwTextFrame::UnitDown(SwPaM
*pPam
, const SwTwips nOffset
,
1294 bool bSetInReadOnly
) const
1296 const SwTextFrame
*pFrame
= GetAdjFrameAtPos(const_cast<SwTextFrame
*>(this), *(pPam
->GetPoint()),
1297 SwTextCursor::IsRightMargin() );
1298 const bool bRet
= pFrame
->UnitDown_( pPam
, nOffset
, bSetInReadOnly
);
1299 SwTextCursor::SetRightMargin( false );
1303 void SwTextFrame::FillCursorPos( SwFillData
& rFill
) const
1305 if( !rFill
.bColumn
&& GetUpper()->IsColBodyFrame() ) // ColumnFrames now with BodyFrame
1307 const SwColumnFrame
* pTmp
=
1308 static_cast<const SwColumnFrame
*>(GetUpper()->GetUpper()->GetUpper()->Lower()); // The 1st column
1309 // The first SwFrame in BodyFrame of the first column
1310 const SwFrame
* pFrame
= static_cast<const SwLayoutFrame
*>(pTmp
->Lower())->Lower();
1311 sal_uInt16 nNextCol
= 0;
1312 // In which column do we end up in?
1313 while( rFill
.X() > pTmp
->getFrameArea().Right() && pTmp
->GetNext() )
1315 pTmp
= static_cast<const SwColumnFrame
*>(pTmp
->GetNext());
1316 if( static_cast<const SwLayoutFrame
*>(pTmp
->Lower())->Lower() ) // ColumnFrames now with BodyFrame
1318 pFrame
= static_cast<const SwLayoutFrame
*>(pTmp
->Lower())->Lower();
1322 ++nNextCol
; // Empty columns require column breaks
1324 if( pTmp
!= GetUpper()->GetUpper() ) // Did we end up in another column?
1330 while( pFrame
->GetNext() )
1331 pFrame
= pFrame
->GetNext();
1335 while( pFrame
->GetNext() && pFrame
->getFrameArea().Bottom() < rFill
.Y() )
1336 pFrame
= pFrame
->GetNext();
1338 // No filling, if the last frame in the targeted column does
1339 // not contain a paragraph, but e.g. a table
1340 if( pFrame
->IsTextFrame() )
1342 rFill
.Fill().nColumnCnt
= nNextCol
;
1343 rFill
.bColumn
= true;
1346 SwTextFrame
const*const pTextFrame(static_cast<const SwTextFrame
*>(pFrame
));
1347 *rFill
.pPos
= pTextFrame
->MapViewToModelPos(
1348 TextFrameIndex(pTextFrame
->GetText().getLength()));
1352 rFill
.aFrame
= pTmp
->getFramePrintArea();
1353 rFill
.aFrame
+= pTmp
->getFrameArea().Pos();
1356 rFill
.aFrame
= pFrame
->getFrameArea();
1357 static_cast<const SwTextFrame
*>(pFrame
)->FillCursorPos( rFill
);
1362 std::unique_ptr
<SwFont
> pFnt
;
1363 SwTextFormatColl
* pColl
= GetTextNodeForParaProps()->GetTextColl();
1364 SwTwips nFirst
= GetTextNodeForParaProps()->GetSwAttrSet().GetULSpace().GetLower();
1365 SwTwips nDiff
= rFill
.Y() - getFrameArea().Bottom();
1366 if( nDiff
< nFirst
)
1369 pColl
= &pColl
->GetNextTextFormatColl();
1370 SwAttrSet
aSet(const_cast<SwDoc
&>(GetDoc()).GetAttrPool(), aTextFormatCollSetRange
);
1371 const SwAttrSet
* pSet
= &pColl
->GetAttrSet();
1372 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
1373 if (GetTextNodeForParaProps()->HasSwAttrSet())
1375 // sw_redlinehide: pSet is mostly used for para props, but there are
1376 // accesses to char props via pFnt - why does it use only the node's
1377 // props for this, and not hints?
1378 aSet
.Put( *GetTextNodeForParaProps()->GetpSwAttrSet() );
1379 aSet
.SetParent( pSet
);
1381 pFnt
.reset(new SwFont( pSet
, &GetDoc().getIDocumentSettingAccess() ));
1385 SwFontAccess
aFontAccess( pColl
, pSh
);
1386 pFnt
.reset(new SwFont( aFontAccess
.Get()->GetFont() ));
1387 pFnt
->CheckFontCacheId( pSh
, pFnt
->GetActual() );
1389 OutputDevice
* pOut
= pSh
->GetOut();
1390 if( !pSh
->GetViewOptions()->getBrowseMode() || pSh
->GetViewOptions()->IsPrtFormat() )
1391 pOut
= GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
1393 pFnt
->SetFntChg( true );
1394 pFnt
->ChgPhysFnt( pSh
, *pOut
);
1396 SwTwips nLineHeight
= pFnt
->GetHeight( pSh
, *pOut
);
1402 const SvxULSpaceItem
&rUL
= pSet
->GetULSpace();
1403 SwTwips nDist
= std::max( rUL
.GetLower(), rUL
.GetUpper() );
1404 if( rFill
.Fill().nColumnCnt
)
1406 rFill
.aFrame
.Height( nLineHeight
);
1407 nDiff
= rFill
.Y() - rFill
.Bottom();
1410 else if( nDist
< nFirst
)
1411 nFirst
= nFirst
- nDist
;
1414 nDist
= std::max( nDist
, SwTwips(GetLineSpace()) );
1415 nDist
+= nLineHeight
;
1421 rFill
.Fill().nParaCnt
= o3tl::narrowing
<sal_uInt16
>(nDiff
+ 1);
1422 rFill
.nLineWidth
= 0;
1423 rFill
.bInner
= false;
1424 rFill
.bEmpty
= true;
1425 rFill
.SetOrient( text::HoriOrientation::LEFT
);
1433 const SvxTabStopItem
&rRuler
= pSet
->GetTabStops();
1434 const SvxFirstLineIndentItem
& rFirstLine(pSet
->GetFirstLineIndent());
1435 const SvxTextLeftMarginItem
& rTextLeftMargin(pSet
->GetTextLeftMargin());
1436 const SvxRightMarginItem
& rRightMargin(pSet
->GetRightMargin());
1438 SwRect
&rRect
= rFill
.Fill().aCursor
;
1439 rRect
.Top( rFill
.Bottom() + (nDiff
+1) * nDist
- nLineHeight
);
1440 if( nFirst
&& nDiff
> -1 )
1441 rRect
.Top( rRect
.Top() + nFirst
);
1442 rRect
.Height( nLineHeight
);
1443 SwTwips nLeft
= rFill
.Left() + rTextLeftMargin
.GetLeft(rFirstLine
) +
1444 GetTextNodeForParaProps()->GetLeftMarginWithNum();
1445 SwTwips nRight
= rFill
.Right() - rRightMargin
.GetRight();
1446 SwTwips nCenter
= ( nLeft
+ nRight
) / 2;
1447 rRect
.Left( nLeft
);
1448 if( SwFillMode::Margin
== rFill
.Mode() )
1452 rFill
.SetOrient( text::HoriOrientation::LEFT
);
1453 if( rFill
.X() < nCenter
)
1455 if( rFill
.X() > ( nLeft
+ 2 * nCenter
) / 3 )
1457 rFill
.SetOrient( text::HoriOrientation::CENTER
);
1458 rRect
.Left( nCenter
);
1461 else if( rFill
.X() > ( nRight
+ 2 * nCenter
) / 3 )
1463 rFill
.SetOrient( text::HoriOrientation::RIGHT
);
1464 rRect
.Left( nRight
);
1468 rFill
.SetOrient( text::HoriOrientation::CENTER
);
1469 rRect
.Left( nCenter
);
1478 if( SwFillMode::Tab
!= rFill
.Mode() )
1480 SwDrawTextInfo
aDrawInf( pSh
, *pOut
, " ", 0, 2 );
1481 nSpace
= pFnt
->GetTextSize_( aDrawInf
).Width()/2;
1483 if( rFill
.X() >= nRight
)
1485 if( SwFillMode::Indent
!= rFill
.Mode() && ( rFill
.bEmpty
||
1486 rFill
.X() > rFill
.nLineWidth
+ FILL_MIN_DIST
) )
1488 rFill
.SetOrient( text::HoriOrientation::RIGHT
);
1489 rRect
.Left( nRight
);
1494 else if( SwFillMode::Indent
== rFill
.Mode() )
1496 SwTwips nIndent
= rFill
.X();
1497 if( !rFill
.bEmpty
|| nIndent
> nRight
)
1501 nIndent
-= rFill
.Left();
1502 if( nIndent
>= 0 && nSpace
)
1506 rFill
.SetTab( sal_uInt16( nIndent
) );
1507 rRect
.Left( nIndent
+ rFill
.Left() );
1513 else if( rFill
.X() > nLeft
)
1515 SwTwips nTextLeft
= rFill
.Left() + rTextLeftMargin
.GetTextLeft() +
1516 GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
1517 rFill
.nLineWidth
+= rFill
.bFirstLine
? nLeft
: nTextLeft
;
1519 SwTwips nRightTab
= nLeft
;
1520 sal_uInt16 nSpaceCnt
= 0;
1521 sal_uInt16 nSpaceOnlyCnt
= 0;
1522 sal_uInt16 nTabCnt
= 0;
1523 sal_uInt16 nIdx
= 0;
1526 nLeftTab
= nRightTab
;
1527 if( nIdx
< rRuler
.Count() )
1529 const SvxTabStop
&rTabStop
= rRuler
.operator[](nIdx
);
1530 nRightTab
= nTextLeft
+ rTabStop
.GetTabPos();
1531 if( nLeftTab
< nTextLeft
&& nRightTab
> nTextLeft
)
1532 nRightTab
= nTextLeft
;
1535 if( nRightTab
> rFill
.nLineWidth
)
1540 const SvxTabStopItem
& rTab
=
1541 pSet
->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP
);
1542 const SwTwips nDefTabDist
= rTab
[0].GetTabPos();
1543 nRightTab
= nLeftTab
- nTextLeft
;
1544 nRightTab
/= nDefTabDist
;
1545 nRightTab
= nRightTab
* nDefTabDist
+ nTextLeft
;
1546 while ( nRightTab
<= nLeftTab
)
1547 nRightTab
+= nDefTabDist
;
1548 if( nRightTab
> rFill
.nLineWidth
)
1550 while ( nRightTab
< rFill
.X() )
1552 nRightTab
+= nDefTabDist
;
1553 if( nRightTab
> rFill
.nLineWidth
)
1556 if( nLeftTab
< nRightTab
- nDefTabDist
)
1557 nLeftTab
= nRightTab
- nDefTabDist
;
1559 if( nRightTab
> nRight
)
1562 while( rFill
.X() > nRightTab
);
1564 if( SwFillMode::TabSpace
== rFill
.Mode() )
1569 nLeftTab
= rFill
.nLineWidth
;
1570 while( nLeftTab
< rFill
.X() )
1580 if( rFill
.X() - nLeftTab
> nRightTab
- rFill
.X() )
1584 rRect
.Left( nRightTab
);
1588 if( rFill
.X() - nLeftTab
> nSpace
/2 )
1591 rRect
.Left( nLeftTab
+ nSpace
);
1594 rRect
.Left( nLeftTab
);
1597 else if( rFill
.X() - nLeftTab
< nRightTab
- rFill
.X() )
1598 rRect
.Left( nLeftTab
);
1601 if( nRightTab
>= nRight
)
1603 rFill
.SetOrient( text::HoriOrientation::RIGHT
);
1604 rRect
.Left( nRight
);
1610 rRect
.Left( nRightTab
);
1615 else if( SwFillMode::Space
== rFill
.Mode() )
1617 SwTwips nLeftSpace
= nLeft
;
1618 while( nLeftSpace
< rFill
.X() )
1620 nLeftSpace
+= nSpace
;
1623 rRect
.Left( nLeftSpace
);
1627 if( rFill
.X() - nLeftTab
< nRightTab
- rFill
.X() )
1628 rRect
.Left( nLeftTab
);
1631 if( nRightTab
>= nRight
)
1633 rFill
.SetOrient( text::HoriOrientation::RIGHT
);
1634 rRect
.Left( nRight
);
1640 rRect
.Left( nRightTab
);
1645 rFill
.SetTab( nTabCnt
);
1646 rFill
.SetSpace( nSpaceCnt
);
1647 rFill
.SetSpaceOnly( nSpaceOnlyCnt
);
1650 if( std::abs( rFill
.X() - nCenter
) <=
1651 std::abs( rFill
.X() - rRect
.Left() ) )
1653 rFill
.SetOrient( text::HoriOrientation::CENTER
);
1655 rFill
.SetSpace( 0 );
1656 rFill
.SetSpaceOnly( 0 );
1657 rRect
.Left( nCenter
);
1660 rFill
.nLineWidth
+= FILL_MIN_DIST
;
1661 if( rRect
.Left() < rFill
.nLineWidth
)
1666 // Do we extend over the page's/column's/etc. lower edge?
1667 const SwFrame
* pUp
= GetUpper();
1668 if( pUp
->IsInSct() )
1670 if( pUp
->IsSctFrame() )
1671 pUp
= pUp
->GetUpper();
1672 else if( pUp
->IsColBodyFrame() &&
1673 pUp
->GetUpper()->GetUpper()->IsSctFrame() )
1674 pUp
= pUp
->GetUpper()->GetUpper()->GetUpper();
1676 SwRectFnSet
aRectFnSet(this);
1677 SwTwips nLimit
= aRectFnSet
.GetPrtBottom(*pUp
);
1678 SwTwips nRectBottom
= rRect
.Bottom();
1679 if ( aRectFnSet
.IsVert() )
1680 nRectBottom
= SwitchHorizontalToVertical( nRectBottom
);
1682 if( aRectFnSet
.YDiff( nLimit
, nRectBottom
) < 0 )
1688 const_cast<SwCursorMoveState
*>(rFill
.pCMS
)->m_bFillRet
= bFill
;
1691 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */