diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/frmedt/fetab.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/frmedt/fetab.cxx')
-rw-r--r-- | sw/source/core/frmedt/fetab.cxx | 2530 |
1 files changed, 2530 insertions, 0 deletions
diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx new file mode 100644 index 0000000000..79f5eb6b5e --- /dev/null +++ b/sw/source/core/frmedt/fetab.cxx @@ -0,0 +1,2530 @@ +/* -*- 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 <memory> +#include <hintids.hxx> + +#include <vcl/errinf.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <editeng/protitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svtools/ruler.hxx> +#include <osl/diagnose.h> +#include <swwait.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <frmatr.hxx> +#include <fesh.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <docsh.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <flyfrm.hxx> +#include <swtable.hxx> +#include <swddetbl.hxx> +#include <ndtxt.hxx> +#include <calc.hxx> +#include <dialoghelp.hxx> +#include <tabcol.hxx> +#include <tblafmt.hxx> +#include <cellatr.hxx> +#include <pam.hxx> +#include <viscrs.hxx> +#include <tblsel.hxx> +#include <swerror.h> +#include <swundo.hxx> +#include <frmtool.hxx> +#include <fmtrowsplt.hxx> +#include <node.hxx> +#include <sortedobjs.hxx> + +using namespace ::com::sun::star; + +// also see swtable.cxx +#define COLFUZZY 20L + +static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; } + +namespace { + +class TableWait +{ + const std::unique_ptr<SwWait> m_pWait; + // this seems really fishy: do some locking, if an arbitrary number of lines is exceeded + static const size_t our_kLineLimit = 20; + static bool ShouldWait(size_t nCnt, SwFrame *pFrame, size_t nCnt2) + { return our_kLineLimit < nCnt || our_kLineLimit < nCnt2 || (pFrame && our_kLineLimit < pFrame->ImplFindTabFrame()->GetTable()->GetTabLines().size()); } +public: + TableWait(size_t nCnt, SwFrame *pFrame, SwDocShell &rDocShell, size_t nCnt2 = 0) + : m_pWait( ShouldWait(nCnt, pFrame, nCnt2) ? std::make_unique<SwWait>( rDocShell, true ) : nullptr ) + { } +}; + +} + +void SwFEShell::ParkCursorInTab() +{ + SwCursor * pSwCursor = GetCursor(); + + OSL_ENSURE(pSwCursor, "no SwCursor"); + + SwPosition aStartPos = *pSwCursor->GetPoint(), aEndPos = aStartPos; + + /* Search least and greatest position in current cursor ring. + */ + for(SwPaM& rTmpCursor : pSwCursor->GetRingContainer()) + { + SwCursor* pTmpCursor = static_cast<SwCursor *>(&rTmpCursor); + const SwPosition * pPt = pTmpCursor->GetPoint(), + * pMk = pTmpCursor->GetMark(); + + if (*pPt < aStartPos) + aStartPos = *pPt; + + if (*pPt > aEndPos) + aEndPos = *pPt; + + if (*pMk < aStartPos) + aStartPos = *pMk; + + if (*pMk > aEndPos) + aEndPos = *pMk; + + } + + KillPams(); + + /* @@@ semantic: SwCursor::operator=() is not implemented @@@ */ + + /* Set cursor to end of selection to ensure IsLastCellInRow works + properly. */ + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + /* Move the cursor out of the columns to delete and stay in the + same row. If the table has only one column the cursor will + stay in the row and the shell will take care of it. */ + if (IsLastCellInRow()) + { + /* If the cursor is in the last row of the table, first + try to move it to the previous cell. If that fails move + it to the next cell. */ + + { + SwCursor aTmpCursor( aStartPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + if (! pSwCursor->GoPrevCell()) + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + pSwCursor->GoNextCell(); + } + } + else + { + /* If the cursor is not in the last row of the table, first + try to move it to the next cell. If that fails move it + to the previous cell. */ + + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + if (! pSwCursor->GoNextCell()) + { + SwCursor aTmpCursor( aStartPos, nullptr ); + *pSwCursor = aTmpCursor; + pSwCursor->GoPrevCell(); + } + } +} + +void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + // pending drag & drop? + bool bAction = ActionPend(); + + CurrShell aCurr( this ); + StartAllAction(); + + // search boxes via the layout + SwSelBoxes aBoxes; + bool bSelectAll = StartsWith_() == StartsWith::Table && ExtendedSelectedAll(); + if (bSelectAll) + { + // Set the end of the selection to the last paragraph of the last cell of the table. + SwPaM* pPaM = getShellCursor(false); + SwNode* pNode = pPaM->Start()->GetNode().FindTableNode()->EndOfSectionNode(); + // pNode is the end node of the table, we want the last node before the end node of the last cell. + pPaM->End()->Assign( pNode->GetIndex() - 2 ); + } + GetTableSel( *this, aBoxes, SwTableSearchType::Row ); + + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + if ( !aBoxes.empty() ) + GetDoc()->InsertRow( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction ); + + EndAllActionAndCall(); +} + +void SwFEShell::InsertCol( sal_uInt16 nCnt, bool bBehind ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + CurrShell aCurr( this ); + + if( !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::Col ) ) + { + ErrorHandler::HandleError( ERR_TBLINSCOL_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + // pending drag & drop? + bool bAction = ActionPend(); + + StartAllAction(); + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Col ); + + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + if( !aBoxes.empty() ) + GetDoc()->InsertCol( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction ); + + EndAllActionAndCall(); +} + +// Determines if the current cursor is in the last row of the table. +bool SwFEShell::IsLastCellInRow() const +{ + SwTabCols aTabCols; + GetTabCols( aTabCols ); + bool bResult = false; + + if (IsTableRightToLeft()) + /* If the table is right-to-left the last row is the most left one. */ + bResult = 0 == GetCurTabColNum(); + else + /* If the table is left-to-right the last row is the most right one. */ + bResult = aTabCols.Count() == GetCurTabColNum(); + + return bResult; +} + +bool SwFEShell::DeleteCol() +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + + bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording(); + bool bRecordAndHideChanges = bRecordChanges && + GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines(); + + // tracked deletion: remove only textbox content, + // and set IsNoTracked table box property to false + if ( bRecordChanges ) + { + StartUndo(SwUndoId::COL_DELETE); + StartAllAction(); + + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + pWrtShell->SelectTableCol(); + + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Col ); + + TableWait aWait( 20, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + SwTableNode* pTableNd = pFrame->IsTextFrame() + ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode() + : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode(); + + for (size_t i = 0; i < aBoxes.size(); ++i) + { + SwTableBox *pBox = aBoxes[i]; + if ( pBox->GetSttNd() ) + { + SwNodeIndex aIdx( *pBox->GetSttNd(), 1 ); + SwCursor aCursor( SwPosition(aIdx), nullptr ); + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + GetDoc()->SetBoxAttr( aCursor, aHasTextChangesOnly ); + + // add dummy text content to the empty box for change tracking + if ( pBox->IsEmpty() ) + { + IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations(); + IDocumentRedlineAccess& rIDRA = GetDoc()->getIDocumentRedlineAccess(); + RedlineFlags eOld = rIDRA.GetRedlineFlags(); + rIDRA.SetRedlineFlags_intern(RedlineFlags::NONE); + rIDCO.InsertString( aCursor, OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) ); + aCursor.SetMark(); + aCursor.GetMark()->SetContent(0); + rIDRA.SetRedlineFlags_intern( eOld ); + rIDCO.DeleteAndJoin( aCursor ); + } + + } + } + + SwEditShell* pEditShell = GetDoc()->GetEditShell(); + pEditShell->Delete(); + + // remove cell frames in Hide Changes mode (and table frames, if needed) + if ( bRecordAndHideChanges ) + { + // remove all frames of the table, and make them again without the deleted ones + // TODO remove only the deleted frames + pTableNd->DelFrames(); + + if ( !pTableNd->GetTable().IsDeleted() ) + { + pTableNd->MakeOwnFrames(); + } + } + + EndAllActionAndCall(); + EndUndo(SwUndoId::COL_DELETE); + return true; + } + + StartAllAction(); + + // search boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + SwTableSearchType eSearchType = SwTableSearchType::Col; + + // NewModel tables already ExpandColumnSelection, so don't do it here also. + const SwContentNode* pContentNd = getShellCursor(false)->GetPointNode().GetContentNode(); + const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr; + if (pTableNd && pTableNd->GetTable().IsNewModel()) + eSearchType = SwTableSearchType::NONE; + + GetTableSel(*this, aBoxes, eSearchType); + if ( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // remove crsr from the deletion area. + // Put them behind/on the table; via the + // document position they will be put to the old position + while( !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + + ParkCursorInTab(); + + // then delete the column + StartUndo(SwUndoId::COL_DELETE); + bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); + EndUndo(SwUndoId::COL_DELETE); + } + else + bRet = false; + + EndAllActionAndCall(); + return bRet; +} + +void SwFEShell::DeleteTable() +{ + DeleteRow(true); +} + +bool SwFEShell::DeleteRow(bool bCompleteTable) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + + bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording(); + bool bRecordAndHideChanges = bRecordChanges && + GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines(); + + // tracked deletion: all rows have already had tracked row change in the table selection + if ( bRecordChanges && !SwDoc::HasRowNotTracked( *getShellCursor( false ) ) ) + return false; + + if ( bRecordChanges ) + StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + + StartAllAction(); + + // tracked deletion: remove only textbox content, + // and set HasTextChangesOnly table line property to false + SwEditShell* pEditShell = nullptr; + if ( bRecordChanges ) + { + pEditShell = GetDoc()->GetEditShell(); + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + GetDoc()->SetRowNotTracked( *getShellCursor( false ), aHasTextChangesOnly ); + + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + pWrtShell->SelectTableRow(); + + // don't need to remove the row frames in Show Changes mode + if ( !bRecordAndHideChanges ) + { + if ( pEditShell ) + pEditShell->Delete(false); + + EndAllActionAndCall(); + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + + return true; + } + } + + // search for boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Row ); + + if( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // Delete cursors from the deletion area. + // Then the cursor is: + // 1. the following row, if there is another row after this + // 2. the preceding row, if there is another row before this + // 3. otherwise below the table + { + SwTableNode* pTableNd = pFrame->IsTextFrame() + ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode() + : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode(); + + // search all boxes / lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( aBoxes, &aFndBox ); + ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara ); + } + + if( aFndBox.GetLines().empty() ) + { + EndAllActionAndCall(); + return false; + } + + KillPams(); + + FndBox_* pFndBox = &aFndBox; + while( 1 == pFndBox->GetLines().size() && + 1 == pFndBox->GetLines().front()->GetBoxes().size()) + { + FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get(); + if( pTmp->GetBox()->GetSttNd() ) + break; // otherwise too far + pFndBox = pTmp; + } + + SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine(); + SwTableBox* pDelBox = pDelLine->GetTabBoxes().back(); + while( !pDelBox->GetSttNd() ) + { + SwTableLine* pLn = pDelBox->GetTabLines().back(); + pDelBox = pLn->GetTabBoxes().back(); + } + SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(), + pDelBox ); + // skip deleted lines in Hide Changes mode with enabled change tracking + if ( bRecordAndHideChanges ) + { + SwRedlineTable::size_type nRedlinePos = 0; + while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) ) + pNextBox = pNextBox->GetUpper()->FindNextBox( pTableNd->GetTable(), + pNextBox->GetUpper()->GetTabBoxes().back() ); + } + + // skip protected cells + while( pNextBox && + pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox ); + + if( !pNextBox ) // no next? then the previous + { + pDelLine = pFndBox->GetLines().front()->GetLine(); + pDelBox = pDelLine->GetTabBoxes()[ 0 ]; + while( !pDelBox->GetSttNd() ) + pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0]; + pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(), + pDelBox ); + // skip previous deleted lines in Hide Changes mode with enabled change tracking + if ( bRecordAndHideChanges ) + { + SwRedlineTable::size_type nRedlinePos = 0; + while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) ) + { + pNextBox = pNextBox->GetUpper()->FindPreviousBox( pTableNd->GetTable(), + pNextBox->GetUpper()->GetTabBoxes()[0] ); + nRedlinePos = 0; + } + } + + // skip previous protected cells + while( pNextBox && + pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox ); + } + + // delete row content in Hide Changes mode + if ( pEditShell && bRecordAndHideChanges ) + { + // select the row deleted with change tracking cell by cell to handle + // the already deleted cells + SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this); + for (SwSelBoxes::size_type nBox = 0; pWrtShell && nBox < aBoxes.size(); ++nBox) + { + pWrtShell->SelectTableRow(); + SwCursor* pTableCursor = static_cast<SwCursor*>(GetTableCursor()); + auto pStt = aBoxes[nBox]; + if ( !pTableCursor ) + pTableCursor = GetCursor(true); + + if ( pTableCursor ) + { + // set start and end of the selection + pTableCursor->DeleteMark(); + pTableCursor->GetPoint()->Assign( *pStt->GetSttNd()->EndOfSectionNode() ); + pTableCursor->Move( fnMoveBackward, GoInContent ); + pWrtShell->UpdateCursor(); + } + + pEditShell->Delete(false); + } + } + + SwNodeOffset nIdx; + if( pNextBox ) // put cursor here + nIdx = pNextBox->GetSttIdx() + 1; + else // otherwise below the table + nIdx = pTableNd->EndOfSectionIndex() + 1; + + SwNodeIndex aIdx( GetDoc()->GetNodes(), nIdx ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = GetDoc()->GetNodes().GoNext( &aIdx ); + + // remove row frames in Hide Changes mode (and table frames, if needed) + if ( bRecordAndHideChanges ) + { + // remove all frames of the table, and make them again without the deleted ones + // TODO remove only the deleted frames + pTableNd->DelFrames(); + if ( !pTableNd->GetTable().IsDeleted() ) + { + pTableNd->MakeOwnFrames(); + } + + EndAllActionAndCall(); + + // put cursor + SwPaM* pPam = GetCursor(); + pPam->GetPoint()->Assign( *pCNd, 0 ); + pPam->SetMark(); // both want something + pPam->DeleteMark(); + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + { + pWrtShell->UpdateCursor(); + // tdf#150578 enable the disabled table toolbar by (zero) cursor moving + pWrtShell->Right( SwCursorSkipMode::Chars, false, 0, false ); + } + + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + return true; + } + else if( pCNd ) + { + // put cursor + SwPaM* pPam = GetCursor(); + pPam->GetPoint()->Assign( *pCNd, 0 ); + pPam->SetMark(); // both want something + pPam->DeleteMark(); + } + } + + // now delete the lines + StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + bRet = GetDoc()->DeleteRowCol( aBoxes ); + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + } + else + bRet = false; + + EndAllActionAndCall(); + return bRet; +} + +TableMergeErr SwFEShell::MergeTab() +{ + // check if Point/Mark of current cursor are in a table + TableMergeErr nRet = TableMergeErr::NoSelection; + if( IsTableMode() ) + { + SwShellTableCursor* pTableCursor = GetTableCursor(); + const SwTableNode* pTableNd = pTableCursor->GetPointNode().FindTableNode(); + if( dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + } + else + { + CurrShell aCurr( this ); + StartAllAction(); + + TableWait aWait(pTableCursor->GetSelectedBoxesCount(), nullptr, + *GetDoc()->GetDocShell(), + pTableNd->GetTable().GetTabLines().size() ); + + nRet = GetDoc()->MergeTable( *pTableCursor ); + + KillPams(); + + EndAllActionAndCall(); + } + } + return nRet; +} + +void SwFEShell::SplitTab( bool bVert, sal_uInt16 nCnt, bool bSameHeight ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + CurrShell aCurr( this ); + + if( bVert && !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::NONE ) ) + { + ErrorHandler::HandleError( ERR_TBLSPLIT_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + StartAllAction(); + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes ); + if( !aBoxes.empty() ) + { + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + // now delete the columns + GetDoc()->SplitTable( aBoxes, bVert, nCnt, bSameHeight ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + } + EndAllActionAndCall(); +} + +void SwFEShell::GetTabCols_(SwTabCols &rToFill, const SwFrame *pBox) const +{ + const SwTabFrame *pTab = pBox->FindTabFrame(); + if (m_pColumnCache) + { + bool bDel = true; + if (m_pColumnCache->pLastTable == pTab->GetTable()) + { + bDel = false; + SwRectFnSet aRectFnSet(pTab); + + const SwPageFrame* pPage = pTab->FindPageFrame(); + const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + + if (m_pColumnCache->pLastTabFrame != pTab) + { + // if TabFrame was changed, we only shift a little bit + // as the width is the same + SwRectFnSet fnRectX(m_pColumnCache->pLastTabFrame); + if (fnRectX.GetWidth(m_pColumnCache->pLastTabFrame->getFrameArea()) == + aRectFnSet.GetWidth(pTab->getFrameArea()) ) + { + m_pColumnCache->pLastCols->SetLeftMin( nLeftMin ); + + m_pColumnCache->pLastTabFrame = pTab; + } + else + bDel = true; + } + + if ( !bDel && + m_pColumnCache->pLastCols->GetLeftMin () == o3tl::narrowing<sal_uInt16>(nLeftMin) && + m_pColumnCache->pLastCols->GetLeft () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) && + m_pColumnCache->pLastCols->GetRight () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&& + m_pColumnCache->pLastCols->GetRightMax() == o3tl::narrowing<sal_uInt16>(nRightMax) - m_pColumnCache->pLastCols->GetLeftMin() ) + { + if (m_pColumnCache->pLastCellFrame != pBox) + { + pTab->GetTable()->GetTabCols( *m_pColumnCache->pLastCols, + static_cast<const SwCellFrame*>(pBox)->GetTabBox(), true); + m_pColumnCache->pLastCellFrame = pBox; + } + rToFill = *m_pColumnCache->pLastCols; + } + else + bDel = true; + } + if ( bDel ) + m_pColumnCache.reset(); + } + if (!m_pColumnCache) + { + SwDoc::GetTabCols( rToFill, static_cast<const SwCellFrame*>(pBox) ); + + m_pColumnCache.reset(new SwColCache); + m_pColumnCache->pLastCols.reset(new SwTabCols(rToFill)); + m_pColumnCache->pLastTable = pTab->GetTable(); + m_pColumnCache->pLastTabFrame = pTab; + m_pColumnCache->pLastCellFrame = pBox; + } +} + +void SwFEShell::GetTabRows_(SwTabCols &rToFill, const SwFrame *pBox) const +{ + const SwTabFrame *pTab = pBox->FindTabFrame(); + if (m_pRowCache) + { + bool bDel = true; + if (m_pRowCache->pLastTable == pTab->GetTable()) + { + bDel = false; + SwRectFnSet aRectFnSet(pTab); + const SwPageFrame* pPage = pTab->FindPageFrame(); + const tools::Long nLeftMin = ( aRectFnSet.IsVert() ? + pTab->GetPrtLeft() - pPage->getFrameArea().Left() : + pTab->GetPrtTop() - pPage->getFrameArea().Top() ); + const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0; + const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea()); + const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX; + + if (m_pRowCache->pLastTabFrame != pTab || m_pRowCache->pLastCellFrame != pBox) + bDel = true; + + if ( !bDel && + m_pRowCache->pLastCols->GetLeftMin () == nLeftMin && + m_pRowCache->pLastCols->GetLeft () == nLeft && + m_pRowCache->pLastCols->GetRight () == nRight && + m_pRowCache->pLastCols->GetRightMax() == nRightMax ) + { + rToFill = *m_pRowCache->pLastCols; + } + else + bDel = true; + } + if ( bDel ) + m_pRowCache.reset(); + } + if (!m_pRowCache) + { + SwDoc::GetTabRows( rToFill, static_cast<const SwCellFrame*>(pBox) ); + + m_pRowCache.reset(new SwColCache); + m_pRowCache->pLastCols.reset(new SwTabCols(rToFill)); + m_pRowCache->pLastTable = pTab->GetTable(); + m_pRowCache->pLastTabFrame = pTab; + m_pRowCache->pLastCellFrame = pBox; + } +} + +void SwFEShell::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly ) +{ + SwFrame *pBox = GetCurrFrame(); + if( !pBox || !pBox->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do + { + pBox = pBox->GetUpper(); + } while (pBox && !pBox->IsCellFrame()); + + GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<SwCellFrame*>(pBox) ); + EndAllActionAndCall(); +} + +void SwFEShell::GetTabCols( SwTabCols &rToFill ) const +{ + const SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + do + { + pFrame = pFrame->GetUpper(); + } + while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return; + + GetTabCols_( rToFill, pFrame ); +} + +void SwFEShell::GetTabRows( SwTabCols &rToFill ) const +{ + const SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + do + { + pFrame = pFrame->GetUpper(); + } while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return; + + GetTabRows_( rToFill, pFrame ); +} + +void SwFEShell::SetTabRows( const SwTabCols &rNew, bool bCurColOnly ) +{ + SwFrame *pBox = GetCurrFrame(); + if( !pBox || !pBox->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do + { + pBox = pBox->GetUpper(); + } while (pBox && !pBox->IsCellFrame()); + + GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<SwCellFrame*>(pBox) ); + EndAllActionAndCall(); +} + +void SwFEShell::GetMouseTabRows( SwTabCols &rToFill, const Point &rPt ) const +{ + const SwFrame *pBox = GetBox( rPt ); + if ( pBox ) + GetTabRows_( rToFill, pBox ); +} + +void SwFEShell::SetMouseTabRows( const SwTabCols &rNew, bool bCurColOnly, const Point &rPt ) +{ + const SwFrame *pBox = GetBox( rPt ); + if( pBox ) + { + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) ); + EndAllActionAndCall(); + } +} + +void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowSplit( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +std::unique_ptr<SwFormatRowSplit> SwFEShell::GetRowSplit() const +{ + return SwDoc::GetRowSplit( *getShellCursor( false ) ); +} + +void SwFEShell::SetRowHeight( const SwFormatFrameSize &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowHeight( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +std::unique_ptr<SwFormatFrameSize> SwFEShell::GetRowHeight() const +{ + return SwDoc::GetRowHeight( *getShellCursor( false ) ); +} + +bool SwFEShell::BalanceRowHeight( bool bTstOnly, const bool bOptimize ) +{ + CurrShell aCurr( this ); + if( !bTstOnly ) + StartAllAction(); + bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize ); + if( !bTstOnly ) + EndAllActionAndCall(); + return bRet; +} + +void SwFEShell::SetRowBackground( const SvxBrushItem &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowBackground( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetRowBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + return SwDoc::GetRowBackground( *getShellCursor( false ), rToFill ); +} + +void SwFEShell::SetTabBorders( const SfxItemSet& rSet ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabBorders( *getShellCursor( false ), rSet ); + EndAllActionAndCall(); +} + +void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine, + const editeng::SvxBorderLine* pBorderLine ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabLineStyle( *getShellCursor( false ), + pColor, bSetLine, pBorderLine ); + EndAllActionAndCall(); +} + +void SwFEShell::GetTabBorders( SfxItemSet& rSet ) const +{ + SwDoc::GetTabBorders( *getShellCursor( false ), rSet ); +} + +void SwFEShell::SetBoxBackground( const SvxBrushItem &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetBoxBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill); + bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp)); + rToFill.reset(static_cast<SvxBrushItem*>(aTemp.release())); + return bRetval; +} + +void SwFEShell::SetBoxDirection( const SvxFrameDirectionItem& rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetBoxDirection( std::unique_ptr<SvxFrameDirectionItem>& rToFill ) const +{ + std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill); + bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp)); + rToFill.reset(static_cast<SvxFrameDirectionItem*>(aTemp.release())); + return bRetval; +} + +void SwFEShell::SetBoxAlign( sal_uInt16 nAlign ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAlign( *getShellCursor( false ), nAlign ); + EndAllActionAndCall(); +} + +sal_uInt16 SwFEShell::GetBoxAlign() const +{ + return SwDoc::GetBoxAlign( *getShellCursor( false ) ); +} + +void SwFEShell::SetTabBackground( const SvxBrushItem &rNew ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetAttr( rNew, *pFrame->ImplFindTabFrame()->GetFormat() ); + EndAllAction(); // no call, nothing changes! + GetDoc()->getIDocumentState().SetModified(); +} + +void SwFEShell::GetTabBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + rToFill = pFrame->ImplFindTabFrame()->GetFormat()->makeBackgroundBrushItem(); +} + +bool SwFEShell::HasWholeTabSelection() const +{ + // whole table selected? + if ( IsTableMode() ) + { + SwSelBoxes aBoxes; + ::GetTableSelCrs( *this, aBoxes ); + if( !aBoxes.empty() ) + { + const SwTableNode *pTableNd = IsCursorInTable(); + return pTableNd && + aBoxes[0]->GetSttIdx() - 1 == pTableNd->EndOfSectionNode()->StartOfSectionIndex() && + aBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1 == pTableNd->EndOfSectionIndex(); + } + } + return false; +} + +bool SwFEShell::HasBoxSelection() const +{ + if(!IsCursorInTable()) + return false; + // whole table selected? + if( IsTableMode() ) + return true; + SwPaM* pPam = GetCursor(); + // empty boxes are also selected as the absence of selection + bool bChg = false; + if( pPam->GetPoint() == pPam->End()) + { + bChg = true; + pPam->Exchange(); + } + SwNode* pNd; + if( pPam->GetPoint()->GetNodeIndex() -1 == + ( pNd = &pPam->GetPointNode())->StartOfSectionIndex() && + !pPam->GetPoint()->GetContentIndex() && + pPam->GetMark()->GetNodeIndex() + 1 == + pNd->EndOfSectionIndex()) + { + SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + { + pCNd = SwNodes::GoPrevious( &aIdx ); + assert(pCNd && "no ContentNode in box ??"); + } + if( pPam->GetMark()->GetContentIndex() == pCNd->Len() ) + { + if( bChg ) + pPam->Exchange(); + return true; + } + } + if( bChg ) + pPam->Exchange(); + return false; +} + +void SwFEShell::ProtectCells() +{ + SvxProtectItem aProt( RES_PROTECT ); + aProt.SetContentProtect( true ); + + CurrShell aCurr( this ); + StartAllAction(); + + GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt ); + + if( !IsCursorReadonly() ) + { + if( IsTableMode() ) + ClearMark(); + ParkCursorInTab(); + } + EndAllActionAndCall(); +} + +// cancel table selection +void SwFEShell::UnProtectCells() +{ + CurrShell aCurr( this ); + StartAllAction(); + + SwSelBoxes aBoxes; + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + SwFrame *pFrame = GetCurrFrame(); + do { + pFrame = pFrame->GetUpper(); + } while ( pFrame && !pFrame->IsCellFrame() ); + if( pFrame ) + { + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + } + + if( !aBoxes.empty() ) + GetDoc()->UnProtectCells( aBoxes ); + + EndAllActionAndCall(); +} + +void SwFEShell::UnProtectTables() +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->UnProtectTables( *GetCursor() ); + EndAllActionAndCall(); +} + +bool SwFEShell::HasTableAnyProtection( const OUString* pTableName, + bool* pFullTableProtection ) +{ + return GetDoc()->HasTableAnyProtection( GetCursor()->GetPoint(), pTableName, + pFullTableProtection ); +} + +bool SwFEShell::CanUnProtectCells() const +{ + bool bUnProtectAvailable = false; + const SwTableNode *pTableNd = IsCursorInTable(); + if( pTableNd && !pTableNd->IsProtect() ) + { + SwSelBoxes aBoxes; + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + SwFrame *pFrame = GetCurrFrame(); + do { + if ( pFrame ) + pFrame = pFrame->GetUpper(); + } while ( pFrame && !pFrame->IsCellFrame() ); + if( pFrame ) + { + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + } + if( !aBoxes.empty() ) + bUnProtectAvailable = ::HasProtectedCells( aBoxes ); + } + return bUnProtectAvailable; +} + +sal_uInt16 SwFEShell::GetRowsToRepeat() const +{ + const SwFrame *pFrame = GetCurrFrame(); + const SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr; + if( pTab ) + return pTab->GetTable()->GetRowsToRepeat(); + return 0; +} + +void SwFEShell::SetRowsToRepeat( sal_uInt16 nSet ) +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr; + if( pTab && pTab->GetTable()->GetRowsToRepeat() != nSet ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowsToRepeat( *pTab->GetTable(), nSet ); + EndAllActionAndCall(); + } +} + +// returns the number of rows consecutively selected from top +static sal_uInt16 lcl_GetRowNumber( const SwPosition& rPos ) +{ + Point aTmpPt; + const SwContentNode *pNd; + const SwContentFrame *pFrame; + + std::pair<Point, bool> const tmp(aTmpPt, false); + pNd = rPos.GetNode().GetContentNode(); + if( nullptr != pNd ) + pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp); + else + pFrame = nullptr; + + const SwFrame* pRow = (pFrame && pFrame->IsInTab()) ? pFrame->GetUpper() : nullptr; + + while (pRow && (!pRow->GetUpper() || !pRow->GetUpper()->IsTabFrame())) + pRow = pRow->GetUpper(); + + if (!pRow) + return USHRT_MAX; + + const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper()); + const SwTableLine* pTabLine = static_cast<const SwRowFrame*>(pRow)->GetTabLine(); + sal_uInt16 nRet = USHRT_MAX; + sal_uInt16 nI = 0; + while ( sal::static_int_cast<SwTableLines::size_type>(nI) < pTabFrame->GetTable()->GetTabLines().size() ) + { + if ( pTabFrame->GetTable()->GetTabLines()[ nI ] == pTabLine ) + { + nRet = nI; + break; + } + ++nI; + } + + return nRet; +} + +sal_uInt16 SwFEShell::GetRowSelectionFromTop() const +{ + sal_uInt16 nRet = 0; + const SwPaM* pPaM = IsTableMode() ? GetTableCursor() : GetCursor_(); + const sal_uInt16 nPtLine = lcl_GetRowNumber( *pPaM->GetPoint() ); + + if ( !IsTableMode() ) + { + nRet = 0 == nPtLine ? 1 : 0; + } + else + { + const sal_uInt16 nMkLine = lcl_GetRowNumber( *pPaM->GetMark() ); + + if ( ( nPtLine == 0 && nMkLine != USHRT_MAX ) || + ( nMkLine == 0 && nPtLine != USHRT_MAX ) ) + { + nRet = std::max( nPtLine, nMkLine ) + 1; + } + } + + return nRet; +} + +/* + * 1. case: bRepeat = true + * returns true if the current frame is located inside a table headline in + * a follow frame + * + * 2. case: bRepeat = false + * returns true if the current frame is located inside a table headline OR + * inside the first line of a table!!! + */ +bool SwFEShell::CheckHeadline( bool bRepeat ) const +{ + bool bRet = false; + if ( !IsTableMode() ) + { + SwFrame *pFrame = GetCurrFrame(); // DONE MULTIIHEADER + SwTabFrame* pTab = (pFrame && pFrame->IsInTab()) ? pFrame->FindTabFrame() : nullptr; + if (pTab) + { + if ( bRepeat ) + { + bRet = pTab->IsFollow() && pTab->IsInHeadline( *pFrame ); + } + else + { + bRet = static_cast<SwLayoutFrame*>(pTab->Lower())->IsAnLower( pFrame ) || + pTab->IsInHeadline( *pFrame ); + } + } + } + return bRet; +} + +void SwFEShell::AdjustCellWidth( const bool bBalance, const bool bNoShrink ) +{ + CurrShell aCurr( this ); + StartAllAction(); + + // switch on wait-cursor, as we do not know how + // much content is affected + TableWait aWait(std::numeric_limits<size_t>::max(), nullptr, + *GetDoc()->GetDocShell()); + + GetDoc()->AdjustCellWidth( *getShellCursor( false ), bBalance, bNoShrink ); + EndAllActionAndCall(); +} + +bool SwFEShell::IsAdjustCellWidthAllowed( bool bBalance ) const +{ + // at least one row with content should be contained in the selection + + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + SwSelBoxes aBoxes; + ::GetTableSelCrs( *this, aBoxes ); + + if ( bBalance ) + return aBoxes.size() > 1; + + if ( aBoxes.empty() ) + { + do + { + pFrame = pFrame->GetUpper(); + } + while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return false; + + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + + for (size_t i = 0; i < aBoxes.size(); ++i) + { + SwTableBox *pBox = aBoxes[i]; + if ( pBox->GetSttNd() ) + { + SwNodeIndex aIdx( *pBox->GetSttNd(), 1 ); + SwTextNode* pCNd = aIdx.GetNode().GetTextNode(); + if( !pCNd ) + pCNd = static_cast<SwTextNode*>(GetDoc()->GetNodes().GoNext( &aIdx )); + + while ( pCNd ) + { + if (!pCNd->GetText().isEmpty()) + return true; + ++aIdx; + pCNd = aIdx.GetNode().GetTextNode(); + } + } + } + return false; +} + +void SwFEShell::SetTableStyle(const OUString& rStyleName) +{ + // make sure SwDoc has the style + SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName); + if (!pTableFormat) + return; + + SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode) + return; + + // set the name & update + UpdateTableStyleFormatting(pTableNode, false, &rStyleName); +} + + // AutoFormat for the table/table selection +bool SwFEShell::SetTableStyle(const SwTableAutoFormat& rStyle) +{ + // make sure SwDoc has the style + GetDoc()->GetTableStyles().AddAutoFormat(rStyle); + + SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode) + return false; + + // set the name & update + return UpdateTableStyleFormatting(pTableNode, false, &rStyle.GetName()); +} + +bool SwFEShell::UpdateTableStyleFormatting(SwTableNode *pTableNode, + bool bResetDirect, OUString const*const pStyleName) +{ + if (!pTableNode) + { + pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode || pTableNode->GetTable().IsTableComplex()) + return false; + } + + OUString const aTableStyleName(pStyleName + ? *pStyleName + : pTableNode->GetTable().GetTableStyleName()); + SwTableAutoFormat* pTableStyle = GetDoc()->GetTableStyles().FindAutoFormat(aTableStyleName); + if (!pTableStyle) + return false; + + SwSelBoxes aBoxes; + + // whole table or only current selection + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + const SwTableSortBoxes& rTBoxes = pTableNode->GetTable().GetTabSortBoxes(); + for (size_t n = 0; n < rTBoxes.size(); ++n) + { + SwTableBox* pBox = rTBoxes[ n ]; + aBoxes.insert( pBox ); + } + } + + bool bRet; + if( !aBoxes.empty() ) + { + CurrShell aCurr( this ); + StartAllAction(); + bRet = GetDoc()->SetTableAutoFormat( + aBoxes, *pTableStyle, bResetDirect, pStyleName != nullptr); + ClearFEShellTabCols(*GetDoc(), nullptr); + EndAllActionAndCall(); + } + else + bRet = false; + return bRet; +} + +bool SwFEShell::GetTableAutoFormat( SwTableAutoFormat& rGet ) +{ + const SwTableNode *pTableNd = IsCursorInTable(); + if( !pTableNd || pTableNd->GetTable().IsTableComplex() ) + return false; + + SwSelBoxes aBoxes; + + if ( !IsTableMode() ) // if cursor are not current + GetCursor(); + + // whole table or only current selection + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + const SwTableSortBoxes& rTBoxes = pTableNd->GetTable().GetTabSortBoxes(); + for (size_t n = 0; n < rTBoxes.size(); ++n) + { + SwTableBox* pBox = rTBoxes[ n ]; + aBoxes.insert( pBox ); + } + } + + return GetDoc()->GetTableAutoFormat( aBoxes, rGet ); +} + +bool SwFEShell::DeleteTableSel() +{ + // check if SPoint/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + StartAllAction(); + + // search boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + GetTableSelCrs( *this, aBoxes ); + if( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // cursor should be removed from deletion area. + // Put them behind/on the table; via the document + // position they'll be set to the old position + while( !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + ParkCursor( *static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetSttNd() ); + + bRet = GetDoc()->DeleteRowCol( aBoxes ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + } + else + bRet = false; + EndAllActionAndCall(); + return bRet; +} + +size_t SwFEShell::GetCurTabColNum() const +{ + //!!!GetCurMouseTabColNum() mitpflegen!!!! + SwFrame *pFrame = GetCurrFrame(); + OSL_ENSURE( pFrame, "Cursor parked?" ); + + // check if SPoint/Mark of current cursor are in a table + if (!pFrame || !pFrame->IsInTab()) + return 0; + + do + { + // JP 26.09.95: why compare with ContentFrame + // and not with CellFrame ???? + pFrame = pFrame->GetUpper(); + } while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return 0; + + size_t nRet = 0; + + SwRectFnSet aRectFnSet(pFrame); + + const SwPageFrame* pPage = pFrame->FindPageFrame(); + + // get TabCols, as only via these we get to the position + SwTabCols aTabCols; + GetTabCols( aTabCols ); + + if( pFrame->FindTabFrame()->IsRightToLeft() ) + { + tools::Long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea()); + + const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight(); + + if ( !::IsSame( nX, nRight ) ) + { + nX = nRight - nX + aTabCols.GetLeft(); + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + else + { + const tools::Long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + + const tools::Long nLeft = aTabCols.GetLeftMin(); + + if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + { + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + return nRet; +} + +static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy ) +{ + const SwFrame *pFrame = pLay->Lower(); + + while( pFrame && pLay->IsAnLower( pFrame ) ) + { + if ( pFrame->getFrameArea().IsNear( rPt, nFuzzy ) ) + { + if ( pFrame->IsLayoutFrame() ) + { + const SwFrame *pTmp = ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), rPt, nFuzzy ); + if ( pTmp ) + return pTmp; + } + + return pFrame; + } + + pFrame = pFrame->FindNext(); + } + + return nullptr; +} + +static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point &rPt, + SwTwips nFuzzy, bool* pbRow, bool* pbCol ) +{ + // bMouseMoveRowCols : + // Method is called for + // - Moving columns/rows with the mouse or + // - Enhanced table selection + const bool bMouseMoveRowCols = nullptr == pbCol; + + bool bCloseToRow = false; + bool bCloseToCol = false; + + const SwFrame *pFrame = pLay->ContainsContent(); + const SwFrame* pRet = nullptr; + + if ( pFrame ) + { + do + { + if ( pFrame->IsInTab() ) + pFrame = const_cast<SwFrame*>(pFrame)->ImplFindTabFrame(); + + if (!pFrame) + break; + + if ( pFrame->IsTabFrame() ) + { + Point aPt( rPt ); + bool bSearchForFrameInTab = true; + SwTwips nTmpFuzzy = nFuzzy; + + if ( !bMouseMoveRowCols ) + { + // We ignore nested tables for the enhanced table selection: + while ( pFrame->GetUpper()->IsInTab() ) + pFrame = pFrame->GetUpper()->FindTabFrame(); + + // We first check if the given point is 'close' to the left or top + // border of the table frame: + OSL_ENSURE( pFrame, "Nested table frame without outer table" ); + SwRectFnSet aRectFnSet(pFrame); + const bool bRTL = pFrame->IsRightToLeft(); + + SwRect aTabRect = pFrame->getFramePrintArea(); + aTabRect.Pos() += pFrame->getFrameArea().Pos(); + + const SwTwips nLeft = bRTL ? + aRectFnSet.GetRight(aTabRect) : + aRectFnSet.GetLeft(aTabRect); + const SwTwips nTop = aRectFnSet.GetTop(aTabRect); + + SwTwips const rPointX = aRectFnSet.IsVert() ? aPt.Y() : aPt.X(); + SwTwips const rPointY = aRectFnSet.IsVert() ? aPt.X() : aPt.Y(); + + const SwTwips nXDiff = aRectFnSet.XDiff( nLeft, rPointX ) * ( bRTL ? -1 : 1 ); + const SwTwips nYDiff = aRectFnSet.YDiff( nTop, rPointY ); + + bCloseToRow = nXDiff >= 0 && nXDiff < nFuzzy; + bCloseToCol = nYDiff >= 0 && nYDiff < nFuzzy; + + if ( bCloseToCol && 2 * nYDiff > nFuzzy ) + { + const SwFrame* pPrev = pFrame->GetPrev(); + if ( pPrev ) + { + SwRect aPrevRect = pPrev->getFramePrintArea(); + aPrevRect.Pos() += pPrev->getFrameArea().Pos(); + + if( aPrevRect.Contains( rPt ) ) + { + bCloseToCol = false; + } + } + + } + + // If we found the point to be 'close' to the left or top border + // of the table frame, we adjust the point to be on that border: + if ( bCloseToRow && bCloseToCol ) + aPt = bRTL ? aTabRect.TopRight() : aRectFnSet.GetPos(aTabRect); + else if ( bCloseToRow ) + aRectFnSet.IsVert() ? aPt.setY(nLeft) : aPt.setX(nLeft); + else if ( bCloseToCol ) + aRectFnSet.IsVert() ? aPt.setX(nTop) : aPt.setY(nTop); + + if ( !bCloseToRow && !bCloseToCol ) + bSearchForFrameInTab = false; + + // Since the point has been adjusted, we call lcl_FindFrameInTab() + // with a fuzzy value of 1: + nTmpFuzzy = 1; + } + + const SwFrame* pTmp = bSearchForFrameInTab ? + ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), aPt, nTmpFuzzy ) : + nullptr; + + if ( pTmp ) + { + pFrame = pTmp; + break; + } + } + pFrame = pFrame->FindNextCnt(); + + } while ( pFrame && pLay->IsAnLower( pFrame ) ); + } + + if ( pFrame && pFrame->IsInTab() && pLay->IsAnLower( pFrame ) ) + { + do + { + // We allow mouse drag of table borders within nested tables, + // but disallow hotspot selection of nested tables. + if ( bMouseMoveRowCols ) + { + // find the next cell frame + while ( pFrame && !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + } + else + { + // find the most upper cell frame: + while ( pFrame && + ( !pFrame->IsCellFrame() || + !pFrame->GetUpper()->GetUpper()->IsTabFrame() || + pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) ) + pFrame = pFrame->GetUpper(); + } + + if ( pFrame ) // Note: this condition should be the same like the while condition!!! + { + // #i32329# Enhanced table selection + // used for hotspot selection of tab/cols/rows + if ( !bMouseMoveRowCols ) + { + + OSL_ENSURE( pbCol && pbRow, "pbCol or pbRow missing" ); + + if ( bCloseToRow || bCloseToCol ) + { + *pbRow = bCloseToRow; + *pbCol = bCloseToCol; + pRet = pFrame; + break; + } + } + else + { + // used for mouse move of columns/rows + const SwTabFrame* pTabFrame = pFrame->FindTabFrame(); + SwRect aTabRect = pTabFrame->getFramePrintArea(); + aTabRect.Pos() += pTabFrame->getFrameArea().Pos(); + + SwRectFnSet aRectFnSet(pTabFrame); + + const SwTwips nTabTop = aRectFnSet.GetTop(aTabRect); + const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y(); + + // Do not allow to drag upper table border: + if ( !::IsSame( nTabTop, nMouseTop ) ) + { + if ( ::IsSame( pFrame->getFrameArea().Left(), rPt.X() ) || + ::IsSame( pFrame->getFrameArea().Right(),rPt.X() ) ) + { + if ( pbRow ) *pbRow = false; + pRet = pFrame; + break; + } + if ( ::IsSame( pFrame->getFrameArea().Top(), rPt.Y() ) || + ::IsSame( pFrame->getFrameArea().Bottom(),rPt.Y() ) ) + { + if ( pbRow ) *pbRow = true; + pRet = pFrame; + break; + } + } + } + + pFrame = pFrame->GetUpper(); + } + } while ( pFrame ); + } + + // robust: + OSL_ENSURE( !pRet || pRet->IsCellFrame(), "lcl_FindFrame() is supposed to find a cell frame!" ); + return pRet && pRet->IsCellFrame() ? static_cast<const SwCellFrame*>(pRet) : nullptr; +} + +// pbCol = 0 => Used for moving table rows/cols with mouse +// pbCol != 0 => Used for selecting table/rows/cols + +#define ENHANCED_TABLE_SELECTION_FUZZY 10 + +const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) const +{ + const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower()); + vcl::Window* pOutWin = GetWin(); + SwTwips nFuzzy = COLFUZZY; + if( pOutWin ) + { + // #i32329# Enhanced table selection + SwTwips nSize = pbCol ? ENHANCED_TABLE_SELECTION_FUZZY : RULER_MOUSE_MARGINWIDTH; + Size aTmp( nSize, nSize ); + aTmp = pOutWin->PixelToLogic( aTmp ); + nFuzzy = aTmp.Width(); + } + + while ( pPage && !pPage->getFrameArea().IsNear( rPt, nFuzzy ) ) + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + + const SwCellFrame *pFrame = nullptr; + if ( pPage ) + { + // We cannot search the box by GetModelPositionForViewPoint or GetContentPos. + // This would lead to a performance collapse for documents + // with a lot of paragraphs/tables on one page + //(BrowseMode!) + + // check flys first + if ( pPage->GetSortedObjs() ) + { + for ( size_t i = 0; !pFrame && i < pPage->GetSortedObjs()->size(); ++i ) + { + SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i]; + if ( auto pFlyFrame = pObj->DynCastFlyFrame() ) + { + pFrame = lcl_FindFrame( pFlyFrame, rPt, nFuzzy, pbRow, pbCol ); + } + } + } + const SwLayoutFrame *pLay = static_cast<const SwLayoutFrame*>(pPage->Lower()); + while ( pLay && !pFrame ) + { + pFrame = lcl_FindFrame( pLay, rPt, nFuzzy, pbRow, pbCol ); + pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext()); + } + } + return pFrame; +} + +/* Helper function*/ +/* calculated the distance between Point rC and Line Segment (rA, rB) */ +static double lcl_DistancePoint2Segment( const Point& rA, const Point& rB, const Point& rC ) +{ + double nRet = 0; + + const basegfx::B2DVector aBC( rC.X() - rB.X(), rC.Y() - rB.Y() ); + const basegfx::B2DVector aAB( rB.X() - rA.X(), rB.Y() - rA.Y() ); + const double nDot1 = aBC.scalar( aAB ); + + if ( nDot1 > 0 ) // check outside case 1 + nRet = aBC.getLength(); + else + { + const basegfx::B2DVector aAC( rC.X() - rA.X(), rC.Y() - rA.Y() ); + const basegfx::B2DVector aBA( rA.X() - rB.X(), rA.Y() - rB.Y() ); + const double nDot2 = aAC.scalar( aBA ); + + if ( nDot2 > 0 ) // check outside case 2 + nRet = aAC.getLength(); + else + { + const double nDiv = aAB.getLength(); + nRet = nDiv ? aAB.cross( aAC ) / nDiv : 0; + } + } + + return std::abs(nRet); +} + +/* Helper function*/ +static Point lcl_ProjectOntoClosestTableFrame( const SwTabFrame& rTab, const Point& rPoint, bool bRowDrag ) +{ + Point aRet( rPoint ); + const SwTabFrame* pCurrentTab = &rTab; + const bool bVert = pCurrentTab->IsVertical(); + const bool bRTL = pCurrentTab->IsRightToLeft(); + + // Western Layout: + // bRowDrag = true => compare to left border of table + // bRowDrag = false => compare to top border of table + + // Asian Layout: + // bRowDrag = true => compare to right border of table + // bRowDrag = false => compare to top border of table + + // RTL Layout: + // bRowDrag = true => compare to right border of table + // bRowDrag = false => compare to top border of table + bool bLeft = false; + bool bRight = false; + + if ( bRowDrag ) + { + if ( bVert || bRTL ) + bRight = true; + else + bLeft = true; + } + + // used to find the minimal distance + double nMin = -1; + Point aMin1; + Point aMin2; + + Point aS1; + Point aS2; + + while ( pCurrentTab ) + { + SwRect aTabRect( pCurrentTab->getFramePrintArea() ); + aTabRect += pCurrentTab->getFrameArea().Pos(); + + if ( bLeft ) + { + // distance to left table border + aS1 = aTabRect.TopLeft(); + aS2 = aTabRect.BottomLeft(); + } + else if ( bRight ) + { + // distance to right table border + aS1 = aTabRect.TopRight(); + aS2 = aTabRect.BottomRight(); + } + else //if ( bTop ) + { + // distance to top table border + aS1 = aTabRect.TopLeft(); + aS2 = aTabRect.TopRight(); + } + + const double nDist = lcl_DistancePoint2Segment( aS1, aS2, rPoint ); + + if ( nDist < nMin || -1 == nMin ) + { + aMin1 = aS1; + aMin2 = aS2; + nMin = nDist; + } + + pCurrentTab = pCurrentTab->GetFollow(); + } + + // project onto closest line: + if ( bLeft || bRight ) + { + aRet.setX(aMin1.getX()); + if ( aRet.getY() > aMin2.getY() ) + aRet.setY(aMin2.getY()); + else if ( aRet.getY() < aMin1.getY() ) + aRet.setY(aMin1.getY()); + } + else + { + aRet.setY(aMin1.getY()); + if ( aRet.getX() > aMin2.getX() ) + aRet.setX(aMin2.getX()); + else if ( aRet.getX() < aMin1.getX() ) + aRet.setX(aMin1.getX()); + } + + return aRet; +} + +// #i32329# Enhanced table selection +bool SwFEShell::SelTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag ) +{ + bool bRet = false; + Point aEndPt; + if ( pEnd ) + aEndPt = *pEnd; + + SwPosition* ppPos[2] = { nullptr, nullptr }; + Point paPt [2] = { rPt, aEndPt }; + bool pbRow[2] = { false, false }; + bool pbCol[2] = { false, false }; + + // pEnd is set during dragging. + for ( sal_uInt16 i = 0; i < ( pEnd ? 2 : 1 ); ++i ) + { + const SwCellFrame* pFrame = + static_cast<const SwCellFrame*>(GetBox( paPt[i], &pbRow[i], &pbCol[i] ) ); + + if( pFrame ) + { + while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() ) + pFrame = static_cast<const SwCellFrame*>( static_cast<const SwLayoutFrame*>( pFrame->Lower() )->Lower() ); + if( pFrame && pFrame->GetTabBox()->GetSttNd() && + pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() ) + pFrame = nullptr; + } + + if ( pFrame ) + { + const SwContentFrame* pContent = ::GetCellContent( *pFrame ); + + if ( pContent && pContent->IsTextFrame() ) + { + + ppPos[i] = new SwPosition(static_cast<SwTextFrame const*>(pContent)->MapViewToModelPos(TextFrameIndex(0))); + + // paPt[i] will not be used any longer, now we use it to store + // a position inside the content frame + paPt[i] = pContent->getFrameArea().Center(); + } + } + + // no calculation of end frame if start frame has not been found. + if ( 1 == i || !ppPos[0] || !pEnd || !pFrame ) + break; + + // find 'closest' table frame to pEnd: + const SwTabFrame* pCurrentTab = pFrame->FindTabFrame(); + if ( pCurrentTab->IsFollow() ) + pCurrentTab = pCurrentTab->FindMaster( true ); + + const Point aProjection = lcl_ProjectOntoClosestTableFrame( *pCurrentTab, *pEnd, bRowDrag ); + paPt[1] = aProjection; + } + + if ( ppPos[0] ) + { + SwShellCursor* pCursor = GetCursor_(); + SwCursorSaveState aSaveState( *pCursor ); + SwPosition aOldPos( *pCursor->GetPoint() ); + + pCursor->DeleteMark(); + *pCursor->GetPoint() = *ppPos[0]; + pCursor->GetPtPos() = paPt[0]; + + if ( !pCursor->IsInProtectTable() ) + { + bool bNewSelection = true; + + if ( ppPos[1] ) + { + if ( ppPos[1]->GetNode().StartOfSectionNode() != + aOldPos.GetNode().StartOfSectionNode() ) + { + pCursor->SetMark(); + SwCursorSaveState aSaveState2( *pCursor ); + *pCursor->GetPoint() = *ppPos[1]; + pCursor->GetPtPos() = paPt[1]; + + if ( pCursor->IsInProtectTable( false, false ) ) + { + pCursor->RestoreSavePos(); + bNewSelection = false; + } + } + else + { + pCursor->RestoreSavePos(); + bNewSelection = false; + } + } + + if ( bNewSelection ) + { + // #i35543# SelTableRowCol should remove any existing + // table cursor: + if ( IsTableMode() ) + TableCursorToCursor(); + + if ( pbRow[0] && pbCol[0] ) + bRet = SwCursorShell::SelTable(); + else if ( pbRow[0] ) + bRet = SwCursorShell::SelTableRowOrCol( true, true ); + else if ( pbCol[0] ) + bRet = SwCursorShell::SelTableRowOrCol( false, true ); + } + else + bRet = true; + } + + delete ppPos[0]; + delete ppPos[1]; + } + + return bRet; +} + +SwTab SwFEShell::WhichMouseTabCol( const Point &rPt ) const +{ + SwTab nRet = SwTab::COL_NONE; + bool bRow = false; + bool bCol = false; + bool bSelect = false; + + // First try: Do we get the row/col move cursor? + const SwCellFrame* pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow )); + + if ( !pFrame ) + { + // Second try: Do we get the row/col/tab selection cursor? + pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow, &bCol )); + bSelect = true; + } + + if( pFrame ) + { + while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() ) + pFrame = static_cast<const SwCellFrame*>(static_cast<const SwLayoutFrame*>(pFrame->Lower())->Lower()); + if( pFrame && pFrame->GetTabBox()->GetSttNd() && + pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() ) + pFrame = nullptr; + } + + if( pFrame ) + { + if ( !bSelect ) + { + if ( pFrame->IsVertical() ) + nRet = bRow ? SwTab::COL_VERT : SwTab::ROW_VERT; + else + nRet = bRow ? SwTab::ROW_HORI : SwTab::COL_HORI; + } + else + { + const SwTabFrame* pTabFrame = pFrame->FindTabFrame(); + if ( pTabFrame->IsVertical() ) + { + if ( bRow && bCol ) + { + nRet = SwTab::SEL_VERT; + } + else if ( bRow ) + { + nRet = SwTab::ROWSEL_VERT; + } + else if ( bCol ) + { + nRet = SwTab::COLSEL_VERT; + } + } + else + { + if ( bRow && bCol ) + { + nRet = pTabFrame->IsRightToLeft() ? + SwTab::SEL_HORI_RTL : + SwTab::SEL_HORI; + } + else if ( bRow ) + { + nRet = pTabFrame->IsRightToLeft() ? + SwTab::ROWSEL_HORI_RTL : + SwTab::ROWSEL_HORI; + } + else if ( bCol ) + { + nRet = SwTab::COLSEL_HORI; + } + } + } + } + + return nRet; +} + +// -> #i23726# +SwTextNode * SwFEShell::GetNumRuleNodeAtPos( const Point &rPt) +{ + SwTextNode * pResult = nullptr; + + SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel); + + if( GetContentAtPos(rPt, aContentAtPos) && aContentAtPos.aFnd.pNode) + pResult = aContentAtPos.aFnd.pNode->GetTextNode(); + + return pResult; +} + +bool SwFEShell::IsNumLabel( const Point &rPt, int nMaxOffset ) +{ + bool bResult = false; + + SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel); + + if( GetContentAtPos(rPt, aContentAtPos)) + { + if ((nMaxOffset >= 0 && aContentAtPos.nDist <= nMaxOffset) || + (nMaxOffset < 0)) + bResult = true; + } + + return bResult; +} +// <- #i23726# + +// #i42921# +bool SwFEShell::IsVerticalModeAtNdAndPos( const SwTextNode& _rTextNode, + const Point& _rDocPos ) +{ + bool bRet( false ); + + const SvxFrameDirection nTextDir = + _rTextNode.GetTextDirection( SwPosition(_rTextNode), &_rDocPos ); + switch ( nTextDir ) + { + case SvxFrameDirection::Unknown: + case SvxFrameDirection::Horizontal_RL_TB: + case SvxFrameDirection::Horizontal_LR_TB: + { + bRet = false; + } + break; + case SvxFrameDirection::Vertical_LR_TB: + case SvxFrameDirection::Vertical_RL_TB: + { + bRet = true; + } + break; + default: break; + } + + return bRet; +} + +void SwFEShell::GetMouseTabCols( SwTabCols &rToFill, const Point &rPt ) const +{ + const SwFrame *pBox = GetBox( rPt ); + if ( pBox ) + GetTabCols_( rToFill, pBox ); +} + +void SwFEShell::SetMouseTabCols( const SwTabCols &rNew, bool bCurRowOnly, + const Point &rPt ) +{ + const SwFrame *pBox = GetBox( rPt ); + if( pBox ) + { + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<const SwCellFrame*>(pBox) ); + EndAllActionAndCall(); + } +} + +sal_uInt16 SwFEShell::GetCurMouseColNum( const Point &rPt ) const +{ + return GetCurColNum_( GetBox( rPt ), nullptr ); +} + +size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const +{ + //!!!GetCurTabColNum() mitpflegen!!!! + size_t nRet = 0; + + const SwFrame *pFrame = GetBox( rPt ); + OSL_ENSURE( pFrame, "Table not found" ); + if( pFrame ) + { + const tools::Long nX = pFrame->getFrameArea().Left(); + + // get TabCols, only via these we get the position + SwTabCols aTabCols; + GetMouseTabCols( aTabCols, rPt ); + + const tools::Long nLeft = aTabCols.GetLeftMin(); + + if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + { + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + return nRet; +} + +void ClearFEShellTabCols(SwDoc & rDoc, SwTabFrame const*const pFrame) +{ + auto const pShell(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()); + if (pShell) + { + for (SwViewShell& rCurrentShell : pShell->GetRingContainer()) + { + if (auto const pFE = dynamic_cast<SwFEShell *>(&rCurrentShell)) + { + pFE->ClearColumnRowCache(pFrame); + } + } + } +} + +void SwFEShell::ClearColumnRowCache(SwTabFrame const*const pFrame) +{ + if (m_pColumnCache) + { + if (pFrame == nullptr || pFrame == m_pColumnCache->pLastTabFrame) + { + m_pColumnCache.reset(); + } + } + if (m_pRowCache) + { + if (pFrame == nullptr || pFrame == m_pRowCache->pLastTabFrame) + { + m_pRowCache.reset(); + } + } +} + +void SwFEShell::GetTableAttr( SfxItemSet &rSet ) const +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + rSet.Put( pFrame->ImplFindTabFrame()->GetFormat()->GetAttrSet() ); +} + +void SwFEShell::SetTableAttr( const SfxItemSet &rNew ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + { + CurrShell aCurr( this ); + StartAllAction(); + SwTabFrame *pTab = pFrame->FindTabFrame(); + pTab->GetTable()->SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); + GetDoc()->SetAttr( rNew, *pTab->GetFormat() ); + GetDoc()->getIDocumentState().SetModified(); + EndAllActionAndCall(); + } +} + +// change a cell width/cell height/column width/row height +void SwFEShell::SetColRowWidthHeight( TableChgWidthHeightType eType, sal_uInt16 nDiff ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do { + pFrame = pFrame->GetUpper(); + } while( !pFrame->IsCellFrame() ); + + SwTabFrame *pTab = pFrame->ImplFindTabFrame(); + + // if the table is in relative values (USHRT_MAX) + // then it should be recalculated to absolute values now + const SwFormatFrameSize& rTableFrameSz = pTab->GetFormat()->GetFrameSize(); + SwRectFnSet aRectFnSet(pTab); + tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + TableChgWidthHeightType eTypePos = extractPosition(eType); + if( TableChgMode::VarWidthChangeAbs == pTab->GetTable()->GetTableChgMode() && + ( eTypePos == TableChgWidthHeightType::ColLeft || eTypePos == TableChgWidthHeightType::ColRight ) && + text::HoriOrientation::NONE == pTab->GetFormat()->GetHoriOrient().GetHoriOrient() && + nPrtWidth != rTableFrameSz.GetWidth() ) + { + SwFormatFrameSize aSz( rTableFrameSz ); + aSz.SetWidth( pTab->getFramePrintArea().Width() ); + pTab->GetFormat()->SetFormatAttr( aSz ); + } + + SwTwips nLogDiff = nDiff; + nLogDiff *= pTab->GetFormat()->GetFrameSize().GetWidth(); + nLogDiff /= nPrtWidth; + + /** The cells are destroyed in here */ + GetDoc()->SetColRowWidthHeight( + *const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()), + eType, nDiff, nLogDiff ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + EndAllActionAndCall(); +} + +static bool lcl_IsFormulaSelBoxes( const SwTable& rTable, const SwTableBoxFormula& rFormula, + SwCellFrames& rCells ) +{ + SwTableBoxFormula aTmp( rFormula ); + SwSelBoxes aBoxes; + aTmp.GetBoxesOfFormula(rTable, aBoxes); + for (size_t nSelBoxes = aBoxes.size(); nSelBoxes; ) + { + SwTableBox* pBox = aBoxes[ --nSelBoxes ]; + + if( std::none_of(rCells.begin(), rCells.end(), [&pBox](SwCellFrame* pFrame) { return pFrame->GetTabBox() == pBox; }) ) + return false; + } + + return true; +} + + // ask formula for auto-sum +void SwFEShell::GetAutoSum( OUString& rFormula ) const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = pFrame ? pFrame->ImplFindTabFrame() : nullptr; + if( !pTab ) + return; + + SwCellFrames aCells; + OUString sFields; + if( ::GetAutoSumSel( *this, aCells )) + { + sal_uInt16 nW = 0; + for( size_t n = aCells.size(); n; ) + { + SwCellFrame* pCFrame = aCells[ --n ]; + sal_uInt16 nBoxW = pCFrame->GetTabBox()->IsFormulaOrValueBox(); + if( !nBoxW ) + break; + + if( !nW ) + { + if( USHRT_MAX == nBoxW ) + continue; // skip space at beginning + + // formula only if box is contained + if( RES_BOXATR_FORMULA == nBoxW && + !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells)) + { + nW = RES_BOXATR_VALUE; + // restore previous spaces! + for( size_t i = aCells.size(); n+1 < i; ) + { + sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">" + + sFields; + } + } + else + nW = nBoxW; + } + else if( RES_BOXATR_VALUE == nW ) + { + // search for values, Value/Formula/Text found -> include + if( RES_BOXATR_FORMULA == nBoxW && + ::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells )) + break; + else if( USHRT_MAX != nBoxW ) + sFields = OUStringChar(cListDelim) + sFields; + else + break; + } + else if( RES_BOXATR_FORMULA == nW ) + { + // only continue search when the current formula points to + // all boxes contained in the selection + if( RES_BOXATR_FORMULA == nBoxW ) + { + if( !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells )) + { + // redo only for values! + + nW = RES_BOXATR_VALUE; + sFields.clear(); + // restore previous spaces! + for( size_t i = aCells.size(); n+1 < i; ) + { + sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">" + + sFields; + } + } + else + sFields = OUStringChar(cListDelim) + sFields; + } + else if( USHRT_MAX == nBoxW ) + break; + else + continue; // ignore this box + } + else + // all other stuff terminates the loop + // possibly allow texts?? + break; + + sFields = "<" + pCFrame->GetTabBox()->GetName() + ">" + sFields; + } + } + + rFormula = OUString::createFromAscii( sCalc_Sum ); + if (!sFields.isEmpty()) + { + rFormula += "(" + sFields + ")"; + } +} + +bool SwFEShell::IsTableRightToLeft() const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr; + if (!pTab) + return false; + return pTab->IsRightToLeft(); +} + +bool SwFEShell::IsMouseTableRightToLeft(const Point &rPt) const +{ + SwFrame *pFrame = const_cast<SwFrame *>(GetBox( rPt )); + const SwTabFrame* pTabFrame = pFrame ? pFrame->ImplFindTabFrame() : nullptr; + OSL_ENSURE( pTabFrame, "Table not found" ); + return pTabFrame && pTabFrame->IsRightToLeft(); +} + +bool SwFEShell::IsTableVertical() const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr; + if (!pTab) + return false; + return pTab->IsVertical(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |