/* -*- 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 .
 */
 
#include <hintids.hxx>
#include <vcl/svapp.hxx>
#include <editeng/protitem.hxx>
#include <crsrsh.hxx>
#include <doc.hxx>
#include <cntfrm.hxx>
#include <editsh.hxx>
#include <pam.hxx>
#include <swtable.hxx>
#include <docary.hxx>
#include <frmatr.hxx>
#include <frmfmt.hxx>
#include <viscrs.hxx>
#include "callnk.hxx"
#include <tabfrm.hxx>
#include <ndtxt.hxx>
#include <shellres.hxx>
#include <cellatr.hxx>
#include <cellfrm.hxx>
#include <rowfrm.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <svx/srchdlg.hxx>
 
/// set cursor into next/previous cell
bool SwCursorShell::GoNextCell( bool bAppendLine )
{
    bool bRet = false;
    const SwTableNode* pTableNd = nullptr;
 
    if( IsTableMode() || nullptr != ( pTableNd = IsCursorInTable() ))
    {
        SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
        SwCallLink aLk( *this ); // watch Cursor-Moves
        bRet = true;
 
        // Check if we have to move the cursor to a covered cell before
        // proceeding:
        const SwNode* pTableBoxStartNode = pCursor->GetNode().FindTableBoxStartNode();
        const SwTableBox* pTableBox = nullptr;
 
        if ( pCursor->GetCursorRowSpanOffset() )
        {
            pTableBox = pTableBoxStartNode->GetTableBox();
            if ( pTableBox->getRowSpan() > 1 )
            {
                if ( !pTableNd )
                    pTableNd = IsCursorInTable();
                assert (pTableNd);
                pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(),
                                                           static_cast<sal_uInt16>(pTableBox->getRowSpan() + pCursor->GetCursorRowSpanOffset() ) );
                pTableBoxStartNode = pTableBox->GetSttNd();
            }
        }
 
        SwNodeIndex  aCellStt( *pTableBoxStartNode->EndOfSectionNode(), 1 );
 
        // if there is another StartNode after the EndNode of a cell then
        // there is another cell
        if( !aCellStt.GetNode().IsStartNode() )
        {
            if( pCursor->HasMark() || !bAppendLine )
                bRet = false;
            else if (pTableNd)
            {
                // if there is no list anymore then create new one
                if ( !pTableBox )
                    pTableBox = pTableNd->GetTable().GetTableBox(
                                    pCursor->GetPoint()->nNode.GetNode().
                                    StartOfSectionIndex() );
 
                OSL_ENSURE( pTableBox, "Box is not in this table" );
                SwSelBoxes aBoxes;
 
                // the document might change; w/o Action views would not be notified
                static_cast<SwEditShell*>(this)->StartAllAction();
                bRet = mxDoc->InsertRow( SwTable::SelLineFromBox( pTableBox, aBoxes, false ));
                static_cast<SwEditShell*>(this)->EndAllAction();
            }
        }
        bRet = bRet && pCursor->GoNextCell();
        if( bRet )
            UpdateCursor();
    }
    return bRet;
}
 
bool SwCursorShell::GoPrevCell()
{
    bool bRet = false;
    if( IsTableMode() || IsCursorInTable() )
    {
        SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
        SwCallLink aLk( *this ); // watch Cursor-Moves
        bRet = pCursor->GoPrevCell();
        if( bRet )
            UpdateCursor(); // update current cursor
    }
    return bRet;
}
 
static const SwFrame* lcl_FindMostUpperCellFrame( const SwFrame* pFrame )
{
    while ( pFrame &&
            ( !pFrame->IsCellFrame() ||
              !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
               pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
    {
        pFrame = pFrame->GetUpper();
    }
    return pFrame;
}
 
bool SwCursorShell::SelTableRowOrCol( bool bRow, bool bRowSimple )
{
    // check if the current cursor's SPoint/Mark are in a table
    SwFrame *pFrame = GetCurrFrame();
    if( !pFrame->IsInTab() )
        return false;
 
    const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
    const SwTabFrame* pMasterTabFrame = pTabFrame->IsFollow() ? pTabFrame->FindMaster( true ) : pTabFrame;
    const SwTable* pTable = pTabFrame->GetTable();
 
    SET_CURR_SHELL( this );
 
    const SwTableBox* pStt = nullptr;
    const SwTableBox* pEnd = nullptr;
 
    // search box based on layout
    SwSelBoxes aBoxes;
    SwTableSearchType eType = bRow ? SwTableSearchType::Row : SwTableSearchType::Col;
    const bool bCheckProtected = !IsReadOnlyAvailable();
 
    if( bCheckProtected )
        eType = static_cast<SwTableSearchType>(eType | SwTableSearchType::Protect);
 
    if ( !bRowSimple )
    {
        GetTableSel( *this, aBoxes, eType );
 
        if( aBoxes.empty() )
            return false;
 
        pStt = aBoxes[0];
        pEnd = aBoxes.back();
    }
    // #i32329# Enhanced table selection
    else if ( pTable->IsNewModel() )
    {
        const SwShellCursor *pCursor = GetCursor_();
        SwTable::SearchType eSearchType = bRow ? SwTable::SEARCH_ROW : SwTable::SEARCH_COL;
        pTable->CreateSelection( *pCursor, aBoxes, eSearchType, bCheckProtected );
        if( aBoxes.empty() )
            return false;
 
        pStt = aBoxes[0];
        pEnd = aBoxes.back();
    }
    else
    {
        const SwShellCursor *pCursor = GetCursor_();
        const SwFrame* pStartFrame = pFrame;
        const SwContentNode *pCNd = pCursor->GetContentNode( false );
        const SwFrame* pEndFrame   = pCNd ? pCNd->getLayoutFrame( GetLayout(), &pCursor->GetMkPos() ) : nullptr;
 
        if ( bRow )
        {
            pStartFrame = lcl_FindMostUpperCellFrame( pStartFrame );
            pEndFrame   = lcl_FindMostUpperCellFrame( pEndFrame   );
        }
 
        if ( !pStartFrame || !pEndFrame )
            return false;
 
        const bool bVert = pFrame->ImplFindTabFrame()->IsVertical();
 
        // If we select upwards it is sufficient to set pStt and pEnd
        // to the first resp. last box of the selection obtained from
        // GetTableSel. However, selecting downwards requires the frames
        // located at the corners of the selection. This does not work
        // for column selections in vertical tables:
        const bool bSelectUp = ( bVert && !bRow ) ||
                                *pCursor->GetPoint() <= *pCursor->GetMark();
        SwCellFrames aCells;
        GetTableSel( static_cast<const SwCellFrame*>(pStartFrame),
                   static_cast<const SwCellFrame*>(pEndFrame),
                   aBoxes, bSelectUp ? nullptr : &aCells, eType );
 
        if( aBoxes.empty() || ( !bSelectUp && 4 != aCells.size() ) )
            return false;
 
        if ( bSelectUp )
        {
            pStt = aBoxes[0];
            pEnd = aBoxes.back();
        }
        else
        {
            // will become point of table cursor
            pStt = aCells[bVert ? 0 : (bRow ? 2 : 1)]->GetTabBox();
            // will become mark of table cursor
            pEnd = aCells[bVert ? 3 : (bRow ? 1 : 2)]->GetTabBox();
        }
    }
 
    // if no table cursor exists, create one
    if( !m_pTableCursor )
    {
        m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->SwSelPaintRects::Hide();
    }
 
    m_pTableCursor->DeleteMark();
 
    // set start and end of a column
    m_pTableCursor->GetPoint()->nNode = *pEnd->GetSttNd();
    m_pTableCursor->Move( fnMoveForward, GoInContent );
    m_pTableCursor->SetMark();
    m_pTableCursor->GetPoint()->nNode = *pStt->GetSttNd()->EndOfSectionNode();
    m_pTableCursor->Move( fnMoveBackward, GoInContent );
 
    // set PtPos 'close' to the reference table, otherwise we might get problems
    // with the repeated headlines check in UpdateCursor():
    if ( !bRow )
        m_pTableCursor->GetPtPos() = pMasterTabFrame->IsVertical()
                                   ? pMasterTabFrame->getFrameArea().TopRight()
                                   : pMasterTabFrame->getFrameArea().TopLeft();
 
    UpdateCursor();
    return true;
}
 
bool SwCursorShell::SelTable()
{
    // check if the current cursor's SPoint/Mark are in a table
    SwFrame *pFrame = GetCurrFrame();
    if( !pFrame->IsInTab() )
        return false;
 
    const SwTabFrame *pTableFrame = pFrame->ImplFindTabFrame();
    const SwTabFrame* pMasterTabFrame = pTableFrame->IsFollow() ? pTableFrame->FindMaster( true ) : pTableFrame;
    const SwTableNode* pTableNd = pTableFrame->GetTable()->GetTableNode();
 
    SET_CURR_SHELL( this );
 
    if( !m_pTableCursor )
    {
        m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->SwSelPaintRects::Hide();
    }
 
    m_pTableCursor->DeleteMark();
    m_pTableCursor->GetPoint()->nNode = *pTableNd;
    m_pTableCursor->Move( fnMoveForward, GoInContent );
    m_pTableCursor->SetMark();
    // set MkPos 'close' to the master table, otherwise we might get problems
    // with the repeated headlines check in UpdateCursor():
    m_pTableCursor->GetMkPos() = pMasterTabFrame->IsVertical() ? pMasterTabFrame->getFrameArea().TopRight() : pMasterTabFrame->getFrameArea().TopLeft();
    m_pTableCursor->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
    m_pTableCursor->Move( fnMoveBackward, GoInContent );
    UpdateCursor();
    return true;
}
 
bool SwCursorShell::SelTableBox()
{
    // if we're in a table, create a table cursor, and select the cell
    // that the current cursor's point resides in
 
    // search for start node of our table box. If not found, exit really
    const SwStartNode* pStartNode =
        m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
 
#if OSL_DEBUG_LEVEL > 0
    // the old code checks whether we're in a table by asking the
    // frame. This should yield the same result as searching for the
    // table box start node, right?
    SwFrame *pFrame = GetCurrFrame();
    OSL_ENSURE( !pFrame->IsInTab() == !(pStartNode != nullptr),
                "Schroedinger's table: We're in a box, and also we aren't." );
#endif
    if( pStartNode == nullptr )
        return false;
 
    SET_CURR_SHELL( this );
 
    // create a table cursor, if there isn't one already
    if( !m_pTableCursor )
    {
        m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->SwSelPaintRects::Hide();
    }
 
    // select the complete box with our shiny new m_pTableCursor
    // 1. delete mark, and move point to first content node in box
    m_pTableCursor->DeleteMark();
    *(m_pTableCursor->GetPoint()) = SwPosition( *pStartNode );
    m_pTableCursor->Move( fnMoveForward, GoInNode );
 
    // 2. set mark, and move point to last content node in box
    m_pTableCursor->SetMark();
    *(m_pTableCursor->GetPoint()) = SwPosition( *(pStartNode->EndOfSectionNode()) );
    m_pTableCursor->Move( fnMoveBackward, GoInNode );
 
    // 3. exchange
    m_pTableCursor->Exchange();
 
    // with some luck, UpdateCursor() will now update everything that
    // needs updating
    UpdateCursor();
 
    return true;
}
 
// TODO: provide documentation
/** get the next non-protected cell inside a table
 
    @param[in,out] rIdx is on a table node
    @param bInReadOnly  ???
 
    @return <false> if no suitable cell could be found, otherwise <rIdx> points
            to content in a suitable cell and <true> is returned.
*/
static bool lcl_FindNextCell( SwNodeIndex& rIdx, bool bInReadOnly )
{
    // check protected cells
    SwNodeIndex aTmp( rIdx, 2 ); // TableNode + StartNode
 
    // the resulting cell should be in that table:
    const SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
 
    if ( !pTableNd )
    {
        OSL_FAIL( "lcl_FindNextCell not celled with table start node!" );
        return false;
    }
 
    const SwNode* pTableEndNode = pTableNd->EndOfSectionNode();
 
    SwNodes& rNds = aTmp.GetNode().GetNodes();
    SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
 
    // no content node => go to next content node
    if( !pCNd )
        pCNd = rNds.GoNext( &aTmp );
 
    // robust
    if ( !pCNd )
        return false;
 
    SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
 
    if ( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
        (!bInReadOnly && pFrame->IsProtected() ) )
    {
        // we are not located inside a 'valid' cell. We have to continue searching...
 
        // skip behind current section. This might be the end of the table cell
        // or behind a inner section or...
        aTmp.Assign( *pCNd->EndOfSectionNode(), 1 );
 
        // loop to find a suitable cell...
        for( ;; )
        {
            SwNode* pNd = &aTmp.GetNode();
 
            // we break this loop if we reached the end of the table.
            // to make this code even more robust, we also break if we are
            // already behind the table end node:
            if( pNd == pTableEndNode || /*robust: */ pNd->GetIndex() > pTableEndNode->GetIndex() )
                return false;
 
            // ok, get the next content node:
            pCNd = aTmp.GetNode().GetContentNode();
            if( nullptr == pCNd )
                pCNd = rNds.GoNext( &aTmp );
 
            // robust:
            if ( !pCNd )
                return false;
 
            // check if we have found a suitable table cell:
            pFrame = pCNd->getLayoutFrame( pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
 
            if ( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
                (bInReadOnly || !pFrame->IsProtected() ) )
            {
                // finally, we have found a suitable table cell => set index and return
                rIdx = *pCNd;
                return true;
            }
 
            // continue behind the current section:
            aTmp.Assign( *pCNd->EndOfSectionNode(), +1 );
        }
    }
    rIdx = *pCNd;
    return true;
}
 
/// see lcl_FindNextCell()
static bool lcl_FindPrevCell( SwNodeIndex& rIdx, bool bInReadOnly  )
{
    SwNodeIndex aTmp( rIdx, -2 ); // TableNode + EndNode
 
    const SwNode* pTableEndNode = &rIdx.GetNode();
    const SwTableNode* pTableNd = pTableEndNode->StartOfSectionNode()->GetTableNode();
 
    if ( !pTableNd )
    {
        OSL_FAIL( "lcl_FindPrevCell not celled with table start node!" );
        return false;
    }
 
    SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
 
    if( !pCNd )
        pCNd = SwNodes::GoPrevious( &aTmp );
 
    if ( !pCNd )
        return false;
 
    SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
 
    if( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
        (!bInReadOnly && pFrame->IsProtected() ))
    {
        // skip before current section
        aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
        for( ;; )
        {
            SwNode* pNd = &aTmp.GetNode();
 
            if( pNd == pTableNd || pNd->GetIndex() < pTableNd->GetIndex() )
                return false;
 
            pCNd = aTmp.GetNode().GetContentNode();
            if( nullptr == pCNd )
                pCNd = SwNodes::GoPrevious( &aTmp );
 
            if ( !pCNd )
                return false;
 
            pFrame = pCNd->getLayoutFrame( pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
 
            if( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
                (bInReadOnly || !pFrame->IsProtected() ) )
            {
                rIdx = *pCNd;
                return true; // ok, not protected
            }
            aTmp.Assign( *pCNd->StartOfSectionNode(), - 1 );
        }
    }
    rIdx = *pCNd;
    return true;
}
 
bool GotoPrevTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
                    bool bInReadOnly )
{
    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
 
    SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
 
    SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
    if( pTableNd )
    {
        // #i26532#: If we are inside a table, we may not go backward to the
        // table start node, because we would miss any tables inside this table.
        SwTableNode* pInnerTableNd = nullptr;
        SwNodeIndex aTmpIdx( aIdx );
        while( aTmpIdx.GetIndex() &&
                nullptr == ( pInnerTableNd = aTmpIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
            --aTmpIdx;
 
        if( pInnerTableNd == pTableNd )
            aIdx.Assign( *pTableNd, - 1 );
    }
 
    SwNodeIndex aOldIdx = aIdx;
    sal_uLong nLastNd = rCurrentCursor.GetDoc()->GetNodes().Count() - 1;
    do {
        while( aIdx.GetIndex() &&
            nullptr == ( pTableNd = aIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
        {
            --aIdx;
            if ( aIdx == aOldIdx )
            {
                SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
                return false;
            }
        }
 
        if ( !aIdx.GetIndex() )
        {
            SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
            aIdx = nLastNd;
            continue;
        }
 
        {
            if( &fnPosTable == &fnMoveForward ) // at the beginning?
            {
                aIdx = *aIdx.GetNode().StartOfSectionNode();
                if( !lcl_FindNextCell( aIdx, bInReadOnly ))
                {
                    // skip table
                    aIdx.Assign( *pTableNd, -1 );
                    continue;
                }
            }
            else
            {
                // check protected cells
                if( !lcl_FindNextCell( aIdx, bInReadOnly ))
                {
                    // skip table
                    aIdx.Assign( *pTableNd, -1 );
                    continue;
                }
            }
 
            SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
            if ( pTextNode )
            {
                rCurrentCursor.GetPoint()->nNode = *pTextNode;
                rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
                                                      pTextNode->Len() :
                                                      0 );
            }
            return true;
        }
    } while( true );
 
    return false;
}
 
bool GotoNextTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
                    bool bInReadOnly )
{
    SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
 
    SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
    SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
 
    if( pTableNd )
        aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
 
    SwNodeIndex aOldIdx = aIdx;
    sal_uLong nLastNd = rCurrentCursor.GetDoc()->GetNodes().Count() - 1;
    do {
        while( aIdx.GetIndex() < nLastNd &&
                nullptr == ( pTableNd = aIdx.GetNode().GetTableNode()) )
        {
            ++aIdx;
            if ( aIdx == aOldIdx )
            {
                SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
                return false;
            }
        }
 
        if ( aIdx.GetIndex() == nLastNd )
        {
            SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
            aIdx = 0;
            continue;
        }
 
        assert( pTableNd );  // coverity, should never be nullptr
 
        if( &fnPosTable == &fnMoveForward ) // at the beginning?
        {
            if( !lcl_FindNextCell( aIdx, bInReadOnly ))
            {
                // skip table
                aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
                continue;
            }
        }
        else
        {
            aIdx = *aIdx.GetNode().EndOfSectionNode();
            // check protected cells
            if( !lcl_FindNextCell( aIdx, bInReadOnly ))
            {
                // skip table
                aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
                continue;
            }
        }
 
        SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
        if ( pTextNode )
        {
            rCurrentCursor.GetPoint()->nNode = *pTextNode;
            rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
                                                  pTextNode->Len() :
                                                  0 );
        }
        return true;
 
    } while( true );
 
    // the flow is such that it is not possible to get there
 
    return false;
}
 
bool GotoCurrTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
                    bool bInReadOnly )
{
    SwTableNode* pTableNd = rCurrentCursor.GetPoint()->nNode.GetNode().FindTableNode();
    if( !pTableNd )
        return false;
 
    SwTextNode* pTextNode = nullptr;
    if( &fnPosTable == &fnMoveBackward ) // to the end of the table
    {
        SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
        if( !lcl_FindPrevCell( aIdx, bInReadOnly ))
            return false;
        pTextNode = aIdx.GetNode().GetTextNode();
    }
    else
    {
        SwNodeIndex aIdx( *pTableNd );
        if( !lcl_FindNextCell( aIdx, bInReadOnly ))
            return false;
        pTextNode = aIdx.GetNode().GetTextNode();
    }
 
    if ( pTextNode )
    {
        rCurrentCursor.GetPoint()->nNode = *pTextNode;
        rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
                                                        pTextNode->Len() :
                                                        0 );
    }
 
    return true;
}
 
bool SwCursor::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
{
    bool bRet = false;
    SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
 
    if( pTableCursor || !HasMark() )
    {
        SwCursorSaveState aSaveState( *this );
        bRet = (*fnWhichTable)( *this, fnPosTable, IsReadOnlyAvailable() ) &&
                !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
                           SwCursorSelOverFlags::Toggle );
    }
    return bRet;
}
 
bool SwCursorShell::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
{
    SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
 
    SwShellCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
    bool bCheckPos;
    bool bRet;
    sal_uLong nPtNd = 0;
    sal_Int32 nPtCnt = 0;
 
    if ( !m_pTableCursor && m_pCurrentCursor->HasMark() )
    {
        // switch to table mode
        m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
        m_pCurrentCursor->DeleteMark();
        m_pCurrentCursor->SwSelPaintRects::Hide();
        m_pTableCursor->SetMark();
        pCursor = m_pTableCursor;
        bCheckPos = false;
    }
    else
    {
        bCheckPos = true;
        nPtNd = pCursor->GetPoint()->nNode.GetIndex();
        nPtCnt = pCursor->GetPoint()->nContent.GetIndex();
    }
 
    bRet = pCursor->MoveTable( fnWhichTable, fnPosTable );
 
    if( bRet )
    {
        // #i45028# - set "top" position for repeated headline rows
        pCursor->GetPtPos() = Point();
 
        UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
 
        if( bCheckPos &&
            pCursor->GetPoint()->nNode.GetIndex() == nPtNd &&
            pCursor->GetPoint()->nContent.GetIndex() == nPtCnt )
            bRet = false;
    }
    return bRet;
}
 
bool SwCursorShell::IsTableComplexForChart()
{
    bool bRet = false;
 
    // Here we may trigger table formatting so we better do that inside an action
    StartAction();
    const SwTableNode* pTNd = m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableNode();
    if( pTNd )
    {
        // in a table; check if table or section is balanced
        OUString sSel;
        if( m_pTableCursor )
            sSel = GetBoxNms();
        bRet = pTNd->GetTable().IsTableComplexForChart( sSel );
    }
    EndAction();
 
    return bRet;
}
 
OUString SwCursorShell::GetBoxNms() const
{
    OUString sNm;
    const SwPosition* pPos;
    SwFrame* pFrame;
 
    if( IsTableMode() )
    {
        SwContentNode *pCNd = m_pTableCursor->Start()->nNode.GetNode().GetContentNode();
        pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
        if( !pFrame )
            return sNm;
 
        do {
            pFrame = pFrame->GetUpper();
        } while ( pFrame && !pFrame->IsCellFrame() );
 
        OSL_ENSURE( pFrame, "no frame for this box" );
 
        if( !pFrame )
            return sNm;
 
        sNm = static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
        sNm += ":";
        pPos = m_pTableCursor->End();
    }
    else
    {
        const SwTableNode* pTableNd = IsCursorInTable();
        if( !pTableNd )
            return sNm;
        pPos = GetCursor()->GetPoint();
    }
 
    SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
    pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
 
    if( pFrame )
    {
        do {
            pFrame = pFrame->GetUpper();
        } while ( pFrame && !pFrame->IsCellFrame() );
 
        if( pFrame )
            sNm += static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
    }
    return sNm;
}
 
bool SwCursorShell::GotoTable( const OUString& rName )
{
    SwCallLink aLk( *this ); // watch Cursor-Moves
    bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoTable( rName );
    if( bRet )
    {
        m_pCurrentCursor->GetPtPos() = Point();
        UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
                    SwCursorShell::READONLY );
    }
    return bRet;
}
 
bool SwCursorShell::CheckTableBoxContent( const SwPosition* pPos )
{
    if( !m_pBoxIdx || !m_pBoxPtr || IsSelTableCells() || !IsAutoUpdateCells() )
        return false;
 
    // check if box content is consistent with given box format, reset if not
    SwTableBox* pChkBox = nullptr;
    SwStartNode* pSttNd = nullptr;
    if( !pPos )
    {
        // get stored position
        if( m_pBoxIdx && m_pBoxPtr &&
            nullptr != ( pSttNd = m_pBoxIdx->GetNode().GetStartNode() ) &&
            SwTableBoxStartNode == pSttNd->GetStartNodeType() &&
            m_pBoxPtr == pSttNd->FindTableNode()->GetTable().
                        GetTableBox( m_pBoxIdx->GetIndex() ) )
            pChkBox = m_pBoxPtr;
    }
    else if( nullptr != ( pSttNd = pPos->nNode.GetNode().
                                FindSttNodeByType( SwTableBoxStartNode )) )
    {
        pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
    }
 
    // box has more than one paragraph
    if( pChkBox && pSttNd->GetIndex() + 2 != pSttNd->EndOfSectionIndex() )
        pChkBox = nullptr;
 
    // destroy pointer before next action starts
    if( !pPos && !pChkBox )
        ClearTableBoxContent();
 
    // cursor not anymore in this section?
    if( pChkBox && !pPos &&
        ( m_pCurrentCursor->HasMark() || m_pCurrentCursor->GetNext() != m_pCurrentCursor ||
          pSttNd->GetIndex() + 1 == m_pCurrentCursor->GetPoint()->nNode.GetIndex() ))
        pChkBox = nullptr;
 
    // Did the content of a box change at all? This is important if e.g. Undo
    // could not restore the content properly.
    if( pChkBox )
    {
        const SwTextNode* pNd = GetDoc()->GetNodes()[
                                    pSttNd->GetIndex() + 1 ]->GetTextNode();
        if( !pNd ||
            ( pNd->GetText() == SwViewShell::GetShellRes()->aCalc_Error &&
              SfxItemState::SET == pChkBox->GetFrameFormat()->
                            GetItemState( RES_BOXATR_FORMULA )) )
            pChkBox = nullptr;
    }
 
    if( pChkBox )
    {
        // destroy pointer before next action starts
        ClearTableBoxContent();
        StartAction();
        GetDoc()->ChkBoxNumFormat( *pChkBox, true );
        EndAction();
    }
 
    return nullptr != pChkBox;
}
 
