/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* * - SurvivalKit: For how long do we get past the last char of the line. * - RightMargin abstains from adjusting position with -1 * - GetCharRect returns a GetEndCharRect for CursorMoveState::RightMargin * - GetEndCharRect sets bRightMargin to true * - SwTextCursor::bRightMargin is set to false by CharCursorToLine
*/
/* * GetCharRect() returns the char's char line described by aPos. * GetModelPositionForViewPoint() does the reverse: It goes from a document coordinate to * a Pam. * Both are virtual in the frame base class and thus are redefined here.
*/
// Find the right frame first. We need to keep in mind that: // - the cached information could be invalid (GetPara() == 0) // - we could have a Follow // - the Follow chain grows dynamically; the one we end up in // needs to be formatted
bool bGoOn = true;
TextFrameIndex const nOffset = MapModelToViewPos(rPos);
assert(nOffset != TextFrameIndex(COMPLETE_STRING)); // not going to end well
TextFrameIndex nNextOfst;
do
{
{
SwTextSizeInfo aInf( pFrame );
SwTextCursor aLine( pFrame, &aInf );
nNextOfst = aLine.GetEnd(); // See comment in AdjustFrame // Include the line's last char? if (bRightMargin)
aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY ); else
aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
bRet = true;
}
if ( pFrame->IsRightToLeft() )
pFrame->SwitchLTRtoRTL( rOrig );
if ( aRectFnSet.IsVert() )
pFrame->SwitchHorizontalToVertical( rOrig );
// We have the following situation: if the frame is in an invalid // sectionframe, it's possible that the frame is outside the page. // If we restrict the cursor position to the page area, we enforce // the formatting of the page, of the section frame and the frame itself. if( aRectFnSet.YDiff( nPageTop, nOrigTop ) > 0 )
aRectFnSet.SetTop( rOrig, nPageTop );
if ( pFrame->IsRightToLeft() )
pFrame->SwitchLTRtoRTL( rOrig );
if ( aRectFnSet.IsVert() )
pFrame->SwitchHorizontalToVertical( rOrig );
returntrue;
}
}
/** determine top of line for given position in the text frame
- Top of first paragraph line is the top of the printing area of the text frame - If a proportional line spacing is applied use top of anchor character as top of the line.
*/ bool SwTextFrame::GetTopOfLine( SwTwips& _onTopOfLine, const SwPosition& _rPos ) const
{ bool bRet = true;
// get position offset
TextFrameIndex const nOffset = MapModelToViewPos(_rPos);
if (TextFrameIndex(GetText().getLength()) < nOffset)
{
bRet = false;
} else
{
SwRectFnSet aRectFnSet(this); if ( IsEmpty() || !aRectFnSet.GetHeight(getFramePrintArea()) )
{ // consider upper space amount considered // for previous frame and the page grid.
_onTopOfLine = aRectFnSet.GetPrtTop(*this);
} else
{ // determine formatted text frame that contains the requested position
SwTextFrame* pFrame = &(const_cast<SwTextFrame*>(this)->GetFrameAtOfst( nOffset ));
pFrame->GetFormatted();
aRectFnSet.Refresh(pFrame); // If proportional line spacing is applied // to the text frame, the top of the anchor character is also the // top of the line. // Otherwise the line layout determines the top of the line const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing(); if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
{
SwRect aCharRect; if ( GetAutoPos( aCharRect, _rPos ) )
{
_onTopOfLine = aRectFnSet.GetTop(aCharRect);
} else
{
bRet = false;
}
} else
{ // assure that text frame is in a horizontal layout
SwFrameSwapper aSwapper( pFrame, true ); // determine text line that contains the requested position
SwTextSizeInfo aInf( pFrame );
SwTextCursor aLine( pFrame, &aInf );
aLine.CharCursorToLine( nOffset ); // determine top of line
_onTopOfLine = aLine.Y(); if ( aRectFnSet.IsVert() )
{
_onTopOfLine = pFrame->SwitchHorizontalToVertical( _onTopOfLine );
}
}
}
}
return bRet;
}
// Minimum distance of non-empty lines is a little less than 2 cm #define FILL_MIN_DIST 1100
bool SwTextFrame::GetModelPositionForViewPoint_(SwPosition* pPos, const Point& rPoint, constbool bChgFrame, SwCursorMoveState* pCMS ) const
{ // GetModelPositionForViewPoint_ is called by GetModelPositionForViewPoint and GetKeyCursorOfst. // Never just a return false.
if( IsLocked() || IsHiddenNow() ) returnfalse;
const_cast<SwTextFrame*>(this)->GetFormatted();
Point aOldPoint( rPoint );
if ( IsVertical() )
{
SwitchVerticalToHorizontal( const_cast<Point&>(rPoint) ); const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
}
if ( IsRightToLeft() )
SwitchRTLtoLTR( const_cast<Point&>(rPoint) );
// pPos is a pure IN parameter and must not be evaluated. // pIter->GetModelPositionForViewPoint returns from a nesting with COMPLETE_STRING. // If SwTextIter::GetModelPositionForViewPoint calls GetModelPositionForViewPoint further by itself // nNode changes the position. // In such cases, pPos must not be calculated. if (TextFrameIndex(COMPLETE_STRING) != nOffset)
{
*pPos = MapViewToModelPos(nOffset); if( pFillData )
{ if (TextFrameIndex(GetText().getLength()) > nOffset ||
rPoint.Y() < getFrameArea().Top() )
pFillData->bInner = true;
pFillData->bFirstLine = aLine.GetLineNr() < 2; if (GetText().getLength())
{
pFillData->bEmpty = false;
pFillData->nLineWidth = aLine.GetCurr()->Width();
}
}
}
} bool bChgFillData = false; if( pFillData && FindPageFrame()->getFrameArea().Contains( aOldPoint ) )
{
FillCursorPos( *pFillData );
bChgFillData = true;
}
if ( IsVertical() )
{ if ( bChgFillData )
SwitchHorizontalToVertical( pFillData->Fill().aCursor.Pos() ); const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
}
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
SwTextCursor::IsRightMargin() );
pFrame->GetFormatted(); if (!IsEmpty())
{
SwTextSizeInfo aInf( pFrame );
SwTextCursor aLine( pFrame, &aInf );
TextFrameIndex const nCursorPos(MapModelToViewPos(*pPam->GetPoint()));
aLine.CharCursorToLine(nCursorPos); if ( aLine.GetCurr()->IsEndHyph() )
{
TextFrameIndex nPos(aLine.GetStart() + aLine.GetCurr()->GetLen()); while( nPos > nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] )
--nPos; if ( nPos == nCursorPos && ( bSelection || // without selection, the cursor must be inside the word, not before that // to apply the character formatting, as usual
( nPos > aLine.GetStart() && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] ) ) ) returntrue;
} // the hyphenated word starts in the previous line if ( aLine.GetStart() > TextFrameIndex(0) )
{
TextFrameIndex nPos(aLine.GetStart());
aLine.CharCursorToLine(nPos - TextFrameIndex(1)); if ( aLine.GetCurr()->IsEndHyph() )
{ while( nPos < nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos)] )
++nPos; if ( nPos == nCursorPos &&
( bSelection || ' ' != aInf.GetText()[sal_Int32(nPos)] ) ) returntrue;
}
}
} returnfalse;
}
/* * To the line end: That's the position before the last char of the line. * Exception: In the last line, it should be able to place the cursor after * the last char in order to append text.
*/
// The following two methods try to put the Cursor into the next/successive // line. If we do not have a preceding/successive line we forward the call // to the base class. // The Cursor's horizontal justification is done afterwards by the CursorShell.
bool SwTextFrame::UnitUp_( SwPaM *pPam, const SwTwips nOffset, bool bSetInReadOnly ) const
{ // Set the RightMargin if needed
SwSetToRightMargin aSet;
if( IsInTab() &&
pPam->GetPointNode().StartOfSectionNode() !=
pPam->GetMarkNode().StartOfSectionNode() )
{ // If the PaM is located within different boxes, we have a table selection, // which is handled by the base class. return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
}
// We select the target line for the cursor, in case we are in a // double line portion, prev line = curr line if( bPrevLine && !bSecondOfDouble )
{
aLine.PrevLine(); while ( aLine.GetStart() == nStart &&
nullptr != ( pPrevLine = aLine.GetPrevLine() ) &&
pPrevLine != aLine.GetCurr() )
aLine.PrevLine();
}
// See comment in SwTextFrame::GetModelPositionForViewPoint() #if OSL_DEBUG_LEVEL > 0 const SwNodeOffset nOldNode = pPam->GetPoint()->GetNodeIndex(); #endif // The node should not be changed
TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint(pPam->GetPoint(),
aCharBox.Pos(), false ); #if OSL_DEBUG_LEVEL > 0
OSL_ENSURE( nOldNode == pPam->GetPoint()->GetNodeIndex(), "SwTextFrame::UnitUp: illegal node change" ); #endif
// We make sure that we move up. if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
{
nTmpOfst = nStart;
aSet.SetRight( true );
}
*pPam->GetPoint() = MapViewToModelPos(nTmpOfst); returntrue;
}
if ( IsFollow() )
{
aLine.GetCharRect( &aCharBox, nPos );
aCharBox.Width( aCharBox.SSize().Width() / 2 );
} break;
} while ( true );
} /* If 'this' is a follow and a prev failed, we need to go to the * last line of the master, which is us. * Or: If we are a follow with follow, we need to get the master.
*/ if ( IsFollow() )
{ const SwTextFrame *pTmpPrev = FindMaster();
TextFrameIndex nOffs = GetOffset(); if( pTmpPrev )
{
SwViewShell *pSh = getRootFrame()->GetCurrShell(); constbool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea(); const SwTextFrame *pPrevPrev = pTmpPrev; // We skip protected frames and frames without content here while( pPrevPrev && ( pPrevPrev->GetOffset() == nOffs ||
( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) )
{
pTmpPrev = pPrevPrev;
nOffs = pTmpPrev->GetOffset(); if ( pPrevPrev->IsFollow() )
pPrevPrev = pTmpPrev->FindMaster(); else
pPrevPrev = nullptr;
} if ( !pPrevPrev ) return pTmpPrev->SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
aCharBox.Pos().setY( pPrevPrev->getFrameArea().Bottom() - 1 ); return pPrevPrev->GetKeyCursorOfst( pPam->GetPoint(), aCharBox.Pos() );
}
} return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
}
// Used for Bidi. nPos is the logical position in the string, bLeft indicates // if left arrow or right arrow was pressed. The return values are: // nPos: the new visual position // bLeft: whether the break iterator has to add or subtract from the // current position staticvoid lcl_VisualMoveRecursion(const SwLineLayout& rCurrLine, TextFrameIndex nIdx,
TextFrameIndex & nPos, bool& bRight,
sal_uInt8& nCursorLevel, sal_uInt8 nDefaultDir )
{ const SwLinePortion* pPor = rCurrLine.GetFirstPortion(); const SwLinePortion* pLast = nullptr;
// What's the current portion? while ( pPor && nIdx + pPor->GetLen() <= nPos )
{
nIdx = nIdx + pPor->GetLen();
pLast = pPor;
pPor = pPor->GetNextPortion();
}
// 1. special case: at beginning of bidi portion if ( bRecurse && nIdx == nPos )
{
nPos = nPos + pPor->GetLen();
// leave bidi portion if ( nCursorLevel != nDefaultDir )
{
bRecurse = false;
} else // special case: // buffer: abcXYZ123 in LTR paragraph // view: abc123ZYX // cursor is between c and X in the buffer and cursor level = 0
nCursorLevel++;
}
// 2. special case: at beginning of portion after bidi portion elseif ( pLast && pLast->IsMultiPortion() && static_cast<const SwMultiPortion*>(pLast)->IsBidi() && nIdx == nPos )
{ // enter bidi portion if ( nCursorLevel != nDefaultDir )
{
bRecurse = true;
nIdx = nIdx - pLast->GetLen();
pPor = pLast;
}
}
// We have to distinguish between an insert and overwrite cursor: // The insert cursor position depends on the cursor level: // buffer: abcXYZdef in LTR paragraph // display: abcZYXdef // If cursor is between c and X in the buffer and cursor level is 0, // the cursor blinks between c and Z and -> sets the cursor between Z and Y. // If the cursor level is 1, the cursor blinks between X and d and // -> sets the cursor between d and e. // The overwrite cursor simply travels to the next visual character. if ( bInsertCursor )
{
lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward,
nCursorLevel, IsRightToLeft() ? 1 : 0 ); return;
}
if ( IsInTab() &&
pPam->GetPointNode().StartOfSectionNode() !=
pPam->GetMarkNode().StartOfSectionNode() )
{ // If the PaM is located within different boxes, we have a table selection, // which is handled by the base class. return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
} const_cast<SwTextFrame*>(this)->GetFormatted(); const TextFrameIndex nPos = MapModelToViewPos(*pPam->GetPoint());
SwRect aCharBox; const SwContentFrame *pTmpFollow = nullptr;
if ( IsVertical() ) const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
if ( !IsEmpty() && !IsHiddenNow() )
{
TextFrameIndex nFormat(COMPLETE_STRING); do
{ if (nFormat != TextFrameIndex(COMPLETE_STRING) && !IsFollow() &&
!sw_ChangeOffset( const_cast<SwTextFrame*>(this), nFormat ) ) break;
if ( IsVertical() ) const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
// We take a shortcut for follows if( pTmpFollow )
{
aCharBox.Pos().setY( pTmpFollow->getFrameArea().Top() + 1 ); returnstatic_cast<const SwTextFrame*>(pTmpFollow)->GetKeyCursorOfst( pPam->GetPoint(),
aCharBox.Pos() );
} return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
}
bool SwTextFrame::UnitUp(SwPaM *pPam, const SwTwips nOffset, bool bSetInReadOnly ) const
{ /* We call ContentNode::GertFrame() in CursorSh::Up(). * This _always returns the master. * In order to not mess with cursor travelling, we correct here * in SwTextFrame. * We calculate UnitUp for pFrame. pFrame is either a master (= this) or a * follow (!= this).
*/ const SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *(pPam->GetPoint()),
SwTextCursor::IsRightMargin() ); constbool bRet = pFrame->UnitUp_( pPam, nOffset, bSetInReadOnly );
// No SwTextCursor::SetRightMargin( false ); // Instead we have a SwSetToRightMargin in UnitUp_ return bRet;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.