android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / frmcrsr.cxx
blobd7f7a2cab99e67bfa446d36b95ac70ce7ba94396
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <ndtxt.hxx>
21 #include <pam.hxx>
22 #include <frmatr.hxx>
23 #include <frmtool.hxx>
24 #include <viewopt.hxx>
25 #include <paratr.hxx>
26 #include <rootfrm.hxx>
27 #include <pagefrm.hxx>
28 #include <colfrm.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"
35 #include <doc.hxx>
36 #include <IDocumentDeviceAccess.hxx>
37 #include <sortedobjs.hxx>
39 #include <unicode/ubidi.h>
41 #include <txtfrm.hxx>
42 #include "inftxt.hxx"
43 #include "itrtxt.hxx"
44 #include <crstate.hxx>
45 #include <viewsh.hxx>
46 #include <swfntcch.hxx>
47 #include <flyfrm.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
61 namespace
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);
80 else
81 nNew -= TextFrameIndex(MIN_OFFSET_STEP);
82 sw_ChangeOffset( pFrameAtPos, nNew );
85 while( pFrame != pFrameAtPos )
87 pFrame = pFrameAtPos;
88 pFrame->GetFormatted();
89 pFrameAtPos = pFrame->GetFrameAtPos( rPos );
92 if( nOffset && bRightMargin )
94 while (pFrameAtPos &&
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();
122 if( pVsh )
124 if( pVsh->GetRingContainer().size() > 1 ||
125 ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() ) )
127 if( !pFrame->GetOffset() )
128 return false;
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() );
136 return true;
140 return false;
143 SwTextFrame& SwTextFrame::GetFrameAtOfst(TextFrameIndex const nWhere)
145 SwTextFrame* pRet = this;
146 while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOffset() )
147 pRet = pRet->GetFollow();
148 return *pRet;
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();
159 else
161 if (nPos == pFoll->GetFollow()->GetOffset()
162 && !SwTextCursor::IsRightMargin() )
163 pFoll = pFoll->GetFollow();
164 else
165 break;
168 return pFoll;
172 * GetCharRect() returns the char's char line described by aPos.
173 * GetModelPositionForViewPoint() does the reverse: It goes from a document coordinate to
174 * a Pam.
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" );
183 if (IsLocked())
184 return false;
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,
196 bNoScroll );
197 pFrame->GetFormatted();
199 const SwFrame* pTmpFrame = pFrame->GetUpper();
200 if (pTmpFrame->getFrameArea().Top() == FAR_AWAY && !bAllowFarAway)
201 return false;
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 );
212 bool bRet = false;
214 if ( pFrame->IsEmpty() || ! aRectFnSet.GetHeight(pFrame->getFramePrintArea()) )
216 Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
217 SwTextNode const*const pTextNd(GetTextNodeForParaProps());
218 short nFirstOffset;
219 pTextNd->GetFirstLineOfsWithNum( nFirstOffset );
221 Point aPnt2;
222 if ( aRectFnSet.IsVert() )
224 if( nFirstOffset > 0 )
225 aPnt1.AdjustY(nFirstOffset );
226 if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
227 aPnt1.setX( nMaxY );
228 aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
229 aPnt2.setY( aPnt1.Y() );
230 if( aPnt2.X() < nMaxY )
231 aPnt2.setX( nMaxY );
233 else
235 if( nFirstOffset > 0 )
236 aPnt1.AdjustX(nFirstOffset );
238 if( aPnt1.Y() > nMaxY )
239 aPnt1.setY( nMaxY );
240 aPnt2.setX( aPnt1.X() );
241 aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
242 if( aPnt2.Y() > nMaxY )
243 aPnt2.setY( nMaxY );
246 rOrig = SwRect( aPnt1, aPnt2 );
248 if ( pCMS )
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 );
257 bRet = true;
259 else
261 if( !pFrame->HasPara() )
262 return false;
264 SwFrameSwapper aSwapper( pFrame, true );
265 if ( aRectFnSet.IsVert() )
266 nMaxY = pFrame->SwitchVerticalToHorizontal( nMaxY );
268 bool bGoOn = true;
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?
281 if (bRightMargin)
282 aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY );
283 else
284 aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
285 bRet = true;
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 );
302 else
303 bGoOn = false;
304 } while ( bGoOn );
306 if ( pCMS )
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 );
339 if( bRet )
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 );
358 return bRet;
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
368 if( IsHiddenNow() )
369 return false;
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
381 SwTwips nMaxY;
382 if ( aRectFnSet.IsVert() )
384 if ( aRectFnSet.IsVertL2R() )
385 nMaxY = std::min( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
386 else
387 nMaxY = std::max( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
389 else
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();
394 Point aPnt2;
395 if ( aRectFnSet.IsVert() )
397 if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
398 aPnt1.setX( nMaxY );
400 aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
401 aPnt2.setY( aPnt1.Y() );
402 if( aPnt2.X() < nMaxY )
403 aPnt2.setX( nMaxY );
405 else
407 if( aPnt1.Y() > nMaxY )
408 aPnt1.setY( nMaxY );
409 aPnt2.setX( aPnt1.X() );
410 aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
411 if( aPnt2.Y() > nMaxY )
412 aPnt2.setY( nMaxY );
414 rOrig = SwRect( aPnt1, aPnt2 );
415 return true;
417 else
419 if( !pFrame->HasPara() )
420 return false;
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 );
443 return true;
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
451 top of the line.
453 bool SwTextFrame::GetTopOfLine( SwTwips& _onTopOfLine,
454 const SwPosition& _rPos ) const
456 bool bRet = true;
458 // get position offset
459 TextFrameIndex const nOffset = MapModelToViewPos(_rPos);
461 if (TextFrameIndex(GetText().getLength()) < nOffset)
463 bRet = false;
465 else
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);
474 else
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
482 // top of the line.
483 // Otherwise the line layout determines the top of the line
484 const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing();
485 if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
487 SwRect aCharRect;
488 if ( GetAutoPos( aCharRect, _rPos ) )
490 _onTopOfLine = aRectFnSet.GetTop(aCharRect);
492 else
494 bRet = false;
497 else
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 );
515 return bRet;
518 // Minimum distance of non-empty lines is a little less than 2 cm
519 #define FILL_MIN_DIST 1100
521 struct SwFillData
523 SwRect aFrame;
524 const SwCursorMoveState *pCMS;
525 SwPosition* pPos;
526 const Point& rPoint;
527 SwTwips nLineWidth;
528 bool bFirstLine : 1;
529 bool bInner : 1;
530 bool bColumn : 1;
531 bool bEmpty : 1;
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 ),
535 bEmpty( true ){}
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() )
556 return false;
558 const_cast<SwTextFrame*>(this)->GetFormatted();
560 Point aOldPoint( rPoint );
562 if ( IsVertical() )
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 ));
575 if ( IsEmpty() )
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;
585 else
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 )
595 if( !aLine.Prev() )
596 break;
599 if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr()
600 && rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() )
601 while( aLine.GetLineNr() > 1 )
602 aLine.Prev();
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);
617 if( pFillData )
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 );
635 bChgFillData = true;
638 if ( IsVertical() )
640 if ( bChgFillData )
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;
658 return true;
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);
682 else
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 );
697 return true;
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);
714 if (!IsEmpty())
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])
725 --nRightMargin;
726 else if( !bAPI && (aLine.GetNext() || pFrame->GetFollow()) )
728 while( nRightMargin > aLine.GetStart() &&
729 ' ' == aInf.GetText()[sal_Int32(nRightMargin) - 1])
730 --nRightMargin;
733 *pPam->GetPoint() = pFrame->MapViewToModelPos(nRightMargin);
734 SwTextCursor::SetRightMargin( !bAPI );
735 return true;
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.
743 namespace {
745 class SwSetToRightMargin
747 bool m_bRight;
749 public:
750 SwSetToRightMargin()
751 : m_bRight(false)
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;
766 if( IsInTab() &&
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());
777 SwRect aCharBox;
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()
791 if( nPos )
792 aLine.CharCursorToLine( nPos );
793 else
794 aLine.Top();
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();
807 if( !nDiff )
808 nDiff = TextFrameIndex(MIN_OFFSET_STEP);
809 if( nFormat > nDiff )
810 nFormat = nFormat - nDiff;
811 else
812 nFormat = TextFrameIndex(0);
813 continue;
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 )
820 aLine.PrevLine();
821 while ( aLine.GetStart() == nStart &&
822 nullptr != ( pPrevLine = aLine.GetPrevLine() ) &&
823 pPrevLine != aLine.GetCurr() )
824 aLine.PrevLine();
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();
835 #endif
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" );
842 #endif
844 // We make sure that we move up.
845 if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
847 nTmpOfst = nStart;
848 aSet.SetRight( true );
850 *pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
851 return true;
854 if ( IsFollow() )
856 aLine.GetCharRect( &aCharBox, nPos );
857 aCharBox.Width( aCharBox.SSize().Width() / 2 );
859 break;
860 } while ( true );
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.
866 if ( IsFollow() )
868 const SwTextFrame *pTmpPrev = FindMaster();
869 TextFrameIndex nOffs = GetOffset();
870 if( pTmpPrev )
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();
883 else
884 pPrevPrev = nullptr;
886 if ( !pPrevPrev )
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
899 // current position
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();
911 pLast = pPor;
912 pPor = pPor->GetNextPortion();
915 if ( bRight )
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 )
928 bRecurse = false;
930 else
931 // special case:
932 // buffer: abcXYZ123 in LTR paragraph
933 // view: abc123ZYX
934 // cursor is between c and X in the buffer and cursor level = 0
935 nCursorLevel++;
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 )
945 bRecurse = true;
946 nIdx = nIdx - pLast->GetLen();
947 pPor = pLast;
951 // Recursion
952 if ( bRecurse )
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;
966 // go forward
967 else
969 bRight = true;
970 nCursorLevel = nDefaultDir;
974 else
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 )
984 bRecurse = false;
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 )
997 bRecurse = true;
998 nIdx = nIdx - pLast->GetLen();
999 pPor = pLast;
1001 // special case:
1002 // buffer: abcXYZ123 in LTR paragraph
1003 // view: abc123ZYX
1004 // cursor is behind 3 in the buffer and cursor level = 2
1005 if ( nDefaultDir + 2 == nCursorLevel )
1006 nPos = nPos + pLast->GetLen();
1010 // go forward
1011 if ( bRecurse )
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 );
1020 // special case:
1021 // buffer: abcXYZ123 in LTR paragraph
1022 // view: abc123ZYX
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;
1036 // go backward
1037 else
1039 bRight = false;
1040 nCursorLevel = nDefaultDir;
1045 void SwTextFrame::PrepareVisualMove(TextFrameIndex & nPos, sal_uInt8& nCursorLevel,
1046 bool& bForward, bool bInsertCursor )
1048 if( IsEmpty() || IsHiddenNow() )
1049 return;
1051 GetFormatted();
1053 SwTextSizeInfo aInf(this);
1054 SwTextCursor aLine(this, &aInf);
1056 if( nPos )
1057 aLine.CharCursorToLine( nPos );
1058 else
1059 aLine.Top();
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 );
1078 return;
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
1102 if ( bVisualRight )
1104 if (nTmpPos + TextFrameIndex(1) < nStt + nLen)
1105 ++nTmpPos;
1106 else
1108 nPos = nDefaultDir == UBIDI_RTL ? TextFrameIndex(0) : nStt + nLen;
1109 bOutOfBounds = true;
1112 else
1114 if ( nTmpPos )
1115 --nTmpPos;
1116 else
1118 nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : TextFrameIndex(0);
1119 bOutOfBounds = true;
1123 else
1125 nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - TextFrameIndex(1) : TextFrameIndex(0);
1128 if ( ! bOutOfBounds )
1130 nPos = TextFrameIndex(ubidi_getLogicalIndex( pBidi, sal_Int32(nTmpPos), &nError ));
1132 if ( bForward )
1134 if ( nPos )
1135 --nPos;
1136 else
1138 ++nPos;
1139 bForward = ! bForward;
1142 else
1143 ++nPos;
1146 ubidi_close( pBidi );
1149 bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
1150 bool bSetInReadOnly ) const
1153 if ( IsInTab() &&
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());
1163 SwRect aCharBox;
1164 const SwContentFrame *pTmpFollow = nullptr;
1166 if ( IsVertical() )
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 ) )
1176 break;
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();
1196 #endif
1197 if ( pNextLine && ! bFirstOfDouble )
1198 aLine.NextLine();
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" );
1205 #endif
1207 // We make sure that we move down.
1208 if( nTmpOfst <= nStart && ! bFirstOfDouble )
1209 nTmpOfst = nStart + TextFrameIndex(1);
1210 *pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
1212 if ( IsVertical() )
1213 const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
1215 return true;
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() )
1226 pTmp = pTmpFollow;
1227 pTmpFollow = pTmpFollow->GetFollow();
1230 if( !pTmpFollow ) // Only protected ones left
1232 if ( IsVertical() )
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),
1248 nTmpLen );
1249 if( nFormat <= GetOffset() )
1250 break;
1252 continue;
1255 break;
1256 } while( true );
1258 else
1259 pTmpFollow = GetFollow();
1261 if ( IsVertical() )
1262 const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
1264 // We take a shortcut for follows
1265 if( pTmpFollow )
1267 aCharBox.Pos().setY( pTmpFollow->getFrameArea().Top() + 1 );
1268 return static_cast<const SwTextFrame*>(pTmpFollow)->GetKeyCursorOfst( pPam->GetPoint(),
1269 aCharBox.Pos() );
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
1280 * in SwTextFrame.
1281 * We calculate UnitUp for pFrame. pFrame is either a master (= this) or a
1282 * follow (!= this).
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_
1290 return bRet;
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 );
1300 return bRet;
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();
1319 nNextCol = 0;
1321 else
1322 ++nNextCol; // Empty columns require column breaks
1324 if( pTmp != GetUpper()->GetUpper() ) // Did we end up in another column?
1326 if( !pFrame )
1327 return;
1328 if( nNextCol )
1330 while( pFrame->GetNext() )
1331 pFrame = pFrame->GetNext();
1333 else
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;
1344 if( rFill.pPos )
1346 SwTextFrame const*const pTextFrame(static_cast<const SwTextFrame*>(pFrame));
1347 *rFill.pPos = pTextFrame->MapViewToModelPos(
1348 TextFrameIndex(pTextFrame->GetText().getLength()));
1350 if( nNextCol )
1352 rFill.aFrame = pTmp->getFramePrintArea();
1353 rFill.aFrame += pTmp->getFrameArea().Pos();
1355 else
1356 rFill.aFrame = pFrame->getFrameArea();
1357 static_cast<const SwTextFrame*>(pFrame)->FillCursorPos( rFill );
1359 return;
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 )
1367 nDiff = -1;
1368 else
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 );
1380 pSet = &aSet;
1381 pFnt.reset(new SwFont( pSet, &GetDoc().getIDocumentSettingAccess() ));
1383 else
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 );
1398 bool bFill = false;
1399 if( nLineHeight )
1401 bFill = true;
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();
1408 nFirst = 0;
1410 else if( nDist < nFirst )
1411 nFirst = nFirst - nDist;
1412 else
1413 nFirst = 0;
1414 nDist = std::max( nDist, SwTwips(GetLineSpace()) );
1415 nDist += nLineHeight;
1416 nDiff -= nFirst;
1418 if( nDiff > 0 )
1420 nDiff /= nDist;
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 );
1427 else
1428 nDiff = -1;
1429 if( rFill.bInner )
1430 bFill = false;
1431 else
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() )
1450 if( rFill.bEmpty )
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 );
1466 else
1468 rFill.SetOrient( text::HoriOrientation::CENTER );
1469 rRect.Left( nCenter );
1472 else
1473 bFill = false;
1475 else
1477 SwTwips nSpace = 0;
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 );
1491 else
1492 bFill = false;
1494 else if( SwFillMode::Indent == rFill.Mode() )
1496 SwTwips nIndent = rFill.X();
1497 if( !rFill.bEmpty || nIndent > nRight )
1498 bFill = false;
1499 else
1501 nIndent -= rFill.Left();
1502 if( nIndent >= 0 && nSpace )
1504 nIndent /= nSpace;
1505 nIndent *= nSpace;
1506 rFill.SetTab( sal_uInt16( nIndent ) );
1507 rRect.Left( nIndent + rFill.Left() );
1509 else
1510 bFill = false;
1513 else if( rFill.X() > nLeft )
1515 SwTwips nTextLeft = rFill.Left() + rTextLeftMargin.GetTextLeft() +
1516 GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
1517 rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
1518 SwTwips nLeftTab;
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;
1533 else
1534 ++nIdx;
1535 if( nRightTab > rFill.nLineWidth )
1536 ++nTabCnt;
1538 else
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 )
1549 ++nTabCnt;
1550 while ( nRightTab < rFill.X() )
1552 nRightTab += nDefTabDist;
1553 if( nRightTab > rFill.nLineWidth )
1554 ++nTabCnt;
1556 if( nLeftTab < nRightTab - nDefTabDist )
1557 nLeftTab = nRightTab - nDefTabDist;
1559 if( nRightTab > nRight )
1560 nRightTab = nRight;
1562 while( rFill.X() > nRightTab );
1563 --nTabCnt;
1564 if( SwFillMode::TabSpace == rFill.Mode() )
1566 if( nSpace > 0 )
1568 if( !nTabCnt )
1569 nLeftTab = rFill.nLineWidth;
1570 while( nLeftTab < rFill.X() )
1572 nLeftTab += nSpace;
1573 ++nSpaceCnt;
1575 if( nSpaceCnt )
1577 nLeftTab -= nSpace;
1578 --nSpaceCnt;
1580 if( rFill.X() - nLeftTab > nRightTab - rFill.X() )
1582 nSpaceCnt = 0;
1583 ++nTabCnt;
1584 rRect.Left( nRightTab );
1586 else
1588 if( rFill.X() - nLeftTab > nSpace/2 )
1590 ++nSpaceCnt;
1591 rRect.Left( nLeftTab + nSpace );
1593 else
1594 rRect.Left( nLeftTab );
1597 else if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
1598 rRect.Left( nLeftTab );
1599 else
1601 if( nRightTab >= nRight )
1603 rFill.SetOrient( text::HoriOrientation::RIGHT );
1604 rRect.Left( nRight );
1605 nTabCnt = 0;
1606 nSpaceCnt = 0;
1608 else
1610 rRect.Left( nRightTab );
1611 ++nTabCnt;
1615 else if( SwFillMode::Space == rFill.Mode() )
1617 SwTwips nLeftSpace = nLeft;
1618 while( nLeftSpace < rFill.X() )
1620 nLeftSpace += nSpace;
1621 ++nSpaceOnlyCnt;
1623 rRect.Left( nLeftSpace );
1625 else
1627 if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
1628 rRect.Left( nLeftTab );
1629 else
1631 if( nRightTab >= nRight )
1633 rFill.SetOrient( text::HoriOrientation::RIGHT );
1634 rRect.Left( nRight );
1635 nTabCnt = 0;
1636 nSpaceCnt = 0;
1638 else
1640 rRect.Left( nRightTab );
1641 ++nTabCnt;
1645 rFill.SetTab( nTabCnt );
1646 rFill.SetSpace( nSpaceCnt );
1647 rFill.SetSpaceOnly( nSpaceOnlyCnt );
1648 if( bFill )
1650 if( std::abs( rFill.X() - nCenter ) <=
1651 std::abs( rFill.X() - rRect.Left() ) )
1653 rFill.SetOrient( text::HoriOrientation::CENTER );
1654 rFill.SetTab( 0 );
1655 rFill.SetSpace( 0 );
1656 rFill.SetSpaceOnly( 0 );
1657 rRect.Left( nCenter );
1659 if( !rFill.bEmpty )
1660 rFill.nLineWidth += FILL_MIN_DIST;
1661 if( rRect.Left() < rFill.nLineWidth )
1662 bFill = false;
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 )
1683 bFill = false;
1684 else
1685 rRect.Width( 1 );
1688 const_cast<SwCursorMoveState*>(rFill.pCMS)->m_bFillRet = bFill;
1691 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */