Revert "tdf#43100 tdf#104683 tdf#120715 sw: cursor on spaces over margin"

This reverts commit 7ef9c3ef30023cc40068e1f735aa4bec4811288b (plus relevant part
of c08d71a0e0015ec7857335b68a354df04fa04a0c "Fix typos" follow-up).  It started
to cause <https://ci.libreoffice.org/job/lo_ubsan/2441/> to fail with

> /sw/source/core/text/itrcrsr.cxx:1659:50: runtime error: downcast of address 0x60700059b250 which does not point to an object of type 'SwTextPortion'
> 0x60700059b250: note: object is of type 'SwHolePortion'
>  00 00 00 00  90 23 40 a7 6f 7f 00 00  e1 03 00 00 00 00 00 00  0d 01 00 00 00 00 00 00  00 00 00 00
>               ^~~~~~~~~~~~~~~~~~~~~~~
>               vptr for 'SwHolePortion'
>     #0 0x7f6f9f1777f0 in SwTextCursor::GetModelPositionForViewPoint(SwPosition*, Point const&, bool, SwCursorMoveState*) const /sw/source/core/text/itrcrsr.cxx:1659:50
>     #1 0x7f6f9efc2b39 in SwTextFrame::UnitDown_(SwPaM*, long, bool) const /sw/source/core/text/frmcrsr.cxx:1200:49
>     #2 0x7f6f9efc5b50 in SwTextFrame::UnitDown(SwPaM*, long, bool) const /sw/source/core/text/frmcrsr.cxx:1298:31
>     #3 0x7f6f9be4bac8 in SwCursor::UpDown(bool, unsigned short, Point const*, long, SwRootFrame&) /sw/source/core/crsr/swcrsr.cxx:2062:31
>     #4 0x7f6f9bf0692b in SwShellCursor::UpDown(bool, unsigned short) /sw/source/core/crsr/viscrs.cxx:1025:22
>     #5 0x7f6f9bb840c0 in SwCursorShell::UpDown(bool, unsigned short) /sw/source/core/crsr/crsrsh.cxx:511:29
>     #6 0x7f6fa37f131d in SwCursorShell::Down(unsigned short) /sw/inc/crsrsh.hxx:359:50
>     #7 0x7f6fa37dcfc9 in SwWrtShell::Down(bool, unsigned short, bool) /sw/source/uibase/wrtsh/move.cxx:171:27
>     #8 0x7f6fb6aa1325 in testTdf43100_CursorMoveToSpacesOverMargin::TestBody() /sw/qa/core/text/text.cxx:532:20

during CppunitTest_sw_core_text
CPPUNIT_TEST_NAME=testTdf43100_CursorMoveToSpacesOverMargin::TestBody

Change-Id: I37947825ec9db826446ed28fa87a23ee60749b82
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136549
Reviewed-by: László Németh <nemeth@numbertext.org>
Tested-by: Jenkins
diff --git a/sw/qa/core/text/data/tdf43100_tdf120715_cursorOnSpacesOverMargin.docx b/sw/qa/core/text/data/tdf43100_tdf120715_cursorOnSpacesOverMargin.docx
deleted file mode 100644
index 474d805..0000000
--- a/sw/qa/core/text/data/tdf43100_tdf120715_cursorOnSpacesOverMargin.docx
+++ /dev/null
Binary files differ
diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx
index 99d80f6..2db4d6d 100644
--- a/sw/qa/core/text/text.cxx
+++ b/sw/qa/core/text/text.cxx
@@ -470,74 +470,6 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testRedlineDelete)
                         pDoc->getIDocumentRedlineAccess().GetRedlineTable().size());
}

CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf120715_CursorMoveWhenTypingSpaceAtCenteredLineEnd)
{
    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf43100_tdf120715_cursorOnSpacesOverMargin.docx");
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();

    // Make a paint to force the call of AddExtraBlankWidth, that calculate width for holePortions.
    pDoc->GetDocShell()->GetPreviewBitmap();

    // Move the cursor to the last character of the document.
    pWrtShell->EndOfSection();

    //Press space and check if the cursor move right with the additional space.
    sal_Int32 nOldCursorPos = pWrtShell->GetCharRect().Left();
    pWrtShell->Insert(" ");
    sal_Int32 nNewCursorPos = pWrtShell->GetCharRect().Left();
    CPPUNIT_ASSERT_GREATER(nOldCursorPos, nNewCursorPos);
}

CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf43100_CursorMoveToSpacesOverMargin)
{
    // Test the cursor movement over the right margin in several different paragraphs.
    // These differences are based on its paragraphs
    // - alignment (left, center, right, justified),
    // - line count (1 line, 2 lines, blank line containing only spaces)
    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf43100_tdf120715_cursorOnSpacesOverMargin.docx");
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();

    // Make a paint to force the call of AddExtraBlankWidth, that calculate width for holePortions.
    pDoc->GetDocShell()->GetPreviewBitmap();

    // Move the cursor to the 2. line.
    pWrtShell->Down(/*bSelect=*/false, 1, /*bBasicCall=*/false);
    // Move the cursor to the right margin.
    pWrtShell->RightMargin(false, false);

    sal_Int32 nMarginPos = pWrtShell->GetCharRect().Left();
    sal_Int32 nLastCursorPos = nMarginPos;

    // Move the cursor right 5 times, every step should increase the cursor x position.
    // Before this fix, the cursor stopped at the margin.
    for (int i = 0; i < 5; i++)
    {
        pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
        sal_Int32 nNewCursorPos = pWrtShell->GetCharRect().Left();
        CPPUNIT_ASSERT_GREATER(nLastCursorPos, nNewCursorPos);
        nLastCursorPos = nNewCursorPos;
    }

    // Move down the cursor several lines, and check if it will keep nearly its horizontal position.
    // Some of the lines are not reach beyond the margin, there the cursor won't be able to keep its
    // original position.
    bool aLineReachOverMargin[] = { false, true, true, false, false, true, true,  false, true,
                                    true,  true, true, false, true,  true, false, false };
    // Cursor position can be a bit inaccurate, because it can only be positioned on characters,
    // that is based on the actual line layout, therefore the actual cursor position
    // is checked against a more distinct position instead of the nMarginPos.
    sal_Int32 nAvgLeft = (nMarginPos + nLastCursorPos) / 2;
    for (int i = 2; i < 17; i++)
    {
        pWrtShell->Down(/*bSelect=*/false, 1, /*bBasicCall=*/false);
        sal_Int32 nNewCursorPos = pWrtShell->GetCharRect().Left();
        if (aLineReachOverMargin[i])
            CPPUNIT_ASSERT_GREATER(nAvgLeft, nNewCursorPos);
        else
            CPPUNIT_ASSERT_LESS(nAvgLeft, nNewCursorPos);
    }
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
index 34681c1..d469083 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -100,9 +100,6 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
                TextFrameIndex nCharsCnt = nMaxLen - nSpaceCnt;
                if ( nSpaceCnt && nCharsCnt < rPor.GetLen() )
                {
                    if (nSpaceCnt)
                        rInf.GetTextSize( &rSI, rInf.GetIdx() + nCharsCnt, nSpaceCnt,
                                          nMaxComp, m_nExtraBlankWidth, nMaxSizeDiff );
                    nMaxLen = nCharsCnt;
                    if ( !nMaxLen )
                        return true;
@@ -614,13 +611,6 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
    else
        m_nBreakWidth = 0;

    if (m_nBreakStart > rInf.GetIdx() + nPorLen + m_nFieldDiff)
    {
        rInf.GetTextSize(&rSI, rInf.GetIdx() + nPorLen,
                         m_nBreakStart - rInf.GetIdx() - nPorLen - m_nFieldDiff, nMaxComp,
                         m_nExtraBlankWidth, nMaxSizeDiff, rInf.GetCachedVclData().get());
    }

    if( m_pHanging )
    {
        m_nBreakPos = m_nCutPos;
diff --git a/sw/source/core/text/guess.hxx b/sw/source/core/text/guess.hxx
index f83c7e2..696a09f 100644
--- a/sw/source/core/text/guess.hxx
+++ b/sw/source/core/text/guess.hxx
@@ -38,10 +38,9 @@ class SwTextGuess
    TextFrameIndex m_nFieldDiff;      // absolute positions can be wrong if we
                               // a field in the text has been expanded
    sal_uInt16 m_nBreakWidth;    // width of the broken portion
    sal_uInt16 m_nExtraBlankWidth;    // width of spaces after the break
public:
    SwTextGuess(): m_nCutPos(0), m_nBreakStart(0),
                   m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0), m_nExtraBlankWidth(0)
                   m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0)
        { }

    // true, if current portion still fits to current line
@@ -52,7 +51,6 @@ public:
    SwHangingPortion* GetHangingPortion() const { return m_pHanging.get(); }
    SwHangingPortion* ReleaseHangingPortion() { return m_pHanging.release(); }
    sal_uInt16 BreakWidth() const { return m_nBreakWidth; }
    sal_uInt16 ExtraBlankWidth() const { return m_nExtraBlankWidth; }
    TextFrameIndex CutPos() const { return m_nCutPos; }
    TextFrameIndex BreakStart() const { return m_nBreakStart; }
    TextFrameIndex BreakPos() const {return m_nBreakPos; }
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
index 8867929..546d91b 100644
--- a/sw/source/core/text/itrcrsr.cxx
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -401,26 +401,6 @@ void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *p
    // GetInfo().SetOut( GetInfo().GetWin() );
}