void SwCursorShell::SaveTableBoxContent( const SwPosition* pPos )
{
    if( IsSelTableCells() || !IsAutoUpdateCells() )
        return ;
 
    if( !pPos )
        pPos = m_pCurrentCursor->GetPoint();
 
    SwStartNode* pSttNd = pPos->nNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
 
    bool bCheckBox = false;
    if( pSttNd && m_pBoxIdx )
    {
        if( pSttNd == &m_pBoxIdx->GetNode() )
            pSttNd = nullptr;
        else
            bCheckBox = true;
    }
    else
        bCheckBox = nullptr != m_pBoxIdx;
 
    if( bCheckBox )
    {
        // check m_pBoxIdx
        SwPosition aPos( *m_pBoxIdx );
        CheckTableBoxContent( &aPos );
    }
 
    if( pSttNd )
    {
        m_pBoxPtr = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
 
        if( m_pBoxIdx )
            *m_pBoxIdx = *pSttNd;
        else
            m_pBoxIdx = new SwNodeIndex( *pSttNd );
    }
}
 
void SwCursorShell::ClearTableBoxContent()
{
    delete m_pBoxIdx;
    m_pBoxIdx = nullptr;
    m_pBoxPtr = nullptr;
}
 
bool SwCursorShell::EndAllTableBoxEdit()
{
    bool bRet = false;
    for(SwViewShell& rSh : GetRingContainer())
    {
        if( dynamic_cast<const SwCursorShell *>(&rSh) != nullptr )
            bRet |= static_cast<SwCursorShell*>(&rSh)->CheckTableBoxContent(
                        static_cast<SwCursorShell*>(&rSh)->m_pCurrentCursor->GetPoint() );
 
    }
    return bRet;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V595 The 'm_pBoxIdx' pointer was utilized before it was verified against nullptr. Check lines: 895, 903.

V560 A part of conditional expression is always true: m_pBoxPtr.

V560 A part of conditional expression is always true: m_pBoxIdx.