// tdf#120715 tdf#43100: Make width for some HolePortions, so cursor will be able to move into it.
// It should not change the layout, so this should be called after the layout is calculated.
void SwTextCursor::AddExtraBlankWidth()
{
    SwLinePortion* pPos = m_pCurr->GetNextPortion();
    SwLinePortion* pNextPos;
    while (pPos)
    {
        pNextPos = pPos->GetNextPortion();
        // Do it only if it is the last portion that able to handle the cursor,
        // else the next portion would misscalculate the cursor position
        if (pPos->ExtraBlankWidth() && (!pNextPos || pNextPos->IsMarginPortion()))
        {
            pPos->Width(pPos->Width() + pPos->ExtraBlankWidth());
            pPos->ExtraBlankWidth(0);
        }
        pPos = pNextPos;
    }
}

// 1170: Ancient bug: Shift-End forgets the last character ...
void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
                                  SwCursorMoveState* pCMS, const tools::Long nMax )
@@ -1233,6 +1213,10 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,

    GetCharRect_( pOrig, nFindOfst, pCMS );

    // This actually would have to be "-1 LogicToPixel", but that seems too
    // expensive, so it's a value (-12), that should hopefully be OK.
    const SwTwips nTmpRight = Right() - 12;

    pOrig->Pos().AdjustX(aCharPos.X() );
    pOrig->Pos().AdjustY(aCharPos.Y() );

@@ -1244,6 +1228,13 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
        pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
    }

    const IDocumentSettingAccess& rIDSA = GetTextFrame()->GetDoc().getIDocumentSettingAccess();
    const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN)
        || rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
    // Make sure the cursor respects the right margin, unless in compat mode, where the tab size has priority over the margin size.
    if( pOrig->Left() > nTmpRight && !bTabOverMargin)
        pOrig->Pos().setX( nTmpRight );

    if( nMax )
    {
        if( pOrig->Top() + pOrig->Height() > nMax )
@@ -1264,6 +1255,16 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
                pCMS->m_aRealHeight.setY( nMax - nTmp );
        }
    }
    tools::Long nOut = pOrig->Right() - GetTextFrame()->getFrameArea().Right();
    if( nOut > 0 )
    {
        if( GetTextFrame()->getFrameArea().Width() < GetTextFrame()->getFramePrintArea().Left()
                                   + GetTextFrame()->getFramePrintArea().Width() )
            nOut += GetTextFrame()->getFrameArea().Width() - GetTextFrame()->getFramePrintArea().Left()
                    - GetTextFrame()->getFramePrintArea().Width();
        if( nOut > 0 )
            pOrig->Pos().AdjustX( -(nOut + 10) );
    }
}

/**
@@ -1319,6 +1320,9 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
    if( bLeftOver )
        x = nLeftMargin;
    const bool bRightOver = x > nRightMargin;
    if( bRightOver )
        x = nRightMargin;

    const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );

    // Until here everything in document coordinates.
@@ -1643,7 +1647,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
            return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
                                bChgNode, pCMS );
        }
        if( pPor->InTextGrp() || pPor->IsHolePortion() )
        if( pPor->InTextGrp() )
        {
            sal_uInt8 nOldProp;
            if( GetPropFont() )
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index a66d358..3935630 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -127,7 +127,6 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,

    // maybe catch-up adjustment
    GetAdjusted();
    AddExtraBlankWidth();
    GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
    GetInfo().ResetSpaceIdx();
    GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
diff --git a/sw/source/core/text/itrtxt.hxx b/sw/source/core/text/itrtxt.hxx
index 0b48e3b..f36932d 100644
--- a/sw/source/core/text/itrtxt.hxx
+++ b/sw/source/core/text/itrtxt.hxx
@@ -270,7 +270,6 @@ class SwTextCursor : public SwTextAdjuster
protected:
    void CtorInitTextCursor( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
    explicit SwTextCursor(SwTextNode const * pTextNode) : SwTextAdjuster(pTextNode) { }
    void AddExtraBlankWidth();
public:
    SwTextCursor( SwTextFrame *pTextFrame, SwTextSizeInfo *pTextSizeInf )
        : SwTextAdjuster(pTextFrame->GetTextNodeFirst())
diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx
index 5ce25a3..3cd1d9f 100644
--- a/sw/source/core/text/porlin.hxx
+++ b/sw/source/core/text/porlin.hxx
@@ -63,7 +63,6 @@ private:
    PortionType mnWhichPor;       // Who's who?
    bool m_bJoinBorderWithPrev;
    bool m_bJoinBorderWithNext;
    SwTwips m_nExtraBlankWidth = 0;    // width of spaces after the break

    void Truncate_();

@@ -84,8 +83,6 @@ public:
    SwTwips PrtWidth() const { return Width(); }
    void AddPrtWidth( const SwTwips nNew ) { Width( Width() + nNew ); }
    void SubPrtWidth( const SwTwips nNew ) { Width( Width() - nNew ); }
    SwTwips ExtraBlankWidth() const { return m_nExtraBlankWidth; }
    void ExtraBlankWidth(const SwTwips nNew) { m_nExtraBlankWidth = nNew; }
    SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
    void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }

@@ -194,7 +191,6 @@ inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
    mnWhichPor = rPortion.mnWhichPor;
    m_bJoinBorderWithPrev = rPortion.m_bJoinBorderWithPrev;
    m_bJoinBorderWithNext = rPortion.m_bJoinBorderWithNext;
    m_nExtraBlankWidth = rPortion.m_nExtraBlankWidth;
    return *this;
}

@@ -206,8 +202,7 @@ inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
    mnHangingBaseline( rPortion.mnHangingBaseline ),
    mnWhichPor( rPortion.mnWhichPor ),
    m_bJoinBorderWithPrev( rPortion.m_bJoinBorderWithPrev ),
    m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext ),
    m_nExtraBlankWidth(rPortion.m_nExtraBlankWidth)
    m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext )
{
}

diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index c0f6649..a5ae0ea 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -321,7 +321,6 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
    if ( !bFull )
    {
        Width( aGuess.BreakWidth() );
        ExtraBlankWidth(aGuess.ExtraBlankWidth());
        // Caution!
        if( !InExpGrp() || InFieldGrp() )
            SetLen( rInf.GetLen() );
@@ -410,8 +409,6 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
            {
                SwHolePortion *pNew = new SwHolePortion( *this );
                pNew->SetLen( nRealStart - aGuess.BreakPos() );
                pNew->Width(0);
                pNew->ExtraBlankWidth( aGuess.ExtraBlankWidth() );
                Insert( pNew );
            }
        }
@@ -752,29 +749,12 @@ SwHolePortion::SwHolePortion( const SwTextPortion &rPor )
{
    SetLen( TextFrameIndex(1) );
    Height( rPor.Height() );
    Width(0);
    SetAscent( rPor.GetAscent() );
    SetWhichPor( PortionType::Hole );
}

SwLinePortion *SwHolePortion::Compress() { return this; }

// The GetTextSize() assumes that the own length is correct
SwPosSize SwHolePortion::GetTextSize(const SwTextSizeInfo& rInf) const
{
    SwPosSize aSize = rInf.GetTextSize();
    if (!GetJoinBorderWithPrev())
        aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace());
    if (!GetJoinBorderWithNext())
        aSize.Width(aSize.Width() + rInf.GetFont()->GetRightBorderSpace());

    aSize.Height(aSize.Height() +
        rInf.GetFont()->GetTopBorderSpace() +
        rInf.GetFont()->GetBottomBorderSpace());

    return aSize;
}

void SwHolePortion::Paint( const SwTextPaintInfo &rInf ) const
{
    if( !rInf.GetOut() )
diff --git a/sw/source/core/text/portxt.hxx b/sw/source/core/text/portxt.hxx
index a30f6f0..77ec0a9 100644
--- a/sw/source/core/text/portxt.hxx
+++ b/sw/source/core/text/portxt.hxx
@@ -69,7 +69,6 @@ public:
    void SetBlankWidth( const sal_uInt16 nNew ) { m_nBlankWidth = nNew; }
    virtual SwLinePortion *Compress() override;
    virtual bool Format( SwTextFormatInfo &rInf ) override;
    virtual SwPosSize GetTextSize(const SwTextSizeInfo& rInfo) const override;
    virtual void Paint( const SwTextPaintInfo &rInf ) const override;

    // Accessibility: pass information about this portion to the PortionHandler