diff options
Diffstat (limited to 'sw/source')
138 files changed, 2813 insertions, 1218 deletions
diff --git a/sw/source/core/attr/swatrset.cxx b/sw/source/core/attr/swatrset.cxx index 7cd6303ecef1..55835c5d1c30 100644 --- a/sw/source/core/attr/swatrset.cxx +++ b/sw/source/core/attr/swatrset.cxx @@ -398,7 +398,8 @@ void SwAttrSet::CopyToModify( SwModify& rMod ) const } if (pSrcDoc != pDstDoc && - SfxItemState::SET == GetItemState(RES_PARATR_LIST_AUTOFMT, false, &pItem)) + SfxItemState::SET == GetItemState(RES_PARATR_LIST_AUTOFMT, false, &pItem) + && static_cast<SwFormatAutoFormat const*>(pItem)->GetStyleHandle()) { SfxItemSet const& rAutoStyle(*static_cast<SwFormatAutoFormat const&>(*pItem).GetStyleHandle()); std::shared_ptr<SfxItemSet> const pNewSet( diff --git a/sw/source/core/bastyp/calc.cxx b/sw/source/core/bastyp/calc.cxx index 19dd4e7751f0..dc9fb62973f7 100644 --- a/sw/source/core/bastyp/calc.cxx +++ b/sw/source/core/bastyp/calc.cxx @@ -140,7 +140,7 @@ const sal_Int32 coStartFlags = // Continuing characters may be any alphanumeric, underscore, or dot. const sal_Int32 coContFlags = - ( coStartFlags | i18n::KParseTokens::ASC_DOT ) + (coStartFlags | i18n::KParseTokens::ASC_DOT | i18n::KParseTokens::GROUP_SEPARATOR_IN_NUMBER) & ~i18n::KParseTokens::IGNORE_LEADING_WS; extern "C" { diff --git a/sw/source/core/bastyp/index.cxx b/sw/source/core/bastyp/index.cxx index 4f740948abff..cb7c05825274 100644 --- a/sw/source/core/bastyp/index.cxx +++ b/sw/source/core/bastyp/index.cxx @@ -229,12 +229,11 @@ SwIndexReg::~SwIndexReg() void SwIndexReg::Update( SwIndex const & rIdx, const sal_Int32 nDiff, - const bool bNeg, - const bool /* argument is only used in derived class*/ ) + UpdateMode const eMode) { SwIndex* pStt = const_cast<SwIndex*>(&rIdx); const sal_Int32 nNewVal = rIdx.m_nIndex; - if( bNeg ) + if (eMode & UpdateMode::Negative) { const sal_Int32 nLast = rIdx.GetIndex() + nDiff; while (pStt && pStt->m_nIndex == nNewVal) diff --git a/sw/source/core/crsr/callnk.cxx b/sw/source/core/crsr/callnk.cxx index 4d956e90e2c5..4f93f9b5d232 100644 --- a/sw/source/core/crsr/callnk.cxx +++ b/sw/source/core/crsr/callnk.cxx @@ -30,6 +30,7 @@ #include <ndtxt.hxx> #include <flyfrm.hxx> #include <breakit.hxx> +#include <UndoTable.hxx> SwCallLink::SwCallLink( SwCursorShell & rSh ) : rShell( rSh ) @@ -59,24 +60,36 @@ SwCallLink::SwCallLink( SwCursorShell & rSh ) } } -static void lcl_notifyRow(const SwContentNode* pNode, SwCursorShell & rShell) +namespace sw { + +/** + An empty paragraph inside a table with a nested table preceding it + should be hidden, unless the cursor is positioned in the paragraph. + + If the cursor is now (or was previously) inside such a paragraph, + send a size change notification on the row frame to force reformatting. + */ +void NotifyTableCollapsedParagraph(const SwContentNode *const pNode, SwCursorShell *const pShell) { if ( !pNode ) return; - SwFrame *const pMyFrame = pNode->getLayoutFrame( rShell.GetLayout() ); + SwFrame *const pMyFrame = pNode->getLayoutFrame(pShell ? pShell->GetLayout() : nullptr); if ( !pMyFrame ) return; - // We need to emulated a change of the row height in order - // to have the complete row redrawn + // important: only invalidate layout if something is actually hidden or + // shown! Otherwise performance is going to suffer with "difficult" tables. + if (!pMyFrame->IsCollapse()) + return; + SwRowFrame *const pRow = pMyFrame->FindRowFrame(); if ( !pRow ) return; const SwTableLine* pLine = pRow->GetTabLine( ); - if (rShell.IsTableMode() || (rShell.StartsWithTable() && rShell.ExtendedSelectedAll())) + if (pShell && (pShell->IsTableMode() || (pShell->StartsWith_() != SwCursorShell::StartsWith::None && pShell->ExtendedSelectedAll()))) { // If we have a table selection, then avoid the notification: it's not necessary (the text // cursor needs no updating) and the notification may kill the selection overlay, leading to @@ -85,10 +98,13 @@ static void lcl_notifyRow(const SwContentNode* pNode, SwCursorShell & rShell) return; } + // notify a change in frame size to force reformatting of the row SwFormatFrameSize aSize = pLine->GetFrameFormat()->GetFrameSize(); pRow->ModifyNotification(nullptr, &aSize); } +} // namespace sw + SwCallLink::~SwCallLink() COVERITY_NOEXCEPT_FALSE { if( nNdTyp == SwNodeType::NONE || !rShell.m_bCallChgLnk ) // see ctor @@ -101,15 +117,17 @@ SwCallLink::~SwCallLink() COVERITY_NOEXCEPT_FALSE if( !pCNd ) return; - lcl_notifyRow(pCNd, rShell); - - const SwDoc *pDoc=rShell.GetDoc(); - const SwContentNode *pNode = nullptr; - if ( pDoc && nNode < pDoc->GetNodes( ).Count( ) ) + if (pCNd->GetIndex() != nNode) // only if moved to different node { - pNode = pDoc->GetNodes()[nNode]->GetContentNode(); + ::sw::NotifyTableCollapsedParagraph(pCNd, &rShell); + + const SwDoc *pDoc=rShell.GetDoc(); + if (nNode < pDoc->GetNodes().Count()) + { + const SwContentNode *const pNode = pDoc->GetNodes()[nNode]->GetContentNode(); + ::sw::NotifyTableCollapsedParagraph(pNode, &rShell); + } } - lcl_notifyRow(pNode, rShell); sal_Int32 nCmp, nCurrentContent = pCurrentCursor->GetPoint()->nContent.GetIndex(); SwNodeType nNdWhich = pCNd->GetNodeType(); diff --git a/sw/source/core/crsr/crbm.cxx b/sw/source/core/crsr/crbm.cxx index a9175808de85..b35b1329cbca 100644 --- a/sw/source/core/crsr/crbm.cxx +++ b/sw/source/core/crsr/crbm.cxx @@ -130,9 +130,14 @@ bool IsMarkHidden(SwRootFrame const& rLayout, ::sw::mark::IMark const& rMark) { return false; } - SwTextNode const& rNode(*rMark.GetMarkPos().nNode.GetNode().GetTextNode()); + SwNode const& rNode(rMark.GetMarkPos().nNode.GetNode()); + SwTextNode const*const pTextNode(rNode.GetTextNode()); + if (pTextNode == nullptr) + { // UNO_BOOKMARK may point to table node + return rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden; + } SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>( - rNode.getLayoutFrame(&rLayout))); + pTextNode->getLayoutFrame(&rLayout))); if (!pFrame) { return true; @@ -147,14 +152,14 @@ bool IsMarkHidden(SwRootFrame const& rLayout, ::sw::mark::IMark const& rMark) } else { - if (rMark.GetMarkPos().nContent.GetIndex() == rNode.Len()) + if (rMark.GetMarkPos().nContent.GetIndex() == pTextNode->Len()) { // at end of node: never deleted (except if node deleted) - return rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden; + return pTextNode->GetRedlineMergeFlag() == SwNode::Merge::Hidden; } else { // check character following mark pos return pFrame->MapModelToViewPos(rMark.GetMarkPos()) - == pFrame->MapModelToView(&rNode, rMark.GetMarkPos().nContent.GetIndex() + 1); + == pFrame->MapModelToView(pTextNode, rMark.GetMarkPos().nContent.GetIndex() + 1); } } } diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index 7e0defb67ca0..85e9eb837017 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -120,6 +120,10 @@ SwPaM * SwCursorShell::CreateCursor() // don't create new Cursor with active table Selection assert(!IsTableMode()); + // ensure that m_pCurrentCursor is valid; if it's invalid it would be + // copied to pNew and then pNew would be deleted in UpdateCursor() below + ClearUpCursors(); + // New cursor as copy of current one. Add to the ring. // Links point to previously created one, ie forward. SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor ); @@ -228,7 +232,7 @@ void SwCursorShell::StartAction() void SwCursorShell::EndAction( const bool bIdleEnd, const bool DoSetPosX ) { - comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll()); bool bVis = m_bSVCursorVis; sal_uInt16 eFlags = SwCursorShell::CHKRANGE; @@ -581,42 +585,331 @@ bool SwCursorShell::SttEndDoc( bool bStt ) return bRet; } +const SwTableNode* SwCursorShell::IsCursorInTable() const +{ + if (m_pTableCursor) + { // find the table that has the selected boxes + return m_pTableCursor->GetSelectedBoxes()[0]->GetSttNd()->FindTableNode(); + } + return m_pCurrentCursor->GetNode().FindTableNode(); +} + +// fun cases to consider: +// * outermost table +// - into para => SA/ESA +// - into prev/next table => continue... +// - no prev/next => done +// * inner table +// - into containing cell => SA/ESA +// - into prev/next of containing cell +// + into para +// + into table nested in prev/next cell +// - out of table -> as above +// => iterate in one direction until a node is reached that is a parent or a sibling of a parent of the current table +// - parent reached => SA/ESA depending +// - not in parent but in *prev/next* sibling of outer cell => TrySelectOuterTable +// - not in parent but in *prev/next* sibling of outer table => TrySelectOuterTable +// => select-all cannot select a sequence of table with no para at same level; only 1 table +// - no parent, no prev/next => TrySelectOuterTable + +bool SwCursorShell::MoveOutOfTable() +{ + SwPosition const point(*getShellCursor(false)->GetPoint()); + SwPosition const mark(*getShellCursor(false)->GetMark()); + + for (auto const fnMove : {&fnMoveBackward, &fnMoveForward}) + { + Push(); + SwCursor *const pCursor(getShellCursor(false)); + + pCursor->Normalize(fnMove == &fnMoveBackward); + pCursor->DeleteMark(); + SwTableNode const*const pTable(pCursor->GetPoint()->nNode.GetNode().FindTableNode()); + assert(pTable); + while (MovePara(GoInContent, *fnMove)) + { + SwStartNode const*const pBox(pCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode()); + if (!pBox) + { + Pop(SwCursorShell::PopMode::DeleteStack); + return true; // moved to paragraph at top-level of text + } + if (pBox->GetIndex() < pTable->GetIndex() + && pTable->EndOfSectionIndex() < pBox->EndOfSectionIndex()) + { + Pop(SwCursorShell::PopMode::DeleteStack); + return true; // pBox contains start position (pTable) + } + } + + Pop(SwCursorShell::PopMode::DeleteCurrent); + // FIXME: Pop doesn't restore original cursor if nested tables + *getShellCursor(false)->GetPoint() = point; + getShellCursor(false)->SetMark(); + *getShellCursor(false)->GetMark() = mark; + } + return false; +} + +bool SwCursorShell::TrySelectOuterTable() +{ + assert(m_pTableCursor); + SwTableNode const& rInnerTable(*m_pTableCursor->GetPoint()->nNode.GetNode().FindTableNode()); + SwNodes const& rNodes(rInnerTable.GetNodes()); + SwTableNode const*const pOuterTable(rInnerTable.GetNodes()[rInnerTable.GetIndex()-1]->FindTableNode()); + if (!pOuterTable) + { + return false; + } + + // manually select boxes of pOuterTable + SwNodeIndex firstCell(*pOuterTable, +1); + SwNodeIndex lastCell(*rNodes[pOuterTable->EndOfSectionIndex()-1]->StartOfSectionNode()); + SwSelBoxes aNew; + pOuterTable->GetTable().CreateSelection(&firstCell.GetNode(), &lastCell.GetNode(), + aNew, SwTable::SEARCH_NONE, false); + // set table cursor to 1st / last content which may be in inner table + SwContentNode *const pStart = rNodes.GoNext(&firstCell); + assert(pStart); // must at least find the previous point node + lastCell = *lastCell.GetNode().EndOfSectionNode(); + SwContentNode *const pEnd = SwNodes::GoPrevious(&lastCell); + assert(pEnd); // must at least find the previous point node + delete m_pTableCursor; + m_pTableCursor = new SwShellTableCursor(*this, SwPosition(*pStart, 0), Point(), + SwPosition(*pEnd, 0), Point()); + m_pTableCursor->ActualizeSelection( aNew ); + m_pTableCursor->IsCursorMovedUpdate(); // clear this so GetCursor() doesn't recreate our SwSelBoxes + + // this will update m_pCurrentCursor based on m_pTableCursor + UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); + + return true; +} + +/// find XText start node +static SwStartNode const* FindTextStart(SwPosition const& rPos) +{ + SwStartNode const* pStartNode(rPos.nNode.GetNode().StartOfSectionNode()); + while (pStartNode && (pStartNode->IsSectionNode() || pStartNode->IsTableNode())) + { + pStartNode = pStartNode->StartOfSectionNode(); + } + return pStartNode; +} + +static SwStartNode const* FindParentText(SwShellCursor const& rCursor) +{ + // find closest section containing both start and end - ignore Sections + SwStartNode const* pStartNode(FindTextStart(*rCursor.Start())); + SwEndNode const* pEndNode(FindTextStart(*rCursor.End())->EndOfSectionNode()); + while (pStartNode->EndOfSectionNode()->GetIndex() < pEndNode->GetIndex()) + { + pStartNode = pStartNode->StartOfSectionNode(); + } + while (pStartNode->GetIndex() < pEndNode->StartOfSectionNode()->GetIndex()) + { + pEndNode = pEndNode->StartOfSectionNode()->StartOfSectionNode()->EndOfSectionNode(); + } + assert(pStartNode->EndOfSectionNode() == pEndNode); + + return (pStartNode->IsSectionNode() || pStartNode->IsTableNode()) + ? FindTextStart(SwPosition(*pStartNode)) + : pStartNode; +} + +bool SwCursorShell::MoveStartText() +{ + SwPosition const old(*m_pCurrentCursor->GetPoint()); + SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false))); + assert(pStartNode); + SwTableNode const*const pTable(pStartNode->FindTableNode()); + *m_pCurrentCursor->GetPoint() = SwPosition(*pStartNode); + GetDoc()->GetNodes().GoNext(&m_pCurrentCursor->GetPoint()->nNode); + m_pCurrentCursor->GetPoint()->nContent.Assign(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetContentNode(), 0); + while (m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableNode() != pTable + && (!pTable || pTable->GetIndex() < m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableNode()->GetIndex()) + && MoveOutOfTable()); + UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); + return old != *m_pCurrentCursor->GetPoint(); +} + +// select all inside the current XText, with table or hidden para at start/end void SwCursorShell::ExtendedSelectAll(bool bFootnotes) { + // find common ancestor node of both ends of cursor + SwStartNode const*const pStartNode(FindParentText(*getShellCursor(false))); + assert(pStartNode); + if (IsTableMode()) + { // convert m_pTableCursor to m_pCurrentCursor after determining pStartNode + TableCursorToCursor(); + } SwNodes& rNodes = GetDoc()->GetNodes(); + m_pCurrentCursor->Normalize(true); SwPosition* pPos = m_pCurrentCursor->GetPoint(); - pPos->nNode = bFootnotes ? rNodes.GetEndOfPostIts() : rNodes.GetEndOfAutotext(); + pPos->nNode = bFootnotes ? rNodes.GetEndOfPostIts() : static_cast<SwNode const&>(*pStartNode); pPos->nContent.Assign( rNodes.GoNext( &pPos->nNode ), 0 ); pPos = m_pCurrentCursor->GetMark(); - pPos->nNode = rNodes.GetEndOfContent(); + pPos->nNode = bFootnotes ? rNodes.GetEndOfContent() : static_cast<SwNode const&>(*pStartNode->EndOfSectionNode()); SwContentNode* pCNd = SwNodes::GoPrevious( &pPos->nNode ); pPos->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 ); } -bool SwCursorShell::ExtendedSelectedAll() +static typename SwCursorShell::StartsWith StartsWith(SwStartNode const& rStart) +{ + for (auto i = rStart.GetIndex() + 1; i < rStart.EndOfSectionIndex(); ++i) + { + SwNode const& rNode(*rStart.GetNodes()[i]); + switch (rNode.GetNodeType()) + { + case SwNodeType::Section: + if (rNode.GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; + continue; + case SwNodeType::Table: + return SwCursorShell::StartsWith::Table; + case SwNodeType::Text: + if (rNode.GetTextNode()->IsHidden()) + { + return SwCursorShell::StartsWith::HiddenPara; + } + return SwCursorShell::StartsWith::None; + default: + return SwCursorShell::StartsWith::None; + } + } + return SwCursorShell::StartsWith::None; +} + +static typename SwCursorShell::StartsWith EndsWith(SwStartNode const& rStart) +{ + for (auto i = rStart.EndOfSectionIndex() - 1; rStart.GetIndex() < i; --i) + { + SwNode const& rNode(*rStart.GetNodes()[i]); + switch (rNode.GetNodeType()) + { + case SwNodeType::End: + if (auto pStartNode = rNode.StartOfSectionNode(); pStartNode->IsTableNode()) + { + return SwCursorShell::StartsWith::Table; + } + else if (pStartNode->IsSectionNode()) + { + if (pStartNode->GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; + } + //TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode()); + break; + case SwNodeType::Text: + if (rNode.GetTextNode()->IsHidden()) + { + return SwCursorShell::StartsWith::HiddenPara; + } + return SwCursorShell::StartsWith::None; + default: + return SwCursorShell::StartsWith::None; + } + } + return SwCursorShell::StartsWith::None; +} + +// return the node that is the start of the extended selection (to include table +// or section start nodes; looks like extending for end nodes is not required) +::std::optional<::std::pair<SwNode const*, ::std::vector<SwTableNode*>>> +SwCursorShell::ExtendedSelectedAll() const { + if (m_pTableCursor) + { + return {}; + } + SwNodes& rNodes = GetDoc()->GetNodes(); - SwNodeIndex nNode = rNodes.GetEndOfAutotext(); + SwShellCursor const*const pShellCursor = getShellCursor(false); + SwStartNode const* pStartNode(FindParentText(*pShellCursor)); + + SwNodeIndex nNode(*pStartNode); SwContentNode* pStart = rNodes.GoNext(&nNode); + if (!pStart) + { + return {}; + } - nNode = rNodes.GetEndOfContent(); + nNode = *pStartNode->EndOfSectionNode(); SwContentNode* pEnd = SwNodes::GoPrevious(&nNode); - - if (!pStart || !pEnd) - return false; + if (!pEnd) + { + return {}; + } SwPosition aStart(*pStart, 0); SwPosition aEnd(*pEnd, pEnd->Len()); - SwShellCursor* pShellCursor = getShellCursor(false); - return aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End(); + if (!(aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End())) + { + return {}; + } + + auto const ends(::EndsWith(*pStartNode)); + if (::StartsWith(*pStartNode) == StartsWith::None + && ends == StartsWith::None) + { + return {}; // "ordinary" selection will work + } + + ::std::vector<SwTableNode*> tablesAtEnd; + if (ends == StartsWith::Table) + { + SwNode * pLastNode(rNodes[pStartNode->EndOfSectionIndex() - 1]); + while (pLastNode->IsEndNode()) + { + SwNode *const pNode(pLastNode->StartOfSectionNode()); + if (pNode->IsTableNode()) + { + tablesAtEnd.push_back(pNode->GetTableNode()); + pLastNode = rNodes[pNode->GetIndex() - 1]; + } + else if (pNode->IsSectionNode()) + { + pLastNode = rNodes[pLastNode->GetIndex() - 1]; + } + } + assert(!tablesAtEnd.empty()); + } + + // tdf#133990 ensure directly containing section is included in SwUndoDelete + while (pStartNode->IsSectionNode() + && pStartNode->GetIndex() == pStartNode->StartOfSectionNode()->GetIndex() + 1 + && pStartNode->EndOfSectionNode()->GetIndex() + 1 == pStartNode->StartOfSectionNode()->EndOfSectionNode()->GetIndex()) + { + pStartNode = pStartNode->StartOfSectionNode(); + } + + // pStartNode is the node that fully contains the selection - the first + // node of the selection is the first node inside pStartNode + return ::std::make_pair(rNodes[pStartNode->GetIndex() + 1], tablesAtEnd); } -bool SwCursorShell::StartsWithTable() +typename SwCursorShell::StartsWith SwCursorShell::StartsWith_() { - SwNodes& rNodes = GetDoc()->GetNodes(); - SwNodeIndex nNode(rNodes.GetEndOfExtras()); - SwContentNode* pContentNode = rNodes.GoNext(&nNode); - return pContentNode->FindTableNode(); + SwShellCursor const*const pShellCursor = getShellCursor(false); + // first, check if this is invalid; ExtendedSelectAll(true) may result in + // a) an ordinary selection that is valid + // b) a selection that is extended + // c) a selection that is invalid and will cause FindParentText to loop + SwNode const& rEndOfExtras(GetDoc()->GetNodes().GetEndOfExtras()); + if (pShellCursor->Start()->nNode.GetIndex() <= rEndOfExtras.GetIndex() + && rEndOfExtras.GetIndex() < pShellCursor->End()->nNode.GetIndex()) + { + return StartsWith::None; // *very* extended, no ExtendedSelectedAll handling! + } + SwStartNode const*const pStartNode(FindParentText(*pShellCursor)); + if (auto const ret = ::StartsWith(*pStartNode); ret != StartsWith::None) + { + return ret; + } + if (auto const ret = ::EndsWith(*pStartNode); ret != StartsWith::None) + { + return ret; + } + return StartsWith::None; } bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage ) @@ -645,14 +938,14 @@ bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage ) return bRet; } -bool SwCursorShell::isInHiddenTextFrame(SwShellCursor* pShellCursor) +bool SwCursorShell::isInHiddenFrame(SwShellCursor* pShellCursor) { SwContentNode *pCNode = pShellCursor->GetContentNode(); std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false); SwContentFrame *const pFrame = pCNode ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp) : nullptr; - return !pFrame || (pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow()); + return !pFrame || pFrame->IsHiddenNow(); } // sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara @@ -693,7 +986,7 @@ bool SwCursorShell::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & //which is what SwCursorShell::UpdateCursorPos will reset //the position to if we pass it a position in an //invisible hidden paragraph field - while (isInHiddenTextFrame(pTmpCursor) + while (isInHiddenFrame(pTmpCursor) || !IsAtStartOrEndOfFrame(this, pTmpCursor, fnPosPara)) { if (!pTmpCursor->MovePara(fnWhichPara, fnPosPara)) @@ -1395,7 +1688,7 @@ bool SwCursorShell::GoNextPrevCursorSetSearchLabel(const bool bNext) void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect) { - comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll()); SET_CURR_SHELL( this ); // always switch off all cursors when painting @@ -1469,8 +1762,8 @@ void SwCursorShell::VisPortChgd( const SwRect & rRect ) /** Set the cursor back into content. - This should only be called if the cursor was move somewhere else (e.g. when - deleting a border). The new position is calculated from its current position + This should only be called if the cursor was moved (e.g. when deleting a + text frame). The new position is calculated from its current position in the layout. */ void SwCursorShell::UpdateCursorPos() @@ -1480,13 +1773,29 @@ void SwCursorShell::UpdateCursorPos() SwShellCursor* pShellCursor = getShellCursor( true ); Size aOldSz( GetDocSize() ); - if( isInHiddenTextFrame(pShellCursor) ) + if (isInHiddenFrame(pShellCursor) && !ExtendedSelectedAll()) { - SwCursorMoveState aTmpState( MV_NONE ); + SwCursorMoveState aTmpState(MV_SETONLYTEXT); aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); GetLayout()->GetCursorOfst( pShellCursor->GetPoint(), pShellCursor->GetPtPos(), &aTmpState ); pShellCursor->DeleteMark(); + // kde45196-1.html: try to get to a non-hidden paragraph, there must + // be one in the document body + while (isInHiddenFrame(pShellCursor)) + { + if (!pShellCursor->MovePara(GoNextPara, fnParaStart)) + { + break; + } + } + while (isInHiddenFrame(pShellCursor)) + { + if (!pShellCursor->MovePara(GoPrevPara, fnParaStart)) + { + break; + } + } } IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : nullptr; if( pGrammarContact ) @@ -2243,7 +2552,14 @@ void SwCursorShell::Push() */ bool SwCursorShell::Pop(PopMode const eDelete) { - SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed + ::std::unique_ptr<SwCallLink> pLink(::std::make_unique<SwCallLink>(*this)); // watch Cursor-Moves; call Link if needed + return Pop(eDelete, ::std::move(pLink)); +} + +bool SwCursorShell::Pop(PopMode const eDelete, + [[maybe_unused]] ::std::unique_ptr<SwCallLink> const pLink) +{ + assert(pLink); // parameter exists only to be deleted before return // are there any left? if (nullptr == m_pStackCursor) @@ -2264,7 +2580,7 @@ bool SwCursorShell::Pop(PopMode const eDelete) if (PopMode::DeleteCurrent == eDelete) { - SwCursorSaveState aSaveState( *m_pCurrentCursor ); + ::std::optional<SwCursorSaveState> oSaveState( *m_pCurrentCursor ); // If the visible SSelection was not changed const Point& rPoint = pOldStack->GetPtPos(); @@ -2292,6 +2608,7 @@ bool SwCursorShell::Pop(PopMode const eDelete) !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos ) ) { + oSaveState.reset(); // prevent UAF UpdateCursor(); // update current cursor if (m_pTableCursor) { // tdf#106929 ensure m_pCurrentCursor ring is recreated from table @@ -2373,6 +2690,8 @@ void SwCursorShell::ShowCursor() { if( !m_bBasicHideCursor ) { + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll()); + m_bSVCursorVis = true; m_pCurrentCursor->SetShowTextInputFieldOverlay( true ); @@ -2415,6 +2734,8 @@ void SwCursorShell::ShellLoseFocus() void SwCursorShell::ShellGetFocus() { + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWith_() != StartsWith::None && ExtendedSelectedAll()); + m_bHasFocus = true; if( !m_bBasicHideCursor && VisArea().Width() ) { @@ -2694,7 +3015,7 @@ bool SwCursorShell::IsOverReadOnlyPos( const Point& rPt ) const SwPaM aPam( *m_pCurrentCursor->GetPoint() ); GetLayout()->GetCursorOfst( aPam.GetPoint(), aPt ); // form view - return aPam.HasReadonlySel( GetViewOptions()->IsFormView() ); + return aPam.HasReadonlySel(GetViewOptions()->IsFormView(), false); } /** Get the number of elements in the ring of cursors @@ -3091,7 +3412,7 @@ bool SwCursorShell::FindValidContentNode( bool bOnlyText ) GetDoc()->GetDocShell()->IsReadOnlyUI() ) return true; - if( m_pCurrentCursor->HasMark() ) + if( m_pCurrentCursor->HasMark() && !mbSelectAll ) ClearMark(); // first check for frames @@ -3313,7 +3634,7 @@ void SwCursorShell::SetReadOnlyAvailable( bool bFlag ) } } -bool SwCursorShell::HasReadonlySel() const +bool SwCursorShell::HasReadonlySel(bool const isReplace) const { bool bRet = false; // If protected area is to be ignored, then selections are never read-only. @@ -3323,14 +3644,15 @@ bool SwCursorShell::HasReadonlySel() const { if ( m_pTableCursor != nullptr ) { + // TODO: handling when a table cell (cells) is selected bRet = m_pTableCursor->HasReadOnlyBoxSel() - || m_pTableCursor->HasReadonlySel( GetViewOptions()->IsFormView() ); + || m_pTableCursor->HasReadonlySel(GetViewOptions()->IsFormView(), isReplace); } else { for(const SwPaM& rCursor : m_pCurrentCursor->GetRingContainer()) { - if( rCursor.HasReadonlySel( GetViewOptions()->IsFormView() ) ) + if (rCursor.HasReadonlySel(GetViewOptions()->IsFormView(), isReplace)) { bRet = true; break; @@ -3341,6 +3663,31 @@ bool SwCursorShell::HasReadonlySel() const return bRet; } +bool SwCursorShell::HasHiddenSections() const +{ + bool bRet = false; + + if ( m_pTableCursor != nullptr ) + { + // TODO: handling when a table cell (cells) is selected + bRet = m_pTableCursor->HasHiddenBoxSel() + || m_pTableCursor->HasHiddenSections(); + } + else + { + for(const SwPaM& rCursor : m_pCurrentCursor->GetRingContainer()) + { + if (rCursor.HasHiddenSections()) + { + bRet = true; + break; + } + } + } + + return bRet; +} + bool SwCursorShell::IsSelFullPara() const { bool bRet = false; diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index 9153b8b34e85..11fb5db02df6 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -1930,7 +1930,7 @@ bool SwContentAtPos::IsInRTLText()const return bRet; } -bool SwCursorShell::SelectText( const sal_Int32 nStart, +bool SwCursorShell::SelectTextModel( const sal_Int32 nStart, const sal_Int32 nEnd ) { SET_CURR_SHELL( this ); @@ -1954,6 +1954,43 @@ bool SwCursorShell::SelectText( const sal_Int32 nStart, return bRet; } +TextFrameIndex SwCursorShell::GetCursorPointAsViewIndex() const +{ + SwPosition const*const pPos(GetCursor()->GetPoint()); + SwTextNode const*const pTextNode(pPos->nNode.GetNode().GetTextNode()); + assert(pTextNode); + SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTextNode->getLayoutFrame(GetLayout()))); + assert(pFrame); + return pFrame->MapModelToViewPos(*pPos); +} + +bool SwCursorShell::SelectTextView(TextFrameIndex const nStart, + TextFrameIndex const nEnd) +{ + CurrShell aCurr( this ); + bool bRet = false; + + SwCallLink aLk( *this ); + SwCursorSaveState aSaveState( *m_pCurrentCursor ); + + SwPosition& rPos = *m_pCurrentCursor->GetPoint(); + m_pCurrentCursor->DeleteMark(); + // indexes must correspond to cursor point! + SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()->getLayoutFrame(GetLayout()))); + assert(pFrame); + rPos = pFrame->MapViewToModelPos(nStart); + m_pCurrentCursor->SetMark(); + rPos = pFrame->MapViewToModelPos(nEnd); + + if (!m_pCurrentCursor->IsSelOvr()) + { + UpdateCursor(); + bRet = true; + } + + return bRet; +} + bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich, bool bExpand, const SwTextAttr* pTextAttr ) @@ -1977,7 +2014,7 @@ bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich, if( pTextAttr ) { const sal_Int32* pEnd = pTextAttr->End(); - bRet = SelectText( pTextAttr->GetStart(), ( pEnd ? *pEnd : pTextAttr->GetStart() + 1 ) ); + bRet = SelectTextModel(pTextAttr->GetStart(), (pEnd ? *pEnd : pTextAttr->GetStart() + 1)); } } return bRet; diff --git a/sw/source/core/crsr/pam.cxx b/sw/source/core/crsr/pam.cxx index b47b35b4bc30..8a7b3fb65507 100644 --- a/sw/source/core/crsr/pam.cxx +++ b/sw/source/core/crsr/pam.cxx @@ -573,7 +573,7 @@ static const SwFrame* lcl_FindEditInReadonlyFrame( const SwFrame& rFrame ) } /// is in protected section or selection surrounds something protected -bool SwPaM::HasReadonlySel( bool bFormView ) const +bool SwPaM::HasReadonlySel(bool bFormView, bool const isReplace) const { bool bRet = false; @@ -748,7 +748,7 @@ bool SwPaM::HasReadonlySel( bool bFormView ) const if (!bRet && pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)) { - if (pDoc->getIDocumentMarkAccess()->isBookmarkDeleted(*this)) + if (pDoc->getIDocumentMarkAccess()->isBookmarkDeleted(*this, isReplace)) { return true; } @@ -790,6 +790,49 @@ bool SwPaM::HasReadonlySel( bool bFormView ) const return bRet; } +bool SwPaM::HasHiddenSections() const +{ + bool bRet = false; + + if (HasMark() && GetPoint()->nNode != GetMark()->nNode) + { + sal_uLong nSttIdx = GetMark()->nNode.GetIndex(), + nEndIdx = GetPoint()->nNode.GetIndex(); + if (nEndIdx <= nSttIdx) + { + sal_uLong nTmp = nSttIdx; + nSttIdx = nEndIdx; + nEndIdx = nTmp; + } + + // If a hidden section should be between nodes, then the + // selection needs to contain already x nodes. + // (TextNd, SectNd, TextNd, EndNd, TextNd ) + if (nSttIdx + 3 < nEndIdx) + { + const SwSectionFormats& rFormats = GetDoc()->GetSections(); + for (SwSectionFormats::size_type n = rFormats.size(); n;) + { + const SwSectionFormat* pFormat = rFormats[--n]; + if (pFormat->GetSection()->IsHidden()) + { + const SwFormatContent& rContent = pFormat->GetContent(false); + OSL_ENSURE(rContent.GetContentIdx(), "where is the SectionNode?"); + sal_uLong nIdx = rContent.GetContentIdx()->GetIndex(); + if (nSttIdx <= nIdx && nEndIdx >= nIdx + && rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes()) + { + bRet = true; + break; + } + } + } + } + } + + return bRet; +} + /// This function returns the next node in direction of search. If there is no /// left or the next is out of the area, then a null-pointer is returned. /// @param rbFirst If <true> then first time request. If so than the position of @@ -814,7 +857,7 @@ SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & ( nullptr == pFrame || ( !bInReadOnly && pFrame->IsProtected() ) || - (pFrame->IsTextFrame() && static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()) + pFrame->IsHiddenNow() ) || ( !bInReadOnly && pNd->FindSectionNode() && pNd->FindSectionNode()->GetSection().IsProtect() @@ -854,8 +897,7 @@ SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout)); if (nullptr == pFrame || ( !bInReadOnly && pFrame->IsProtected() ) || - ( pFrame->IsTextFrame() && - static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow())) + pFrame->IsHiddenNow()) { pNd = nullptr; continue; diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx index 8cfa09e8c9ac..f8b0213a67cc 100644 --- a/sw/source/core/crsr/swcrsr.cxx +++ b/sw/source/core/crsr/swcrsr.cxx @@ -214,7 +214,7 @@ namespace } } -bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags ) +bool SwCursor::IsSelOvr(SwCursorSelOverFlags const eFlags) { SwDoc* pDoc = GetDoc(); SwNodes& rNds = pDoc->GetNodes(); @@ -336,7 +336,7 @@ bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags ) // skip to the next/prev valid paragraph with a layout SwNodeIndex& rPtIdx = GetPoint()->nNode; bool bGoNxt = m_vSavePos.back().nNode < rPtIdx.GetIndex(); - while( nullptr != ( pFrame = ( bGoNxt ? pFrame->GetNextContentFrame() : pFrame->GetPrevContentFrame() )) + while( nullptr != ( pFrame = ( bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt() )) && 0 == pFrame->getFrameArea().Height() ) ; @@ -348,8 +348,7 @@ bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags ) pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ); while ( pFrame && 0 == pFrame->getFrameArea().Height() ) { - pFrame = bGoNxt ? pFrame->GetNextContentFrame() - : pFrame->GetPrevContentFrame(); + pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt(); } } @@ -395,9 +394,16 @@ bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags ) if( !pFrame ) { - DeleteMark(); - RestoreSavePos(); - return true; // we need a frame + assert(!m_vSavePos.empty()); + SwContentNode const*const pSaveNode(rNds[m_vSavePos.back().nNode]->GetContentNode()); + // if the old position already didn't have a frame, allow moving + // anyway, hope the caller can handle that + if (pSaveNode && pSaveNode->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout())) + { + DeleteMark(); + RestoreSavePos(); + return true; // we need a frame + } } } @@ -882,7 +888,7 @@ static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd, rPam.SetMark(); rPam.GetPoint()->nNode = rEndNd; - pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode ); + pCNd = SwNodes::GoPrevious(&rPam.GetPoint()->nNode, true); if( !pCNd ) return false; pCNd->MakeEndIndex( &rPam.GetPoint()->nContent ); @@ -902,7 +908,7 @@ static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd, if( !bFirst ) { rPam.GetPoint()->nNode = rSttNd; - pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode ); + pCNd = SwNodes::GoPrevious(&rPam.GetPoint()->nNode, true); if( !pCNd ) return false; pCNd->MakeEndIndex( &rPam.GetPoint()->nContent ); @@ -2526,4 +2532,18 @@ bool SwTableCursor::HasReadOnlyBoxSel() const return bRet; } +bool SwTableCursor::HasHiddenBoxSel() const +{ + bool bRet = false; + for (size_t n = m_SelectedBoxes.size(); n; ) + { + if (m_SelectedBoxes[--n]->GetFrameFormat()->IsHidden()) + { + bRet = true; + break; + } + } + return bRet; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index a2605673eefb..8318cc5d5ba8 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -26,6 +26,7 @@ #include <IDocumentSettingAccess.hxx> #include <UndoManager.hxx> #include <docary.hxx> +#include <pamtyp.hxx> #include <textboxhelper.hxx> #include <dcontact.hxx> #include <grfatr.hxx> @@ -286,6 +287,12 @@ namespace sw ::sw::mark::InsertMode::CopyText); // Explicitly try to get exactly the same name as in the source // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name + if (pNewMark == nullptr) + { + assert(IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK + || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK); + continue; // can't insert duplicate cross reference mark + } pDestDoc->getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName()); // copying additional attributes for bookmarks or fieldmarks @@ -387,7 +394,8 @@ namespace *pDelPam->GetPoint(), nDelCount ); } - if (pDelPam->GetNext() && *pDelPam->GetNext()->End() == *pDelPam->Start()) + if (pDelPam->GetNext() != pDelPam.get() + && *pDelPam->GetNext()->End() == *pDelPam->Start()) { *pDelPam->GetNext()->End() = *pDelPam->End(); pDelPam.reset(pDelPam->GetNext()); @@ -617,8 +625,9 @@ namespace sw namespace { - bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam, - bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false) + bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, + SwPaM & rPam, SwDeleteFlags const flags, + bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, SwDeleteFlags, bool), const bool bForceJoinNext = false) { std::vector<std::pair<sal_uLong, sal_Int32>> Breaks; @@ -626,7 +635,7 @@ namespace if (Breaks.empty()) { - return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext); + return (rDocumentContentOperations.*pFunc)(rPam, flags, bForceJoinNext); } // Deletion must be split into several parts if the text node @@ -650,7 +659,7 @@ namespace rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1); if (rStart < rEnd) // check if part is empty { - bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext); + bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags, bForceJoinNext); nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes... } rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second); @@ -660,7 +669,7 @@ namespace rStart = *rPam.Start(); // set to original start if (rStart < rEnd) // check if part is empty { - bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext); + bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags, bForceJoinNext); } return bRet; @@ -935,8 +944,10 @@ namespace for(SaveRedline & rSvRedLine : rArr) { rSvRedLine.SetPos( nInsPos ); - pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ); - if (rSvRedLine.pRedl->GetType() == RedlineType::Delete) + IDocumentRedlineAccess::AppendResult const result( + pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true )); + if ( IDocumentRedlineAccess::AppendResult::APPENDED == result && + rSvRedLine.pRedl->GetType() == RedlineType::Delete ) { UpdateFramesForAddDeleteRedline(*pDoc, *rSvRedLine.pRedl); } @@ -1962,6 +1973,18 @@ DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos, cons return bRet; } +static auto GetCorrPosition(SwPaM const& rPam) -> SwPosition +{ + // tdf#152710 target position must be on node that survives deletion + // so that PaMCorrAbs can invalidate SwUnoCursors properly + return rPam.GetPoint()->nNode.GetNode().IsContentNode() + ? *rPam.GetPoint() + : rPam.GetMark()->nNode.GetNode().IsContentNode() + ? *rPam.GetMark() + // this would be the result in SwNodes::RemoveNode() + : SwPosition(SwNodeIndex(rPam.End()->nNode.GetNode(), +1)); +} + /// Delete a full Section of the NodeArray. /// The passed Node is located somewhere in the designated Section. void DocumentContentOperationsManager::DeleteSection( SwNode *pNode ) @@ -1979,8 +2002,9 @@ void DocumentContentOperationsManager::DeleteSection( SwNode *pNode ) { // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area - SwNodeIndex aMvStt( aSttIdx, 1 ); - SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true ); + SwPaM const range(aSttIdx, aEndIdx); + SwPosition const pos(GetCorrPosition(range)); + ::PaMCorrAbs(range, pos); } m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 ); @@ -1994,7 +2018,7 @@ void DocumentContentOperationsManager::DeleteDummyChar( assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy); (void) cDummy; - DeleteRangeImpl(aPam); + DeleteRangeImpl(aPam, SwDeleteFlags::Default); if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) @@ -2005,12 +2029,7 @@ void DocumentContentOperationsManager::DeleteDummyChar( void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam ) { - lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl ); - - if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) - { - rPam.Normalize(false); // tdf#127635 put point at the end of deletion - } + lcl_DoWithBreaks(*this, rPam, SwDeleteFlags::Default, &DocumentContentOperationsManager::DeleteRangeImpl); if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) @@ -2113,7 +2132,7 @@ bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam ) ::PaMCorrAbs( aDelPam, aTmpPos ); } - std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete( aDelPam, true )); + std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aDelPam, SwDeleteFlags::Default, true)); *rPam.GetPoint() = *aDelPam.GetPoint(); pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc ); @@ -2208,22 +2227,17 @@ bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam ) } // #i100466# Add handling of new optional parameter <bForceJoinNext> -bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM & rPam, +bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM & rPam, SwDeleteFlags const flags, const bool bForceJoinNext ) { if ( lcl_StrLenOverflow( rPam ) ) return false; - bool const ret = lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) + bool const ret = lcl_DoWithBreaks( *this, rPam, flags, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl : &DocumentContentOperationsManager::DeleteAndJoinImpl, bForceJoinNext ); - if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) - { - rPam.Normalize(false); // tdf#127635 put point at the end of deletion - } - return ret; } @@ -3352,8 +3366,8 @@ bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString if (rStart < rEnd) // check if part is empty { bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) - ? DeleteAndJoinWithRedlineImpl(aPam) - : DeleteAndJoinImpl(aPam, false); + ? DeleteAndJoinWithRedlineImpl(aPam, SwDeleteFlags::Default) + : DeleteAndJoinImpl(aPam, SwDeleteFlags::Default, false); nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes... } rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second); @@ -3488,21 +3502,28 @@ void DocumentContentOperationsManager::CopyWithFlyInFly( aRedlRest.Restore(); if (bMakeNewFrames) // tdf#130685 only after aRedlRest { // recreate from previous node (could be merged now) - if (SwTextNode *const pNode = aSavePos.GetNode().GetTextNode()) + std::unordered_set<SwTextFrame*> frames; + SwTextNode * pNode = aSavePos.GetNode().GetTextNode(); + SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode(); + if (pEndNode) { - std::unordered_set<SwTextFrame*> frames; - SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode(); - if (pEndNode) + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) { - SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode); - for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + if (pFrame->getRootFrame()->IsHideRedlines()) { - if (pFrame->getRootFrame()->IsHideRedlines()) + frames.insert(pFrame); + // tdf#135061 check if end node is merged to a preceding node + if (pNode == nullptr && pFrame->GetMergedPara() + && pFrame->GetMergedPara()->pFirstNode->GetIndex() < aSavePos.GetIndex()) { - frames.insert(pFrame); + pNode = pFrame->GetMergedPara()->pFirstNode; } } } + } + if (pNode != nullptr) + { sw::RecreateStartTextFrames(*pNode); if (!frames.empty()) { // tdf#132187 check if the end node needs new frames @@ -3914,7 +3935,7 @@ DocumentContentOperationsManager::~DocumentContentOperationsManager() } //Private methods -bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool ) +bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM & rPam, SwDeleteFlags const flags, const bool) { assert(m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()); @@ -3994,7 +4015,7 @@ bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPa { assert(pRedline->HasValidRange()); undos.emplace_back(std::make_unique<SwUndoRedlineDelete>( - *pRedline, SwUndoId::DELETE)); + *pRedline, SwUndoId::DELETE, flags)); } const SwRewriter aRewriter = undos.front()->GetRewriter(); // can only group a single undo action @@ -4055,7 +4076,7 @@ bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPa return true; } -bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam, +bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM & rPam, SwDeleteFlags const flags, const bool bForceJoinNext ) { bool bJoinText, bJoinPrev; @@ -4067,7 +4088,7 @@ bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam, } { - bool const bSuccess( DeleteRangeImpl( rPam ) ); + bool const bSuccess( DeleteRangeImpl(rPam, flags) ); if (!bSuccess) return false; } @@ -4086,14 +4107,17 @@ bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam, return true; } -bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, const bool) +bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, SwDeleteFlags const flags, const bool) { // Move all cursors out of the deleted range, but first copy the // passed PaM, because it could be a cursor that would be moved! SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); - ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() ); + { + SwPosition const pos(GetCorrPosition(aDelPam)); + ::PaMCorrAbs(aDelPam, pos); + } - bool const bSuccess( DeleteRangeImplImpl( aDelPam ) ); + bool const bSuccess( DeleteRangeImplImpl(aDelPam, flags) ); if (bSuccess) { // now copy position from temp copy to given PaM *rPam.GetPoint() = *aDelPam.GetPoint(); @@ -4102,7 +4126,7 @@ bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, const bool) return bSuccess; } -bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam) +bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam, SwDeleteFlags const flags) { SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); @@ -4167,7 +4191,7 @@ bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam) } if (!bMerged) { - m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelete>( rPam ) ); + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(rPam, flags)); } m_rDoc.getIDocumentState().SetModified(); @@ -4179,14 +4203,18 @@ bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam) m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any ); // Delete and move all "Flys at the paragraph", which are within the Selection - DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode, - &rPam.GetMark()->nContent, &rPam.GetPoint()->nContent); + if (!(flags & SwDeleteFlags::ArtificialSelection)) + { + DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode, + &rPam.GetMark()->nContent, &rPam.GetPoint()->nContent); + } DelBookmarks( pStt->nNode, pEnd->nNode, nullptr, &pStt->nContent, - &pEnd->nContent); + &pEnd->nContent, + bool(flags & SwDeleteFlags::ArtificialSelection)); SwNodeIndex aSttIdx( pStt->nNode ); SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode(); @@ -4302,7 +4330,7 @@ bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam) bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr, const bool bRegExReplace ) { - if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() ) + if (!rPam.HasMark()) return false; bool bJoinText, bJoinPrev; @@ -4417,12 +4445,26 @@ bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUSt InsertItemSet( aTmpRange, aSet ); } + // tdf#139982: Appending the redline may immediately delete flys + // anchored in the previous text if it's inside an insert redline. + // Also flys will be deleted if the redline is accepted. Move them + // to the position between the previous text and the new text, + // there the chance of surviving both accept and reject is best. + SaveFlyArr flys; + SaveFlyInRange(aDelPam, *aDelPam.End(), flys, false); + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE )); } + // add redline similar to DeleteAndJoinWithRedlineImpl() + std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*aDelPam.GetMark())); + pCursor->SetMark(); + *pCursor->GetPoint() = *aDelPam.GetPoint(); m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true); + RestFlyInRange(flys, *aDelPam.End(), &aDelPam.End()->nNode, true); + sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor); *rPam.GetMark() = *aDelPam.GetMark(); if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) @@ -4445,8 +4487,8 @@ bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUSt m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld ); *rPam.GetPoint() = pBkmk->GetMarkPos(); - if(pBkmk->IsExpanded()) - *rPam.GetMark() = pBkmk->GetOtherMarkPos(); + *rPam.GetMark() = pBkmk->IsExpanded() ? pBkmk->GetOtherMarkPos() : pBkmk->GetMarkPos(); + m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk); } bJoinText = false; @@ -4743,26 +4785,25 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // Move the PaM one node back from the insert position, so that // the position doesn't get moved pCopyPam->SetMark(); - bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); - // If the position was shifted from more than one node, an end node has been skipped - bool bAfterTable = false; - if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > 1) + bool bCanMoveBack = false; + // First check if it will be able to move *to* first copied node. + // Note this doesn't just check IsStartNode() because SwDoc::AppendDoc() + // intentionally sets it to the body start node, perhaps it should just + // call SplitNode instead? + if (!pStt->nNode.GetNode().IsSectionNode() && !pStt->nNode.GetNode().IsTableNode()) { - // First go back to the original place - pCopyPam->GetPoint()->nNode = rPos.nNode; - pCopyPam->GetPoint()->nContent = rPos.nContent; - - bCanMoveBack = false; - bAfterTable = true; + bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); } if( !bCanMoveBack ) { pCopyPam->GetPoint()->nNode--; + pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0); assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0); } SwNodeRange aRg( pStt->nNode, pEnd->nNode ); SwNodeIndex aInsPos( rPos.nNode ); + ::std::optional<SwIndex> oInsContentIndex; const bool bOneNode = pStt->nNode == pEnd->nNode; SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode(); SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode(); @@ -4912,8 +4953,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // We have to set the correct PaM for Undo, if this PaM starts in a textnode, // the undo operation will try to merge this node after removing the table. // If we didn't split a textnode, the PaM should start at the inserted table node - if( rPos.nContent.GetIndex() == pDestTextNd->Len() ) - { // Insertion at the last position of a textnode (empty or not) + if (pDestTextNd->Len() && rPos.nContent.GetIndex() == pDestTextNd->Len()) + { // Insertion at the last position of a textnode ++aInsPos; // The table will be inserted behind the text node } else if( rPos.nContent.GetIndex() ) @@ -4947,27 +4988,18 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo aRg.aEnd--; } } - else if( bCanMoveBack ) - { // Insertion at the first position of a text node. It will not be split, the table - // will be inserted before the text node. - // See below, before the SetInsertRange function of the undo object will be called, - // the CpyPam would be moved to the next content position. This has to be avoided - // We want to be moved to the table node itself thus we have to set bCanMoveBack - // and to manipulate pCopyPam. - bCanMoveBack = false; - pCopyPam->GetPoint()->nNode--; - } + assert(!bCanMoveBack); } pDestTextNd = aInsPos.GetNode().GetTextNode(); if (pEndTextNd) { - SwIndex aDestIdx( rPos.nContent ); + oInsContentIndex.emplace(rPos.nContent); if( !pDestTextNd ) { pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); - aDestIdx.Assign( pDestTextNd, 0 ); + oInsContentIndex->Assign(pDestTextNd, 0); aInsPos--; // if we have to insert an extra text node @@ -4985,7 +5017,7 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo PUSH_NUMRULE_STATE } - pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ), + pEndTextNd->CopyText(pDestTextNd, *oInsContentIndex, SwIndex(pEndTextNd), pEnd->nContent.GetIndex() ); // Also copy all format templates @@ -5039,21 +5071,30 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo bCopyBookmarks = false; } + // init *again* - because CopyWithFlyInFly moved startPos + SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1), + SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode())); // at-char anchors post SplitNode are on index 0 of 2nd node and will // remain there - move them back to the start (end would also work?) // ... also for at-para anchors; here start is preferable because // it's consistent with SplitNode from SwUndoInserts::RedoImpl() - if (pFlysAtInsPos) + if (pFlysAtInsPos + && (bCanMoveBack + || startPos.nNode.GetNode().IsTextNode() + || (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode() + && startPos.nNode.GetNode().IsSectionNode()))) // not into table { - // init *again* - because CopyWithFlyInFly moved startPos - SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1), - SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode())); if (bCanMoveBack) { // pCopyPam is actually 1 before the copy range so move it fwd SwPaM temp(*pCopyPam->GetPoint()); temp.Move(fnMoveForward, GoInContent); startPos = *temp.GetPoint(); } + else if (startPos.nNode.GetNode().IsSectionNode()) + { // probably on top-level start node, so no CheckNodesRange here; + GoNextNds(&startPos.nNode, false); // SwFEShell::Paste() deletes node + startPos.nContent.Assign(startPos.nNode.GetNode().GetContentNode(), 0); + } assert(startPos.nNode.GetNode().IsContentNode()); SwPosition startPosAtPara(startPos); startPosAtPara.nContent.Assign(nullptr, 0); @@ -5114,27 +5155,32 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo } else // incremented in (!pSttTextNd && pDestTextNd) above { - pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0); + // assign also content index in this case, see testSectionAnchorCopyTableAtStart + assert(oInsContentIndex); + assert(oInsContentIndex->GetIdxReg() == aInsPos.GetNode().GetContentNode()); + pCopyPam->GetMark()->nContent = (*oInsContentIndex); } rPos = *pCopyPam->GetMark(); } else *pCopyPam->GetMark() = rPos; - if ( !bAfterTable ) - pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode ); + if (bCanMoveBack) + { + pCopyPam->Move(fnMoveForward, GoInContent); + } else { pCopyPam->GetPoint()->nNode++; // Reset the offset to 0 as it was before the insertion pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0); - // If the next node is a start node, then step back: the start node - // has been copied and needs to be in the selection for the undo + // If the next node is a start node, then step back: SetInsertRange() + // will add 1 in this case, but that is too much... if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode()) pCopyPam->GetPoint()->nNode--; - } + oInsContentIndex.reset(); pCopyPam->Exchange(); // Also copy all bookmarks diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index 8ae32f266bdb..718492ab3f79 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -283,6 +283,12 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) break; } + // no nodes can be unmerged by this - skip MakeFrames() etc. + if (rPam.GetPoint()->nNode == rPam.GetMark()->nNode) + { + break; // continue with AppendAllObjs() + } + // first, call CheckParaRedlineMerge on the first paragraph, // to init flag on new merge range (if any) + 1st node post the merge auto eMode(sw::FrameMode::Existing); @@ -2319,7 +2325,7 @@ bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange ) SwRedlineTable::size_type n = 0; const SwPosition* pStt = rRange.Start(); const SwPosition* pEnd = rRange.End(); - GetRedline( *pStt, &n ); + //FIXME overlapping problem GetRedline( *pStt, &n ); for ( ; n < mpRedlineTable->size(); ++n) { SwRangeRedline * pRedline = (*mpRedlineTable)[ n ]; diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index 7918e5db8dbe..19d70d112e8b 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -963,6 +963,7 @@ namespace sw { namespace mark static bool isDeleteMark( ::sw::mark::MarkBase const*const pMark, + bool const isReplace, SwNodeIndex const& rStt, SwNodeIndex const& rEnd, SwIndex const*const pSttIdx, @@ -986,6 +987,8 @@ namespace sw { namespace mark && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, pEndIdx); // special case: completely in range, touching the end? if ( pEndIdx != nullptr + && !(isReplace && IDocumentMarkAccess::GetType(*pMark) + == IDocumentMarkAccess::MarkType::BOOKMARK) && ( ( rbIsOtherPosInRange && pMark->GetMarkPos().nNode == rEnd && pMark->GetMarkPos().nContent == *pEndIdx ) @@ -1031,7 +1034,7 @@ namespace sw { namespace mark return false; } - bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM) const + bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM, bool const isReplace) const { SwPosition const& rStart(*rPaM.Start()); SwPosition const& rEnd(*rPaM.End()); @@ -1046,7 +1049,7 @@ namespace sw { namespace mark bool bIsPosInRange(false); bool bIsOtherPosInRange(false); - bool const bDeleteMark = isDeleteMark(pMark, + bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStart.nNode, rEnd.nNode, &rStart.nContent, &rEnd.nContent, bIsPosInRange, bIsOtherPosInRange); if (bDeleteMark @@ -1063,7 +1066,8 @@ namespace sw { namespace mark const SwNodeIndex& rEnd, std::vector<SaveBookmark>* pSaveBkmk, const SwIndex* pSttIdx, - const SwIndex* pEndIdx ) + const SwIndex* pEndIdx, + bool const isReplace) { std::vector<const_iterator_t> vMarksToDelete; bool bIsSortingNeeded = false; @@ -1086,7 +1090,7 @@ namespace sw { namespace mark bool bIsPosInRange(false); bool bIsOtherPosInRange(false); - bool const bDeleteMark = isDeleteMark(pMark, rStt, rEnd, pSttIdx, pEndIdx, bIsPosInRange, bIsOtherPosInRange); + bool const bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd, pSttIdx, pEndIdx, bIsPosInRange, bIsOtherPosInRange); if ( bIsPosInRange && ( bIsOtherPosInRange @@ -1803,7 +1807,8 @@ void DelBookmarks( const SwNodeIndex& rEnd, std::vector<SaveBookmark> * pSaveBkmk, const SwIndex* pSttIdx, - const SwIndex* pEndIdx) + const SwIndex* pEndIdx, + bool const isReplace) { // illegal range ?? if(rStt.GetIndex() > rEnd.GetIndex() @@ -1811,7 +1816,7 @@ void DelBookmarks( return; SwDoc* const pDoc = rStt.GetNode().GetDoc(); - pDoc->getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk, pSttIdx, pEndIdx); + pDoc->getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk, pSttIdx, pEndIdx, isReplace); // Copy all Redlines which are in the move area into an array // which holds all position information as offset. diff --git a/sw/source/core/doc/doccorr.cxx b/sw/source/core/doc/doccorr.cxx index 8e2769b353e5..a2564119ce06 100644 --- a/sw/source/core/doc/doccorr.cxx +++ b/sw/source/core/doc/doccorr.cxx @@ -33,11 +33,14 @@ namespace /// returns NULL if no restrictions apply const SwStartNode* lcl_FindUnoCursorSection( const SwNode& rNode ) { - const SwStartNode* pStartNode = rNode.StartOfSectionNode(); + const SwStartNode* pStartNode = rNode.IsStartNode() ? rNode.GetStartNode() : rNode.StartOfSectionNode(); while( ( pStartNode != nullptr ) && ( pStartNode->StartOfSectionNode() != pStartNode ) && - ( pStartNode->GetStartNodeType() == SwNormalStartNode ) ) + // section node is only start node allowing overlapped delete + pStartNode->IsSectionNode() ) + { pStartNode = pStartNode->StartOfSectionNode(); + } return pStartNode; } diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx index 1b93a7a56a78..6587cb0e06a2 100644 --- a/sw/source/core/doc/docedt.cxx +++ b/sw/source/core/doc/docedt.cxx @@ -28,6 +28,7 @@ #include <mdiexp.hxx> #include <mvsave.hxx> #include <redline.hxx> +#include <rolbck.hxx> #include <rootfrm.hxx> #include <splargs.hxx> #include <swcrsr.hxx> @@ -48,7 +49,7 @@ using namespace ::com::sun::star::i18n; void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos, - const SwNodeIndex* pInsertPos ) + const SwNodeIndex* pInsertPos, bool const isForceToStartPos) { SwPosition aPos(rStartPos); for(const SaveFly & rSave : rArr) @@ -57,7 +58,7 @@ void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos, SwFrameFormat* pFormat = rSave.pFrameFormat; SwFormatAnchor aAnchor( pFormat->GetAnchor() ); - if (rSave.isAtInsertNode) + if (rSave.isAtInsertNode || isForceToStartPos) { if( pInsertPos != nullptr ) { @@ -130,7 +131,7 @@ void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr ) } void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos, - SaveFlyArr& rArr, bool bMoveAllFlys ) + SaveFlyArr& rArr, bool bMoveAllFlys, SwHistory *const pHistory) { SwFrameFormats& rFormats = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrameFormats(); SwFrameFormat* pFormat; @@ -176,6 +177,10 @@ void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos, || (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId() && (bInsPos = (rInsPos == *pAPos)))) { + if (pHistory) + { + pHistory->AddChangeFlyAnchor(*pFormat); + } SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(), (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ? (pAPos->nNode == rSttNdIdx) @@ -557,7 +562,7 @@ uno::Any SwDoc::Spell( SwPaM& rPaM, { nCurrNd = pNd->EndOfSectionIndex(); } - else if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + else if( !pContentFrame->IsHiddenNow() ) { if( pPageCnt && *pPageCnt && pPageSt ) { @@ -778,7 +783,7 @@ static bool lcl_HyphenateNode( const SwNodePtr& rpNd, void* pArgs ) // sw_redlinehide: this will be called once per node for merged nodes; // the fully deleted ones won't have frames so are skipped. SwContentFrame* pContentFrame = pNode->getLayoutFrame( pNode->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ); - if( pContentFrame && !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + if( pContentFrame && !pContentFrame->IsHiddenNow() ) { sal_uInt16 *pPageSt = pHyphArgs->GetPageSt(); sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt(); diff --git a/sw/source/core/doc/doclay.cxx b/sw/source/core/doc/doclay.cxx index ec4861fe39b2..5b3dc0ef3687 100644 --- a/sw/source/core/doc/doclay.cxx +++ b/sw/source/core/doc/doclay.cxx @@ -157,13 +157,12 @@ SwFlyFrameFormat* SwDoc::MakeFlySection_( const SwPosition& rAnchPos, pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ); OUString sName; - if( !mbInReading ) - switch( rNode.GetNodeType() ) - { + switch( rNode.GetNodeType() ) + { case SwNodeType::Grf: sName = GetUniqueGrfName(); break; case SwNodeType::Ole: sName = GetUniqueOLEName(); break; default: sName = GetUniqueFrameName(); break; - } + } SwFlyFrameFormat* pFormat = MakeFlyFrameFormat( sName, pFrameFormat ); // Create content and connect to the format. @@ -1408,6 +1407,10 @@ const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType void SwDoc::SetFlyName( SwFlyFrameFormat& rFormat, const OUString& rName ) { + if (rFormat.GetName() == rName) + { + return; + } OUString sName( rName ); if( sName.isEmpty() || FindFlyByName( sName ) ) { diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx index 984f2335bc45..6b041fa96c51 100644 --- a/sw/source/core/doc/docnew.cxx +++ b/sw/source/core/doc/docnew.cxx @@ -1054,19 +1054,19 @@ SwNodeIndex SwDoc::AppendDoc(const SwDoc& rSource, sal_uInt16 const nStartPageNu { SwNodeIndex aBreakIdx( GetNodes().GetEndOfContent(), -1 ); SwPosition aBreakPos( aBreakIdx ); - // InsertPageBreak just works on SwTextNode nodes, so make - // sure the last node is one! - bool bIsTextNode = aBreakIdx.GetNode().IsTextNode(); - if ( !bIsTextNode ) - getIDocumentContentOperations().AppendTextNode( aBreakPos ); - const OUString name = pTargetPageDesc->GetName(); - pTargetShell->InsertPageBreak( &name, nStartPageNumber ); - if ( !bIsTextNode ) - { - pTargetShell->SttEndDoc( false ); - --aBreakIdx; - GetNodes().Delete( aBreakIdx ); - } + // insert new node - will be removed at the end... + // (don't SplitNode() as it may move flys to the wrong node) + getIDocumentContentOperations().AppendTextNode(aBreakPos); + SwFormatPageDesc pageDesc(pTargetPageDesc); + pageDesc.SetNumOffset(nStartPageNumber); + // set break on the last paragraph + getIDocumentContentOperations().InsertPoolItem(SwPaM(aBreakPos), + pageDesc, SetAttrMode::DEFAULT, pTargetShell->GetLayout()); + // tdf#148309 move to the last node - so that the "flush page break" + // code below will format the frame of the node with the page break, + // which is required for new page frames to be created! Else layout + // performance will be terrible. + pTargetShell->SttEndDoc(false); // There is now a new empty text node on the new page. If it has // any marks, those are from the previous page: move them back @@ -1097,6 +1097,7 @@ SwNodeIndex SwDoc::AppendDoc(const SwDoc& rSource, sal_uInt16 const nStartPageNu if ( !bDeletePrevious ) { SAL_INFO( "sw.pageframe", "(Flush pagebreak AKA EndAllAction" ); + assert(pTargetShell->GetCursor()->GetPoint()->nNode.GetNode().GetTextNode()->GetSwAttrSet().HasItem(RES_PAGEDESC)); pTargetShell->EndAllAction(); SAL_INFO( "sw.pageframe", "Flush changes AKA EndAllAction)" ); pTargetShell->StartAllAction(); diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx index ea57aa58319d..40d1bb607c1e 100644 --- a/sw/source/core/doc/textboxhelper.cxx +++ b/sw/source/core/doc/textboxhelper.cxx @@ -90,7 +90,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape) xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH)); uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY); - xNamed->setName(pShape->GetDoc()->GetUniqueFrameName()); + assert(!xNamed->getName().isEmpty()); + (void)xNamed; // Link its text range to the original shape. uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW); diff --git a/sw/source/core/docnode/ndsect.cxx b/sw/source/core/docnode/ndsect.cxx index 8c2efee0eb7e..ce0f6cf8d4eb 100644 --- a/sw/source/core/docnode/ndsect.cxx +++ b/sw/source/core/docnode/ndsect.cxx @@ -535,7 +535,7 @@ void SwDoc::DelSectionFormat( SwSectionFormat *pFormat, bool bDelNodes ) { SwNodeIndex aUpdIdx( *pIdx ); SwPaM aPaM( *pSectNd->EndOfSectionNode(), *pSectNd ); - GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelete>( aPaM )); + GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(aPaM, SwDeleteFlags::Default)); if( pFootnoteEndAtTextEnd ) GetFootnoteIdxs().UpdateFootnote( aUpdIdx ); getIDocumentState().SetModified(); @@ -1013,9 +1013,9 @@ SwSectionNode::~SwSectionNode() } } -SwFrame *SwSectionNode::MakeFrame( SwFrame *pSib ) +SwFrame* SwSectionNode::MakeFrame(SwFrame* pSib, bool bHidden) { - m_pSection->m_Data.SetHiddenFlag(false); + m_pSection->m_Data.SetHiddenFlag(bHidden); return new SwSectionFrame( *m_pSection, pSib ); } diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index 15a49729ce51..690b3ade304a 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -880,6 +880,35 @@ const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts, return &rNdTable; } +static void lcl_RemoveBreaksTable(SwTableNode & rNode, SwTableFormat *const pTableFormat) +{ + // delete old layout frames, new ones need to be created... + rNode.DelFrames(nullptr); + + // remove PageBreaks/PageDesc/ColBreak + SwFrameFormat & rFormat(*rNode.GetTable().GetFrameFormat()); + + const SfxPoolItem* pItem; + if (SfxItemState::SET == rFormat.GetItemState(RES_BREAK, false, &pItem)) + { + if (pTableFormat) + { + pTableFormat->SetFormatAttr(*pItem); + } + rFormat.ResetFormatAttr(RES_BREAK); + } + + if (SfxItemState::SET == rFormat.GetItemState(RES_PAGEDESC, false, &pItem) + && static_cast<SwFormatPageDesc const*>(pItem)->GetPageDesc()) + { + if (pTableFormat) + { + pTableFormat->SetFormatAttr(*pItem); + } + rFormat.ResetFormatAttr(RES_PAGEDESC); + } +} + static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat) { // delete old layout frames, new ones need to be created... @@ -1386,10 +1415,19 @@ SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes, // delete frames of all contained content nodes for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines ) { - SwNode& rNode = aNodeIndex.GetNode(); - if( rNode.IsContentNode() ) + SwNode* pNode(&aNodeIndex.GetNode()); + while (pNode->IsSectionNode()) // could be ToX field in table { - lcl_RemoveBreaks(static_cast<SwContentNode&>(rNode), + pNode = pNode->GetNodes()[pNode->GetIndex()+1]; + } + if (pNode->IsTableNode()) + { + lcl_RemoveBreaksTable(static_cast<SwTableNode&>(*pNode), + (0 == nLines) ? pTableFormat : nullptr); + } + else if (pNode->IsContentNode()) + { + lcl_RemoveBreaks(static_cast<SwContentNode&>(*pNode), (0 == nLines) ? pTableFormat : nullptr); } } @@ -1929,169 +1967,210 @@ void SwDoc::DeleteCol( const SwCursor& rCursor ) // Thus delete the Columns GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr); - DeleteRowCol( aBoxes, true ); + DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr); } -bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) +void SwDoc::DelTable(SwTableNode *const pTableNd) { - if( ::HasProtectedCells( rBoxes )) - return false; - - OSL_ENSURE( !rBoxes.empty(), "No valid Box list" ); - SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); - if( !pTableNd ) - return false; - - if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr) - return false; - - ::ClearFEShellTabCols(*this, nullptr); - SwSelBoxes aSelBoxes( rBoxes ); - SwTable &rTable = pTableNd->GetTable(); - long nMin = 0; - long nMax = 0; - if( rTable.IsNewModel() ) { - if( bColumn ) - rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax ); - else - rTable.FindSuperfluousRows( aSelBoxes ); + // tdf#156267 remove DdeBookmarks before deleting nodes + SwDataChanged aTmp(SwPaM(*pTableNd, *pTableNd->EndOfSectionNode())); } - // Are we deleting the whole Table? - const sal_uLong nTmpIdx1 = pTableNd->GetIndex(); - const sal_uLong nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1; - if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() && - aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 && - nTmpIdx2 == pTableNd->EndOfSectionIndex() ) + bool bNewTextNd = false; + // Is it alone in a FlyFrame? + SwNodeIndex aIdx( *pTableNd, -1 ); + const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode(); + if( pSttNd ) { - bool bNewTextNd = false; - // Is it alone in a FlyFrame? - SwNodeIndex aIdx( *pTableNd, -1 ); - const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode(); - if( pSttNd ) + const sal_uLong nTableEnd = pTableNd->EndOfSectionIndex() + 1; + const sal_uLong nSectEnd = pSttNd->EndOfSectionIndex(); + if( nTableEnd == nSectEnd ) { - const sal_uLong nTableEnd = pTableNd->EndOfSectionIndex() + 1; - const sal_uLong nSectEnd = pSttNd->EndOfSectionIndex(); - if( nTableEnd == nSectEnd ) + if( SwFlyStartNode == pSttNd->GetStartNodeType() ) { - if( SwFlyStartNode == pSttNd->GetStartNodeType() ) + SwFrameFormat* pFormat = pSttNd->GetFlyFormat(); + if( pFormat ) { - SwFrameFormat* pFormat = pSttNd->GetFlyFormat(); - if( pFormat ) - { - // That's the FlyFormat we're looking for - getIDocumentLayoutAccess().DelLayoutFormat( pFormat ); - return true; - } + // That's the FlyFormat we're looking for + getIDocumentLayoutAccess().DelLayoutFormat( pFormat ); + return; } - // No Fly? Thus Header or Footer: always leave a TextNode - // We can forget about Undo then! - bNewTextNd = true; } + // No Fly? Thus Header or Footer: always leave a TextNode + // We can forget about Undo then! + bNewTextNd = true; } + } - // No Fly? Then it is a Header or Footer, so keep always a TextNode - ++aIdx; - if (GetIDocumentUndoRedo().DoesUndo()) + // No Fly? Then it is a Header or Footer, so keep always a TextNode + ++aIdx; + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().ClearRedo(); + SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() ); + + if( bNewTextNd ) { - GetIDocumentUndoRedo().ClearRedo(); - SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() ); + const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 ); + GetNodes().MakeTextNode( aTmpIdx, + getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + } - if( bNewTextNd ) + // Save the cursors (UNO and otherwise) + SwPaM const* pSavePaM(nullptr); + SwPaM forwardPaM{SwNodeIndex(*pTableNd->EndOfSectionNode())}; + if (forwardPaM.Move(fnMoveForward, GoInNode)) + { + pSavePaM = &forwardPaM; + } + SwPaM backwardPaM{SwNodeIndex(*pTableNd)}; + if (backwardPaM.Move(fnMoveBackward, GoInNode)) + { + if (pSavePaM == nullptr + // try to stay in the same outer table cell + || (forwardPaM.GetPoint()->nNode.GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode() + && forwardPaM.GetPoint()->nNode.GetNode().StartOfSectionIndex() + < backwardPaM.GetPoint()->nNode.GetNode().StartOfSectionIndex())) { - const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 ); - GetNodes().MakeTextNode( aTmpIdx, - getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + pSavePaM = &backwardPaM; } + } + assert(pSavePaM); // due to bNewTextNd this must succeed + { + SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode()); + ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint()); + } - // Save the cursors (UNO and otherwise) - SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) ); - if( ! aSavePaM.Move( fnMoveForward, GoInNode ) ) - { - *aSavePaM.GetMark() = SwPosition( *pTableNd ); - aSavePaM.Move( fnMoveBackward, GoInNode ); - } + // Move hard PageBreaks to the succeeding Node + bool bSavePageBreak = false, bSavePageDesc = false; + sal_uLong nNextNd = pTableNd->EndOfSectionIndex()+1; + SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode(); + if( pNextNd ) + { + SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, + false, &pItem ) ) { - SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode()); - ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark()); + pNextNd->SetAttr( *pItem ); + bSavePageDesc = true; } - // Move hard PageBreaks to the succeeding Node - bool bSavePageBreak = false, bSavePageDesc = false; - sal_uLong nNextNd = pTableNd->EndOfSectionIndex()+1; - SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode(); - if( pNextNd ) + if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, + false, &pItem ) ) { - SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); - const SfxPoolItem *pItem; - if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, - false, &pItem ) ) - { - pNextNd->SetAttr( *pItem ); - bSavePageDesc = true; - } - - if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, - false, &pItem ) ) - { - pNextNd->SetAttr( *pItem ); - bSavePageBreak = true; - } + pNextNd->SetAttr( *pItem ); + bSavePageBreak = true; } - std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete( aPaM )); - if( bNewTextNd ) - pUndo->SetTableDelLastNd(); - pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc ); - pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName()); - GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); } - else + std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default)); + if( bNewTextNd ) + pUndo->SetTableDelLastNd(); + pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc ); + pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName()); + GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); + } + else + { + if( bNewTextNd ) { - if( bNewTextNd ) - { - const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 ); - GetNodes().MakeTextNode( aTmpIdx, - getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); - } - - // Save the cursors (UNO and otherwise) - SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) ); - if( ! aSavePaM.Move( fnMoveForward, GoInNode ) ) - { - *aSavePaM.GetMark() = SwPosition( *pTableNd ); - aSavePaM.Move( fnMoveBackward, GoInNode ); - } - { - SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode()); - ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark()); - } + const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 ); + GetNodes().MakeTextNode( aTmpIdx, + getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + } - // Move hard PageBreaks to the succeeding Node - SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode(); - if( pNextNd ) + // Save the cursors (UNO and otherwise) + SwPaM const* pSavePaM(nullptr); + SwPaM forwardPaM{SwNodeIndex(*pTableNd->EndOfSectionNode())}; + if (forwardPaM.Move(fnMoveForward, GoInNode)) + { + pSavePaM = &forwardPaM; + } + SwPaM backwardPaM{SwNodeIndex(*pTableNd)}; + if (backwardPaM.Move(fnMoveBackward, GoInNode)) + { + if (pSavePaM == nullptr + // try to stay in the same outer table cell + || (forwardPaM.GetPoint()->nNode.GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode() + && forwardPaM.GetPoint()->nNode.GetNode().StartOfSectionIndex() + < backwardPaM.GetPoint()->nNode.GetNode().StartOfSectionIndex())) { - SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); - const SfxPoolItem *pItem; - if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, - false, &pItem ) ) - pNextNd->SetAttr( *pItem ); - - if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, - false, &pItem ) ) - pNextNd->SetAttr( *pItem ); + pSavePaM = &backwardPaM; } + } + assert(pSavePaM); // due to bNewTextNd this must succeed + { + SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode()); + ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint()); + } - pTableNd->DelFrames(); - getIDocumentContentOperations().DeleteSection( pTableNd ); + // Move hard PageBreaks to the succeeding Node + SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode(); + if( pNextNd ) + { + SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); } - GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + pTableNd->DelFrames(); + getIDocumentContentOperations().DeleteSection( pTableNd ); + } - getIDocumentState().SetModified(); - getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 ); + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 ); +} +bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode) +{ + if (!(eMode & SwDoc::RowColMode::DeleteProtected) + && ::HasProtectedCells(rBoxes)) + { + return false; + } + + OSL_ENSURE( !rBoxes.empty(), "No valid Box list" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + if (!(eMode & SwDoc::RowColMode::DeleteProtected) + && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr) + { + return false; + } + + ::ClearFEShellTabCols(*this, nullptr); + SwSelBoxes aSelBoxes( rBoxes ); + SwTable &rTable = pTableNd->GetTable(); + long nMin = 0; + long nMax = 0; + if( rTable.IsNewModel() ) + { + if (eMode & SwDoc::RowColMode::DeleteColumn) + rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax ); + else + rTable.FindSuperfluousRows( aSelBoxes ); + } + + // Are we deleting the whole Table? + const sal_uLong nTmpIdx1 = pTableNd->GetIndex(); + const sal_uLong nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1; + if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() && + aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 && + nTmpIdx2 == pTableNd->EndOfSectionIndex() ) + { + DelTable(pTableNd); return true; } @@ -2112,7 +2191,7 @@ bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) if (rTable.IsNewModel()) { - if (bColumn) + if (eMode & SwDoc::RowColMode::DeleteColumn) rTable.PrepareDeleteCol( nMin, nMax ); rTable.FindSuperfluousRows( aSelBoxes ); if (pUndo) diff --git a/sw/source/core/docnode/node.cxx b/sw/source/core/docnode/node.cxx index 955e113f2768..0bf2729cd02c 100644 --- a/sw/source/core/docnode/node.cxx +++ b/sw/source/core/docnode/node.cxx @@ -1384,6 +1384,12 @@ void SwContentNode::DelFrames(SwRootFrame const*const pLayout) pMerged->pParaPropsNode = pNode->GetTextNode(); break; } + else if (pMerged->pFirstNode->GetIndex() == i) + { // this can only happen when called from CheckParaRedlineMerge() + // and the pMerged will be deleted anyway + pMerged->pParaPropsNode = pMerged->pFirstNode; + break; + } } assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode)); } diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 2a2bef4f4488..8e8587994c88 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -43,6 +43,7 @@ #include <fmtftn.hxx> #include <docsh.hxx> +#include <rootfrm.hxx> typedef std::vector<SwStartNode*> SwStartNodePointers; @@ -1294,17 +1295,49 @@ SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx) const return static_cast<SwContentNode*>(pNd); } -SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx) +sal_uLong SwNodes::StartOfGlobalSection(const SwNode& node) const +{ + const sal_uLong pos = node.GetIndex(); + if (GetEndOfExtras().GetIndex() < pos) + // Regular ContentSection + return GetEndOfExtras().GetIndex() + sal_uLong(1); + if (GetEndOfAutotext().GetIndex() < pos) + // Redlines + return GetEndOfAutotext().GetIndex() + sal_uLong(1); + if (GetEndOfInserts().GetIndex() < pos) + { + // Flys/Headers/Footers + if (auto* p = node.FindFlyStartNode()) + return p->GetIndex(); + if (auto* p = node.FindHeaderStartNode()) + return p->GetIndex(); + if (auto* p = node.FindFooterStartNode()) + return p->GetIndex(); + return GetEndOfInserts().GetIndex() + sal_uLong(1); + } + if (GetEndOfPostIts().GetIndex() < pos) + { + // Footnotes + if (auto* p = node.FindFootnoteStartNode()) + return p->GetIndex(); + return GetEndOfPostIts().GetIndex() + sal_uLong(1); + } + return sal_uLong(0); +} + +SwContentNode* SwNodes::GoPrevious(SwNodeIndex* pIdx, bool canCrossBoundary) { if( !pIdx->GetIndex() ) return nullptr; SwNodeIndex aTmp( *pIdx, -1 ); + sal_uLong aGlobalStart( + canCrossBoundary ? sal_uLong(0) : aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); SwNode* pNd = nullptr; - while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() ) + while (aTmp > aGlobalStart && !(pNd = &aTmp.GetNode())->IsContentNode()) --aTmp; - if( !aTmp.GetIndex() ) + if (aTmp <= aGlobalStart) pNd = nullptr; else (*pIdx) = aTmp; @@ -1804,7 +1837,7 @@ void SwNodes::CopyNodes( const SwNodeRange& rRange, // If the end of the section is outside the copy range, // the section node will skipped, not copied! // If someone want to change this behaviour, he has to adjust the function - // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it. + // lcl_NonCopyCount() which relies on it. if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) { // copy of the whole section, so create a new SectionNode @@ -1939,7 +1972,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, if (SwNodeType::Section == pNd->GetNodeType()) { const SwSection& rSect = static_cast<const SwSectionNode*>(pNd)->GetSection(); - if( (bSkipHidden && rSect.IsHiddenFlag()) || + if( (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag()) ) // than skip the section aTmp = *pNd->EndOfSectionNode(); @@ -1950,7 +1983,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, { const SwSection& rSect = static_cast<SwSectionNode*>(pNd-> m_pStartOfSection)->GetSection(); - if( (bSkipHidden && rSect.IsHiddenFlag()) || + if( (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag()) ) // than skip the section aTmp = *pNd->EndOfSectionNode(); @@ -1961,7 +1994,7 @@ SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx, const SwSectionNode* pSectNd; if( ( bSkipHidden || bSkipProtect ) && nullptr != (pSectNd = pNd->FindSectionNode() ) && - ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) || + ( ( bSkipHidden && pSectNd->GetSection().CalcHiddenFlag() ) || ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) ) { aTmp = *pSectNd->EndOfSectionNode(); @@ -1984,8 +2017,9 @@ SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx, { bool bFirst = true; SwNodeIndex aTmp( *pIdx ); + sal_uLong aGlobalStart(aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); const SwNode* pNd; - while( aTmp > 0 ) + while (aTmp > aGlobalStart) { pNd = & aTmp.GetNode(); if (SwNodeType::End == pNd->GetNodeType()) @@ -2035,90 +2069,108 @@ SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx, return nullptr; } -//TODO: improve documentation //TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him! -/** find the next/previous ContentNode or a table node with frames +/** find the next/previous ContentNode or table node that should have layout + * frames that are siblings to the ones of the node at rFrameIdx. * - * If no pEnd is given, search is started with FrameIndex; otherwise - * search is started with the one before rFrameIdx and after pEnd. + * Search is started backward with the one before rFrameIdx and + * forward after pEnd. * - * @param rFrameIdx node with frames to search in - * @param pEnd ??? - * @return result node; 0 (!!!) if not found + * @param rFrameIdx in: node with frames to search in; out: found node + * @param pEnd last node after rFrameIdx that should be excluded from search + * @return result node; 0 if not found */ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx, - const SwNode* pEnd ) const + SwNode const*const pEnd, + SwRootFrame const*const pLayout) const { + assert(pEnd != nullptr); // every caller currently + SwNode* pFrameNd = nullptr; // no layout -> skip if( GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() ) { - SwNode* pSttNd = &rFrameIdx.GetNode(); + SwNode *const pSttNd = &rFrameIdx.GetNode(); - // move of a hidden section? - SwSectionNode* pSectNd = pSttNd->IsSectionNode() + // inside a hidden section? + SwSectionNode *const pSectNd = pSttNd->IsSectionNode() ? pSttNd->StartOfSectionNode()->FindSectionNode() : pSttNd->FindSectionNode(); if( !( pSectNd && pSectNd->GetSection().CalcHiddenFlag() ) ) { // in a table in table situation we have to assure that we don't leave the // outer table cell when the inner table is looking for a PrvNxt... - SwTableNode* pTableNd = pSttNd->IsTableNode() + SwTableNode *const pTableNd = pSttNd->IsTableNode() ? pSttNd->StartOfSectionNode()->FindTableNode() : pSttNd->FindTableNode(); SwNodeIndex aIdx( rFrameIdx ); - SwNode* pNd; - if( pEnd ) - { - --aIdx; - pNd = &aIdx.GetNode(); - } - else - pNd = pSttNd; - - if( ( pFrameNd = pNd )->IsContentNode() ) - rFrameIdx = aIdx; - - // search forward or backward for a content node - else if( nullptr != ( pFrameNd = GoPrevSection( &aIdx, true, false )) && - ::CheckNodesRange( aIdx, rFrameIdx, true ) && - // Never out of the table at the start - pFrameNd->FindTableNode() == pTableNd && - // Bug 37652: Never out of the table at the end - (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode() - == pSttNd->FindTableBoxStartNode() ) && - (!pSectNd || pSttNd->IsSectionNode() || - pSectNd->GetIndex() < pFrameNd->GetIndex()) - ) + + // search backward for a content or table node + + --aIdx; + pFrameNd = &aIdx.GetNode(); + + do { - rFrameIdx = aIdx; + if (pFrameNd->IsContentNode()) + { + // TODO why does this not check for nested tables like forward direction + rFrameIdx = aIdx; + return pFrameNd; + } + else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode()) + { + if (pLayout == nullptr + || !pLayout->IsHideRedlines() + || pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden) + { + pFrameNd = pFrameNd->StartOfSectionNode(); + rFrameIdx = *pFrameNd; + return pFrameNd; + } + else + { + aIdx = *pFrameNd->StartOfSectionNode(); + --aIdx; + pFrameNd = &aIdx.GetNode(); + } + } + else + { + pFrameNd = GoPrevSection( &aIdx, true, false ); + if ( nullptr != pFrameNd && !( + ::CheckNodesRange( aIdx, rFrameIdx, true ) && + // Never out of the table at the start + pFrameNd->FindTableNode() == pTableNd && + // Bug 37652: Never out of the table at the end + (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode() + == pSttNd->FindTableBoxStartNode() ) && + (!pSectNd || pSttNd->IsSectionNode() || + pSectNd->GetIndex() < pFrameNd->GetIndex()) + )) + { + pFrameNd = nullptr; // no preceding content node, stop search + } + } } - else + while (pFrameNd != nullptr); + + // search forward for a content or table node + + aIdx = pEnd->GetIndex() + 1; + pFrameNd = &aIdx.GetNode(); + + do { - if( pEnd ) - aIdx = pEnd->GetIndex() + 1; - else - aIdx = rFrameIdx; - - // NEVER leave the section when doing this! - if( ( pEnd && ( pFrameNd = &aIdx.GetNode())->IsContentNode() ) || - ( nullptr != ( pFrameNd = GoNextSection( &aIdx, true, false )) && - ::CheckNodesRange( aIdx, rFrameIdx, true ) && - ( pFrameNd->FindTableNode() == pTableNd && - // NEVER go out of the table cell at the end - (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode() - == pSttNd->FindTableBoxStartNode() ) ) && - (!pSectNd || pSttNd->IsSectionNode() || - pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()) - )) + if (pFrameNd->IsContentNode()) { // Undo when merging a table with one before, if there is also one after it. // However, if the node is in a table, it needs to be returned if the // SttNode is a section or a table! - SwTableNode* pTableNode; + SwTableNode *const pTableNode = pFrameNd->FindTableNode(); if (pSttNd->IsTableNode() && - nullptr != (pTableNode = pFrameNd->FindTableNode()) && + nullptr != pTableNode && // TABLE IN TABLE: pTableNode != pSttNd->StartOfSectionNode()->FindTableNode()) { @@ -2126,23 +2178,54 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx, rFrameIdx = *pFrameNd; } else + { rFrameIdx = aIdx; + } + return pFrameNd; } - else if( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() ) + else if (pFrameNd->IsTableNode()) { - pFrameNd = pNd->StartOfSectionNode(); - rFrameIdx = *pFrameNd; + if (pLayout == nullptr + || !pLayout->IsHideRedlines() + || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden) + { + rFrameIdx = *pFrameNd; + return pFrameNd; + } + else + { + aIdx = *pFrameNd->EndOfSectionNode(); + ++aIdx; + pFrameNd = &aIdx.GetNode(); + } } else { - if( pEnd ) - aIdx = pEnd->GetIndex() + 1; - else - aIdx = rFrameIdx.GetIndex() + 1; + pFrameNd = GoNextSection( &aIdx, true, false ); + // NEVER leave the section when doing this! + if (pFrameNd + && !(::CheckNodesRange(aIdx, rFrameIdx, true) + && (pFrameNd->FindTableNode() == pTableNd && + // NEVER go out of the table cell at the end + (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode() + == pSttNd->FindTableBoxStartNode())) + && (!pSectNd || pSttNd->IsSectionNode() || + pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex())) + ) + { + pFrameNd = nullptr; // no following content node, stop search + } + } + } + while (pFrameNd != nullptr); - if( (pFrameNd = &aIdx.GetNode())->IsTableNode() ) - rFrameIdx = aIdx; - else + // probably this is dead code, because the GoNextSection() + // should have ended up in the first text node in the table and + // then checked it's in a table? + { + aIdx = pEnd->GetIndex() + 1; + + pFrameNd = &aIdx.GetNode(); { pFrameNd = nullptr; @@ -2160,9 +2243,9 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx, { rFrameIdx = aIdx; pFrameNd = &aIdx.GetNode(); + assert(!"this isn't dead code?"); } } - } } } } diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx index 76d54d27ce23..f80b0a624252 100644 --- a/sw/source/core/docnode/section.cxx +++ b/sw/source/core/docnode/section.cxx @@ -307,14 +307,11 @@ void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition) // Tell all Children that they are hidden SwMsgPoolItem aMsgItem( RES_SECTION_HIDDEN ); pFormat->ModifyNotification( &aMsgItem, &aMsgItem ); - - // Delete all Frames - pFormat->DelFrames(); } } else if (m_Data.IsHiddenFlag()) // show Nodes again { - // Show all Frames (Child Sections are accounted for by MakeFrames) + // Show all Frames // Only if the Parent Section is not restricting us! SwSection* pParentSect = pFormat->GetParentSection(); if( !pParentSect || !pParentSect->IsHiddenFlag() ) @@ -322,8 +319,6 @@ void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition) // Tell all Children that the Parent is not hidden anymore SwMsgPoolItem aMsgItem( RES_SECTION_NOT_HIDDEN ); pFormat->ModifyNotification( &aMsgItem, &aMsgItem ); - - pFormat->MakeFrames(); } } } diff --git a/sw/source/core/edit/acorrect.cxx b/sw/source/core/edit/acorrect.cxx index 7304e6e7b702..286d4d078de5 100644 --- a/sw/source/core/edit/acorrect.cxx +++ b/sw/source/core/edit/acorrect.cxx @@ -346,8 +346,11 @@ OUString const* SwAutoCorrDoc::GetPrevPara(bool const bAtNormalPos) } sw::GotoPrevLayoutTextFrame(*pIdx, rEditSh.GetLayout()); } - if (pFrame && 0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel()) + if (pFrame && !pFrame->GetText().isEmpty() && + 0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel()) + { pStr = & pFrame->GetText(); + } if( bUndoIdInitialized ) bUndoIdInitialized = true; diff --git a/sw/source/core/edit/autofmt.cxx b/sw/source/core/edit/autofmt.cxx index a3925246f32f..6e4bccb55bcc 100644 --- a/sw/source/core/edit/autofmt.cxx +++ b/sw/source/core/edit/autofmt.cxx @@ -1196,7 +1196,7 @@ void SwAutoFormat::DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect) SwPaM* pPrev = rPamToCorrect.GetPrev(); rPamToCorrect.GetRingContainer().merge( pShCursor->GetRingContainer() ); - m_pEditShell->DeleteSel( rDelPam ); + m_pEditShell->DeleteSel(rDelPam, true); // and remove Pam again: SwPaM* p; @@ -1212,7 +1212,7 @@ void SwAutoFormat::DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect) m_pCurTextFrame = GetFrame(*m_pCurTextNd); // keep it up to date } else - m_pEditShell->DeleteSel( rDelPam ); + m_pEditShell->DeleteSel(rDelPam, true); } bool SwAutoFormat::DeleteJoinCurNextPara(SwTextFrame const*const pNextFrame, @@ -1256,7 +1256,7 @@ void SwAutoFormat::DelEmptyLine( bool bTstNextPara ) // delete blanks in empty paragraph m_aDelPam.DeleteMark(); *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos( - TextFrameIndex(m_pCurTextFrame->GetText().getLength())); + TextFrameIndex(0)); m_aDelPam.SetMark(); m_aDelPam.GetMark()->nNode = m_pCurTextFrame->GetTextNodeFirst()->GetIndex() - 1; @@ -1275,16 +1275,25 @@ void SwAutoFormat::DelEmptyLine( bool bTstNextPara ) if( pTNd ) { m_aDelPam.GetMark()->nContent.Assign( pTNd, 0 ); - *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0)); + *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos( + TextFrameIndex(m_pCurTextFrame->GetText().getLength())); } } - else - { - *m_aDelPam.GetMark() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0)); - pTNd = m_pCurTextNd; - } if( pTNd ) - DeleteSel( m_aDelPam ); + { // join with previous or next paragraph + DeleteSel(m_aDelPam); + } + assert(m_aDelPam.GetNode().IsTextNode()); + assert(!m_aDelPam.HasMark()); + m_aDelPam.SetMark(); // mark remains at join position + m_pCurTextFrame = GetFrame(*m_aDelPam.GetNode().GetTextNode()); + // replace until the end of the merged paragraph + *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos( + TextFrameIndex(m_pCurTextFrame->GetText().getLength())); + if (*m_aDelPam.GetPoint() != *m_aDelPam.GetMark()) + { // tdf#137245 replace (not delete) to preserve any flys + m_pDoc->getIDocumentContentOperations().ReplaceRange(m_aDelPam, "", false); + } m_aDelPam.DeleteMark(); ClearRedlineText(); diff --git a/sw/source/core/edit/edatmisc.cxx b/sw/source/core/edit/edatmisc.cxx index e8f82956106f..c70c90182a0b 100644 --- a/sw/source/core/edit/edatmisc.cxx +++ b/sw/source/core/edit/edatmisc.cxx @@ -184,8 +184,8 @@ void SwEditShell::SetAttrSet( const SfxItemSet& rSet, SetAttrMode nFlags, SwPaM* GetDoc()->getIDocumentContentOperations().InsertItemSet(*pCursor, rSet, nFlags, GetLayout()); } - EndAllAction(); GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode ); + EndAllAction(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/edit/eddel.cxx b/sw/source/core/edit/eddel.cxx index 74e845353566..e16b8256b611 100644 --- a/sw/source/core/edit/eddel.cxx +++ b/sw/source/core/edit/eddel.cxx @@ -34,13 +34,17 @@ #include <undobj.hxx> #include <SwRewriter.hxx> #include <globals.hrc> +#include <wrtsh.hxx> +#include <officecfg/Office/Writer.hxx> #include <strings.hrc> #include <vector> -void SwEditShell::DeleteSel( SwPaM& rPam, bool* pUndo ) +void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, bool *const pUndo) { - bool bSelectAll = StartsWithTable() && ExtendedSelectedAll(); + auto const oSelectAll(StartsWith_() != SwCursorShell::StartsWith::None + ? ExtendedSelectedAll() + : ::std::optional<::std::pair<SwNode const*, ::std::vector<SwTableNode *>>>{}); // only for selections if (!rPam.HasMark() || (*rPam.GetPoint() == *rPam.GetMark() @@ -56,7 +60,7 @@ void SwEditShell::DeleteSel( SwPaM& rPam, bool* pUndo ) // 3. Point and Mark are at the document start and end, Point is in a table: delete selection as usual if( rPam.GetNode().FindTableNode() && rPam.GetNode().StartOfSectionNode() != - rPam.GetNode(false).StartOfSectionNode() && !bSelectAll ) + rPam.GetNode(false).StartOfSectionNode() && !oSelectAll) { // group the Undo in the table if( pUndo && !*pUndo ) @@ -100,43 +104,57 @@ void SwEditShell::DeleteSel( SwPaM& rPam, bool* pUndo ) { std::unique_ptr<SwPaM> pNewPam; SwPaM * pPam = &rPam; - if (bSelectAll) + if (oSelectAll) { - assert(dynamic_cast<SwShellCursor*>(&rPam)); // must be corrected pam - pNewPam.reset(new SwPaM(*rPam.GetMark(), *rPam.GetPoint())); - // Selection starts at the first para of the first cell, but we - // want to delete the table node before the first cell as well. - while (SwTableNode const* pTableNode = - pNewPam->Start()->nNode.GetNode().StartOfSectionNode()->FindTableNode()) + if (!oSelectAll->second.empty()) { - pNewPam->Start()->nNode = *pTableNode; + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_MULTISEL)); + GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter); } - // tdf#133990 ensure section is included in SwUndoDelete - while (SwSectionNode const* pSectionNode = - pNewPam->Start()->nNode.GetNode().StartOfSectionNode()->FindSectionNode()) + // tdf#155685 tables at the end must be deleted separately + for (SwTableNode *const pTable : oSelectAll->second) { - pNewPam->Start()->nNode = *pSectionNode; + GetDoc()->DelTable(pTable); } - pNewPam->Start()->nContent.Assign(nullptr, 0); + assert(dynamic_cast<SwShellCursor*>(&rPam)); // must be corrected pam + pNewPam.reset(new SwPaM(*rPam.GetMark(), *rPam.GetPoint())); + // Selection starts at the first para of the first cell, but we + // want to delete the table node before the first cell as well. + *pNewPam->Start() = SwPosition(*oSelectAll->first); pPam = pNewPam.get(); } // delete everything - GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam); + GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam, + isArtificialSelection ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default); SaveTableBoxContent( pPam->GetPoint() ); + if (oSelectAll && !oSelectAll->second.empty()) + { + GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr); + } } // Selection is not needed anymore rPam.DeleteMark(); } -bool SwEditShell::Delete() +bool SwEditShell::Delete(bool const isArtificialSelection) { SET_CURR_SHELL( this ); bool bRet = false; if ( !HasReadonlySel() || CursorInsideInputField() ) { - StartAllAction(); + if (HasHiddenSections() && + officecfg::Office::Writer::Content::Display::ShowWarningHiddenSection::get()) + { + if (!WarnHiddenSectionDialog()) + { + bRet = RemoveParagraphMetadataFieldAtCursor(); + return bRet; + } + } + StartAllAction(); bool bUndo = GetCursor()->GetNext() != GetCursor(); if( bUndo ) // more than one selection? { @@ -148,7 +166,7 @@ bool SwEditShell::Delete() for(SwPaM& rPaM : GetCursor()->GetRingContainer()) { - DeleteSel( rPaM, &bUndo ); + DeleteSel(rPaM, isArtificialSelection, &bUndo); } // If undo container then close here @@ -162,6 +180,11 @@ bool SwEditShell::Delete() else { bRet = RemoveParagraphMetadataFieldAtCursor(); + if (!bRet) + { + // false indicates HasReadonlySel failed + InfoReadOnlyDialog(); + } } return bRet; @@ -326,7 +349,7 @@ bool SwEditShell::Replace( const OUString& rNewStr, bool bRegExpRplc ) SET_CURR_SHELL( this ); bool bRet = false; - if( !HasReadonlySel() ) + if (!HasReadonlySel(true)) { StartAllAction(); GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx index abbed5e40e94..1d68caa68d4f 100644 --- a/sw/source/core/edit/edfcol.cxx +++ b/sw/source/core/edit/edfcol.cxx @@ -2202,8 +2202,7 @@ void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat, GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter); for(SwPaM& rPaM : GetCursor()->GetRingContainer()) { - - if ( !rPaM.HasReadonlySel( GetViewOptions()->IsFormView() ) ) + if (!rPaM.HasReadonlySel( GetViewOptions()->IsFormView(), true)) { // tdf#105413 turn off ShowChanges mode for the next loops to apply styles permanently with redlining, // ie. in all directly preceding deleted paragraphs at the actual cursor positions diff --git a/sw/source/core/edit/edglbldc.cxx b/sw/source/core/edit/edglbldc.cxx index 3d916edc5fe0..c5ea9081d043 100644 --- a/sw/source/core/edit/edglbldc.cxx +++ b/sw/source/core/edit/edglbldc.cxx @@ -272,7 +272,7 @@ void SwEditShell::DeleteGlobalDocContent( const SwGlblDocContents& rArr , rPos.nNode = pMyDoc->GetNodes().GetEndOfContent(); --rPos.nNode; if( !pMyDoc->getIDocumentContentOperations().DelFullPara( *pCursor ) ) - Delete(); + Delete(false); } break; diff --git a/sw/source/core/edit/edglss.cxx b/sw/source/core/edit/edglss.cxx index ed46482bf933..49683c682371 100644 --- a/sw/source/core/edit/edglss.cxx +++ b/sw/source/core/edit/edglss.cxx @@ -203,7 +203,9 @@ bool SwEditShell::CopySelToDoc( SwDoc* pInsDoc ) bool bColSel = GetCursor_()->IsColumnSelection(); if( bColSel && pInsDoc->IsClipBoard() ) pInsDoc->SetColumnSelection( true ); - bool bSelectAll = StartsWithTable() && ExtendedSelectedAll(); + auto const oSelectAll(StartsWith_() != SwCursorShell::StartsWith::None + ? ExtendedSelectedAll() + : ::std::optional<::std::pair<SwNode const*, ::std::vector<SwTableNode*>>>{}); { for(SwPaM& rPaM : GetCursor()->GetRingContainer()) { @@ -227,18 +229,21 @@ bool SwEditShell::CopySelToDoc( SwDoc* pInsDoc ) // for the purpose of copying, our shell cursor is not touched. // (Otherwise we would have to restore it.) SwPaM aPaM(*rPaM.GetMark(), *rPaM.GetPoint()); - if (bSelectAll) + if (oSelectAll) { // Selection starts at the first para of the first cell, // but we want to copy the table and the start node before // the first cell as well. - // tdf#133982 tables can be nested - while (SwTableNode const* pTableNode = - aPaM.Start()->nNode.GetNode().StartOfSectionNode()->FindTableNode()) + *aPaM.Start() = SwPosition(*oSelectAll->first); + if (SwSectionNode const* pSection = oSelectAll->first->GetSectionNode()) { - aPaM.Start()->nNode = *pTableNode; + if (aPaM.End()->nNode.GetIndex() < pSection->EndOfSectionIndex()) + { + // include section end so that section is copied + aPaM.End()->nNode = *oSelectAll->first->GetNodes()[pSection->EndOfSectionIndex() + 1]; + aPaM.End()->nContent.Assign(aPaM.End()->nNode.GetNode().GetContentNode(), 0); + } } - aPaM.Start()->nContent.Assign(nullptr, 0); } bRet = GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false ) || bRet; } diff --git a/sw/source/core/edit/editsh.cxx b/sw/source/core/edit/editsh.cxx index 8f84ce42ed75..d697fd05c286 100644 --- a/sw/source/core/edit/editsh.cxx +++ b/sw/source/core/edit/editsh.cxx @@ -663,7 +663,7 @@ bool SwEditShell::InsertURL( const SwFormatINetFormat& rFormat, const OUString& bDelText = bInsText = false; if( bDelText ) - Delete(); + Delete(true); } else if( pCursor->IsMultiSelection() && rFormat.GetValue() == rStr ) bInsText = false; @@ -732,7 +732,7 @@ void SwEditShell::DelINetAttrWithText() { bool bRet = SelectTextAttr( RES_TXTATR_INETFMT, false ); if( bRet ) - DeleteSel( *GetCursor() ); + DeleteSel(*GetCursor(), true); } /// Set the DontExpand flag at the text character attributes @@ -789,7 +789,7 @@ void SwEditShell::SetNumberingRestart() if( nullptr != ( pContentFrame = static_cast<SwTextNode*>(pNd)->getLayoutFrame( GetLayout() )) ) { // skip hidden frames - ignore protection! - if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() ) + if( !pContentFrame->IsHiddenNow() ) { // if the node is numbered and the starting value of the numbering equals the // start value of the numbering rule then set this value as hard starting value diff --git a/sw/source/core/edit/edlingu.cxx b/sw/source/core/edit/edlingu.cxx index 69446dd9b060..8c50eadd8740 100644 --- a/sw/source/core/edit/edlingu.cxx +++ b/sw/source/core/edit/edlingu.cxx @@ -823,11 +823,16 @@ void SwEditShell::HandleCorrectionError(const OUString& aText, SwPosition aPos, SwRect& rSelectRect) { // save the start and end positions of the line and the starting point + SwNode const& rNode(GetCursor()->GetPoint()->nNode.GetNode()); Push(); LeftMargin(); - const sal_Int32 nLineStart = GetCursor()->GetPoint()->nContent.GetIndex(); + const sal_Int32 nLineStart = &rNode == &GetCursor()->GetPoint()->nNode.GetNode() + ? GetCursor()->GetPoint()->nContent.GetIndex() + : 0; RightMargin(); - const sal_Int32 nLineEnd = GetCursor()->GetPoint()->nContent.GetIndex(); + const sal_Int32 nLineEnd = &rNode == &GetCursor()->GetPoint()->nNode.GetNode() + ? GetCursor()->GetPoint()->nContent.GetIndex() + : rNode.GetTextNode()->Len(); Pop(PopMode::DeleteCurrent); // make sure the selection build later from the data below does @@ -909,8 +914,14 @@ uno::Reference< XSpellAlternatives > if (pWrong->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin)) { const OUString aText(pNode->GetText().copy(nBegin, nLen)); - OUString aWord = aText.replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "") - .replaceAll(OUStringChar(CH_TXTATR_INWORD), ""); + // TODO: this doesn't handle fieldmarks properly + ModelToViewHelper const aConversionMap(*pNode, GetLayout(), + ExpandMode::ExpandFields | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode + | (GetLayout()->IsHideRedlines() ? ExpandMode::HideDeletions : ExpandMode(0)) + | (GetViewOptions()->IsShowHiddenChar() ? ExpandMode(0) : ExpandMode::HideInvisible)); + auto const nBeginView(aConversionMap.ConvertToViewPosition(nBegin)); + OUString const aWord(aConversionMap.getViewText().copy(nBeginView, + aConversionMap.ConvertToViewPosition(nBegin+nLen) - nBeginView)); uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() ); if( xSpell.is() ) diff --git a/sw/source/core/edit/edws.cxx b/sw/source/core/edit/edws.cxx index dd5381cbb9eb..4991b9376a89 100644 --- a/sw/source/core/edit/edws.cxx +++ b/sw/source/core/edit/edws.cxx @@ -265,6 +265,12 @@ void SwEditShell::AutoCorrect( SvxAutoCorrect& rACorr, bool bInsert, // FIXME: this _must_ be called with reference to the actual node text! SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout()))); TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint())); + // tdf#147414 sw_redlinehide: if cursor moved backward, it may be at the + // start of a delete redline - but MapViewToModelPos() always returns end + // of redline and it will be called when AutoCorrect actually inserts + // something - so first normalize cursor point to end of redline so that + // point will then be moved forward when something is inserted. + *pCursor->GetPoint() = pFrame->MapViewToModelPos(nPos); OUString const& rMergedText(pFrame->GetText()); rACorr.DoAutoCorrect( aSwAutoCorrDoc, rMergedText, sal_Int32(nPos), diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx index c4825a9343df..9aaf9b2b9f34 100644 --- a/sw/source/core/fields/reffld.cxx +++ b/sw/source/core/fields/reffld.cxx @@ -771,7 +771,7 @@ static std::pair<OUString, bool> MakeRefNumStr( SwTextNode const& rTextNodeOfReferencedItem(pLayout ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem) : i_rTextNodeOfReferencedItem); - if ( rTextNodeOfReferencedItem.HasNumber() && + if ( rTextNodeOfReferencedItem.HasNumber(pLayout) && rTextNodeOfReferencedItem.IsCountedInList() ) { OSL_ENSURE( rTextNodeOfReferencedItem.GetNum(pLayout), @@ -795,7 +795,7 @@ static std::pair<OUString, bool> MakeRefNumStr( == rTextNodeOfReferencedItem.FindFooterStartNode() ) { const SwNodeNum* pNodeNumForTextNodeOfField( nullptr ); - if ( rTextNodeOfField.HasNumber() && + if ( rTextNodeOfField.HasNumber(pLayout) && rTextNodeOfField.GetNumRule() == rTextNodeOfReferencedItem.GetNumRule() ) { pNodeNumForTextNodeOfField = rTextNodeOfField.GetNum(pLayout); diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx index c2470b997a93..a39b7479e669 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -686,25 +686,19 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) OSL_ENSURE( pClpDoc, "no clipboard document" ); // then till end of the nodes array SwNodeIndex aIdx( pClpDoc->GetNodes().GetEndOfExtras(), 2 ); - SwPaM aCpyPam( aIdx ); //DocStart + // select content section, whatever it may contain + SwPaM aCpyPam(aIdx, SwNodeIndex(pClpDoc->GetNodes().GetEndOfContent(), -1)); + if (SwContentNode *const pAtEnd = aCpyPam.GetNode(true).GetContentNode()) + { + pAtEnd->MakeEndIndex(&aCpyPam.GetPoint()->nContent); + } // If there are table formulas in the area, then display the table first // so that the table formula can calculate a new value first // (individual boxes in the area are retrieved via the layout) SwFieldType* pTableFieldTyp = GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table ); - SwTableNode *const pSrcNd = aCpyPam.GetNode().GetTableNode(); - if( !pSrcNd ) // table node ? - { // don't skip !! - SwContentNode* pCNd = aCpyPam.GetNode().GetContentNode(); - if( pCNd ) - aCpyPam.GetPoint()->nContent.Assign( pCNd, 0 ); - else if( !aCpyPam.Move( fnMoveForward, GoInNode )) - aCpyPam.Move( fnMoveBackward, GoInNode ); - } - - aCpyPam.SetMark(); - aCpyPam.Move( fnMoveForward, GoInDoc ); + SwTableNode *const pSrcNd = aCpyPam.GetNode(false).GetTableNode(); bool bRet = true; StartAllAction(); @@ -1021,7 +1015,7 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) { if( bDelTable && IsTableMode() ) { - SwEditShell::Delete(); + SwEditShell::Delete(false); bDelTable = false; } @@ -1045,9 +1039,24 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) --aIndexBefore; + // copying to the clipboard, the section is inserted + // at the start of the nodes, followed by empty text node + bool const isSourceSection(aCpyPam.Start()->nNode.GetNode().IsSectionNode() + && aCpyPam.End()->nNode.GetIndex() == aCpyPam.Start()->nNode.GetNode().EndOfSectionIndex() + 1 + && aCpyPam.End()->nNode.GetNode().IsTextNode() + && aCpyPam.End()->nNode.GetNode().GetTextNode()->Len() == 0); + pClpDoc->getIDocumentContentOperations().CopyRange( aCpyPam, rInsPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false ); // Note: aCpyPam is invalid now + if (isSourceSection + && aIndexBefore.GetNode().IsStartNode() + && rInsPos.nNode.GetNode().GetTextNode()->Len() == 0) + { // if there is an empty text node at the start, it + // should be *replaced* by the section, so delete it + GetDoc()->getIDocumentContentOperations().DelFullPara(rPaM); + } + ++aIndexBefore; SwPaM aPaM(SwPosition(aIndexBefore), SwPosition(rInsPos.nNode)); diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx index fd0a930a95a0..65e684e1af06 100644 --- a/sw/source/core/frmedt/feshview.cxx +++ b/sw/source/core/frmedt/feshview.cxx @@ -3243,8 +3243,11 @@ bool SwFEShell::IsShapeDefaultHoriTextDirR2L() const if ( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr ) { // determine page frame of the frame the shape is anchored. - const SwFrame* pAnchorFrame = - static_cast<SwDrawContact*>(GetUserCall(pSdrObj))->GetAnchorFrame( pSdrObj ); + const SwContact* pContact = GetUserCall(pSdrObj); + OSL_ENSURE( pContact, "<SwFEShell::IsShapeDefaultHoriTextDirR2L(..)> - missing contact!" ); + if (!pContact) + return false; + const SwFrame* pAnchorFrame = static_cast<const SwDrawContact*>(pContact)->GetAnchorFrame( pSdrObj ); OSL_ENSURE( pAnchorFrame, "inconsistent model - no anchor at shape!"); if ( pAnchorFrame ) { diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx index a6cd65a55d15..936ababfaea4 100644 --- a/sw/source/core/frmedt/fetab.cxx +++ b/sw/source/core/frmedt/fetab.cxx @@ -188,7 +188,7 @@ void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind ) // search boxes via the layout SwSelBoxes aBoxes; - bool bSelectAll = StartsWithTable() && ExtendedSelectedAll(); + 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. @@ -296,9 +296,8 @@ bool SwFEShell::DeleteCol() // then delete the column StartUndo(SwUndoId::COL_DELETE); - bRet = GetDoc()->DeleteRowCol( aBoxes, true ); + bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); EndUndo(SwUndoId::COL_DELETE); - } else bRet = false; diff --git a/sw/source/core/inc/DocumentContentOperationsManager.hxx b/sw/source/core/inc/DocumentContentOperationsManager.hxx index 2d600b6ff8ba..994812dc14b4 100644 --- a/sw/source/core/inc/DocumentContentOperationsManager.hxx +++ b/sw/source/core/inc/DocumentContentOperationsManager.hxx @@ -48,6 +48,7 @@ public: // Add optional parameter <bForceJoinNext>, default value <false> // Needed for hiding of deletion redlines bool DeleteAndJoin( SwPaM&, + SwDeleteFlags flags = SwDeleteFlags::Default, const bool bForceJoinNext = false ) override; bool MoveRange(SwPaM&, SwPosition&, SwMoveFlags) override; @@ -159,10 +160,10 @@ public: private: SwDoc& m_rDoc; - bool DeleteAndJoinImpl(SwPaM&, const bool); - bool DeleteAndJoinWithRedlineImpl(SwPaM&, const bool unused = false); - bool DeleteRangeImpl(SwPaM&, const bool unused = false); - bool DeleteRangeImplImpl(SwPaM &); + bool DeleteAndJoinImpl(SwPaM&, SwDeleteFlags, const bool); + bool DeleteAndJoinWithRedlineImpl(SwPaM&, SwDeleteFlags, const bool unused = false); + bool DeleteRangeImpl(SwPaM&, SwDeleteFlags, const bool unused = false); + bool DeleteRangeImplImpl(SwPaM &, SwDeleteFlags); bool ReplaceRangeImpl(SwPaM&, OUString const&, const bool); SwFlyFrameFormat* InsNoTextNode( const SwPosition&rPos, SwNoTextNode*, const SfxItemSet* pFlyAttrSet, diff --git a/sw/source/core/inc/MarkManager.hxx b/sw/source/core/inc/MarkManager.hxx index 762c21e59a85..840d492c2b35 100644 --- a/sw/source/core/inc/MarkManager.hxx +++ b/sw/source/core/inc/MarkManager.hxx @@ -63,7 +63,7 @@ namespace sw { virtual void correctMarksAbsolute(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset) override; virtual void correctMarksRelative(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset) override; - virtual void deleteMarks(const SwNodeIndex& rStt, const SwNodeIndex& rEnd, std::vector< ::sw::mark::SaveBookmark>* pSaveBkmk, const SwIndex* pSttIdx, const SwIndex* pEndIdx) override; + virtual void deleteMarks(const SwNodeIndex& rStt, const SwNodeIndex& rEnd, std::vector< ::sw::mark::SaveBookmark>* pSaveBkmk, const SwIndex* pSttIdx, const SwIndex* pEndIdx, bool isReplace) override; // deleters virtual std::unique_ptr<ILazyDeleter> @@ -78,7 +78,7 @@ namespace sw { virtual const_iterator_t findMark(const OUString& rName) const override; // bookmarks - virtual bool isBookmarkDeleted(SwPaM const& rPaM) const override; + virtual bool isBookmarkDeleted(SwPaM const& rPaM, bool isReplace) const override; virtual const_iterator_t getBookmarksBegin() const override; virtual const_iterator_t getBookmarksEnd() const override; virtual sal_Int32 getBookmarksCount() const override; diff --git a/sw/source/core/inc/UndoDelete.hxx b/sw/source/core/inc/UndoDelete.hxx index a4eb066581c9..b4ae4544d669 100644 --- a/sw/source/core/inc/UndoDelete.hxx +++ b/sw/source/core/inc/UndoDelete.hxx @@ -27,6 +27,7 @@ class SwRedlineSaveDatas; class SwTextNode; +enum class SwDeleteFlags; namespace sfx2 { class MetadatableUndo; @@ -59,6 +60,7 @@ class SwUndoDelete bool m_bResetPgDesc : 1; // TRUE: reset PgDsc on following node bool m_bResetPgBrk : 1; // TRUE: reset PgBreak on following node bool const m_bFromTableCopy : 1; // TRUE: called by SwUndoTableCpyTable + SwDeleteFlags m_DeleteFlags; bool SaveContent( const SwPosition* pStt, const SwPosition* pEnd, SwTextNode* pSttTextNd, SwTextNode* pEndTextNd ); @@ -66,6 +68,7 @@ class SwUndoDelete public: SwUndoDelete( SwPaM&, + SwDeleteFlags flags, bool bFullPara = false, bool bCalledByTableCpy = false ); virtual ~SwUndoDelete() override; diff --git a/sw/source/core/inc/UndoRedline.hxx b/sw/source/core/inc/UndoRedline.hxx index 38ecd86314cb..ada3c34fad7f 100644 --- a/sw/source/core/inc/UndoRedline.hxx +++ b/sw/source/core/inc/UndoRedline.hxx @@ -22,6 +22,7 @@ #include <memory> #include <undobj.hxx> +#include <IDocumentContentOperations.hxx> struct SwSortOptions; class SwRangeRedline; @@ -52,17 +53,22 @@ public: class SwUndoRedlineDelete : public SwUndoRedline { +private: + std::unique_ptr<SwHistory> m_pHistory; ///< for moved fly anchors + // bool bCanGroup : 1; bool bIsDelim : 1; bool bIsBackspace : 1; OUString m_sRedlineText; + void InitHistory(SwPaM const& rRange); + virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; public: - SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUserId ); + SwUndoRedlineDelete(const SwPaM& rRange, SwUndoId nUserId, SwDeleteFlags flags = SwDeleteFlags::Default); virtual SwRewriter GetRewriter() const override; bool CanGrouping( const SwUndoRedlineDelete& rPrev ); diff --git a/sw/source/core/inc/UndoTable.hxx b/sw/source/core/inc/UndoTable.hxx index ecfa87745ef7..8815cb0881c7 100644 --- a/sw/source/core/inc/UndoTable.hxx +++ b/sw/source/core/inc/UndoTable.hxx @@ -42,6 +42,14 @@ class SwStartNode; class SwTableNode; class SwTableAutoFormat; class SwTableSortBoxes; +class SwContentNode; +class SwCursorShell; + +namespace sw { + +void NotifyTableCollapsedParagraph(const SwContentNode* pNode, SwCursorShell *const pShell); + +} class SwUndoInsTable : public SwUndo { @@ -272,7 +280,7 @@ class SwUndoTableCpyTable : public SwUndo //b6341295: When redlining is active, PrepareRedline has to create the //redlining attributes for the new and the old table cell content static std::unique_ptr<SwUndo> PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox, - const SwPosition& rPos, bool& rJoin, bool bRedo ); + SwPosition& rPos, bool& rJoin, bool bRedo ); public: SwUndoTableCpyTable(const SwDoc* pDoc); diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx index a607745db007..cb5a2418ef21 100644 --- a/sw/source/core/inc/frame.hxx +++ b/sw/source/core/inc/frame.hxx @@ -861,6 +861,8 @@ public: // Fly in ... and footnotes bool IsProtected() const; + virtual bool IsHiddenNow() const; + bool IsColLocked() const { return mbColLocked; } virtual bool IsDeleteForbidden() const { return mnForbidDelete > 0; } diff --git a/sw/source/core/inc/layfrm.hxx b/sw/source/core/inc/layfrm.hxx index f7d90f00d8f6..89d23f210f45 100644 --- a/sw/source/core/inc/layfrm.hxx +++ b/sw/source/core/inc/layfrm.hxx @@ -182,6 +182,7 @@ public: m_VertPosOrientFramesFor.end(), pObj), m_VertPosOrientFramesFor.end()); } + void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override; }; /** diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx index c472b6f7bc1a..14b9ee6f6be5 100644 --- a/sw/source/core/inc/mvsave.hxx +++ b/sw/source/core/inc/mvsave.hxx @@ -34,6 +34,7 @@ class SwDoc; class SwFormatAnchor; class SwFrameFormat; class SwIndex; +class SwHistory; class SwNodeIndex; class SwNodeRange; class SwPaM; @@ -94,7 +95,8 @@ void DelBookmarks(const SwNodeIndex& rStt, const SwNodeIndex& rEnd, std::vector< ::sw::mark::SaveBookmark> * SaveBkmk =nullptr, const SwIndex* pSttIdx =nullptr, - const SwIndex* pEndIdx =nullptr); + const SwIndex* pEndIdx = nullptr, + bool isReplace = false); /** data structure to temporarily hold fly anchor positions relative to some * location. */ @@ -116,10 +118,10 @@ struct SaveFly typedef std::deque< SaveFly > SaveFlyArr; void RestFlyInRange( SaveFlyArr& rArr, const SwPosition& rSttIdx, - const SwNodeIndex* pInsPos ); + const SwNodeIndex* pInsPos, bool isForceToStartPos = false); void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr ); void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos, - SaveFlyArr& rArr, bool bMoveAllFlys ); + SaveFlyArr& rArr, bool bMoveAllFlys, SwHistory * pHistory = nullptr); void DelFlyInRange( const SwNodeIndex& rMkNdIdx, const SwNodeIndex& rPtNdIdx, diff --git a/sw/source/core/inc/rootfrm.hxx b/sw/source/core/inc/rootfrm.hxx index 484ff172e25d..44d7a5800032 100644 --- a/sw/source/core/inc/rootfrm.hxx +++ b/sw/source/core/inc/rootfrm.hxx @@ -76,7 +76,7 @@ using SwDestroyList = std::set<SwSectionFrame*>; /// The root element of a Writer document layout. Lower frames are expected to /// be SwPageFrame instances. -class SAL_DLLPUBLIC_RTTI SwRootFrame: public SwLayoutFrame +class SW_DLLPUBLIC SwRootFrame: public SwLayoutFrame { // Needs to disable the Superfluous temporarily friend void AdjustSizeChgNotify( SwRootFrame *pRoot ); diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx index 3b890b385e2a..8235e6244335 100644 --- a/sw/source/core/inc/sectfrm.hxx +++ b/sw/source/core/inc/sectfrm.hxx @@ -75,6 +75,8 @@ public: virtual void Cut() override; virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override; + virtual bool IsHiddenNow() const override; + inline const SwSectionFrame *GetFollow() const; inline SwSectionFrame *GetFollow(); SwSectionFrame* FindMaster() const; @@ -96,7 +98,7 @@ public: * Splits the SectionFrame surrounding the pFrame up in two parts: * pFrame and the start of the 2nd part */ - bool SplitSect( SwFrame* pFrame, bool bApres ); + SwSectionFrame* SplitSect( SwFrame* pFrameStartAfter, SwFrame* pFramePutAfter ); void DelEmpty( bool bRemove ); // Like Cut(), except for that Follow chaining is maintained SwFootnoteContFrame* ContainsFootnoteCont( const SwFootnoteContFrame* pCont = nullptr ) const; bool Growable() const; diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index 6bf44794cbaf..7681324c7eed 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -537,7 +537,7 @@ public: #endif /// Hidden - bool IsHiddenNow() const; // bHidden && pOut == pPrt + virtual bool IsHiddenNow() const override; // bHidden && pOut == pPrt void HideHidden(); // Remove appendage if Hidden void HideFootnotes(TextFrameIndex nStart, TextFrameIndex nEnd); diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index 79235781896d..884d791caebe 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -3363,7 +3363,7 @@ SwHandleAnchorNodeChg::~SwHandleAnchorNodeChg() COVERITY_NOEXCEPT_FALSE mpWrtShell->SwEditShell::Copy(mpWrtShell); mpWrtShell->DestroyCursor(); - mpWrtShell->Delete(); + mpWrtShell->Delete(false); mpWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); } diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx index b9687accdf90..b889de6689ae 100644 --- a/sw/source/core/layout/calcmove.cxx +++ b/sw/source/core/layout/calcmove.cxx @@ -163,7 +163,7 @@ bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & ) if ( nMoveAnyway < 3 ) { - if ( nSpace ) + if (nSpace || IsHiddenNow()) { // Do not notify footnotes which are stuck to the paragraph: // This would require extremely confusing code, taking into @@ -195,7 +195,7 @@ bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & ) } // Check for space left in new upper - return nSpace != 0; + return nSpace != 0 || IsHiddenNow(); } } return false; @@ -531,7 +531,7 @@ static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame ) do { pRet = lcl_Prev( pRet ); - } while ( pRet && pRet->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() ); + } while ( pRet && pRet->IsHiddenNow() ); return pRet; } @@ -1076,9 +1076,8 @@ void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs ) { setFramePrintAreaValid(true); SwRectFnSet aRectFnSet(this); - const bool bTextFrame = IsTextFrame(); SwTwips nUpper = 0; - if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() ) + if (IsTextFrame() && IsHiddenNow()) { if ( static_cast<SwTextFrame*>(this)->HasFollow() ) static_cast<SwTextFrame*>(this)->JoinFrame(); @@ -1709,7 +1708,7 @@ void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/) const bool bMoveFwdInvalid = nullptr != GetIndNext(); const bool bNxtNew = ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) && - (!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow()); + !pNxt->IsHiddenNow(); pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut()); @@ -2211,7 +2210,7 @@ bool SwContentFrame::WouldFit_( SwTwips nSpace, pTmpPrev = nullptr; else { - if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() ) + if (pFrame->IsHiddenNow()) pTmpPrev = lcl_NotHiddenPrev( pFrame ); else pTmpPrev = pFrame; diff --git a/sw/source/core/layout/findfrm.cxx b/sw/source/core/layout/findfrm.cxx index a35af84d54a1..9468c6283fbe 100644 --- a/sw/source/core/layout/findfrm.cxx +++ b/sw/source/core/layout/findfrm.cxx @@ -896,7 +896,7 @@ SwFrame *SwFrame::FindNext_() (!bFootnote || pSct->IsInFootnote() ) ) return pSct; } - return pRet; + return pRet == this ? nullptr : pRet; } // #i27138# - add parameter <_bInSameFootnote> @@ -1282,11 +1282,7 @@ void SwFrame::InvalidateNextPrtArea() SwFrame* pNextFrame = FindNext(); // skip empty section frames and hidden text frames { - while ( pNextFrame && - ( ( pNextFrame->IsSctFrame() && - !static_cast<SwSectionFrame*>(pNextFrame)->GetSection() ) || - ( pNextFrame->IsTextFrame() && - static_cast<SwTextFrame*>(pNextFrame)->IsHiddenNow() ) ) ) + while (pNextFrame && pNextFrame->IsHiddenNow()) { pNextFrame = pNextFrame->FindNext(); } diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx index 2cf8ba751b7a..b29909288056 100644 --- a/sw/source/core/layout/flowfrm.cxx +++ b/sw/source/core/layout/flowfrm.cxx @@ -1157,8 +1157,7 @@ bool SwFlowFrame::IsPageBreak( bool bAct ) const // Determine predecessor const SwFrame *pPrev = m_rThis.FindPrev(); - while ( pPrev && ( !pPrev->IsInDocBody() || - ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) ) + while (pPrev && (!pPrev->IsInDocBody() || pPrev->IsHiddenNow())) pPrev = pPrev->FindPrev(); if ( pPrev ) @@ -1219,7 +1218,7 @@ bool SwFlowFrame::IsColBreak( bool bAct ) const // Determine predecessor const SwFrame *pPrev = m_rThis.FindPrev(); while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) || - ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) ) + pPrev->IsHiddenNow() ) ) pPrev = pPrev->FindPrev(); if ( pPrev ) @@ -1250,6 +1249,14 @@ bool SwFlowFrame::IsColBreak( bool bAct ) const return false; } +// Skip hidden paragraphs and empty sections on the same level +static const SwFrame* skipHiddenSiblingFrames_(const SwFrame* pFrame) +{ + while (pFrame && pFrame->IsHiddenNow()) + pFrame = pFrame->GetPrev(); + return pFrame; +} + bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const { if( m_rThis.IsInSct() ) @@ -1265,7 +1272,7 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const return !pTmp->GetPrev() || IsPageBreak(true); if( pTmp->IsColumnFrame() && pTmp->GetPrev() ) return IsColBreak( true ); - if( pTmp->IsSctFrame() && ( !bSct || pTmp->GetPrev() ) ) + if (pTmp->IsSctFrame() && (!bSct || skipHiddenSiblingFrames_(pTmp->GetPrev()))) return false; pTmp = pTmp->GetUpper(); } @@ -1287,6 +1294,31 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const return pTmp && !pTmp->GetPrev(); } +// Skip hidden paragraphs and empty sections +static const SwFrame* skipHiddenFrames_(const SwFrame* pFrame) +{ + do + { + pFrame = skipHiddenSiblingFrames_(pFrame); + if (!pFrame || !pFrame->IsSctFrame()) + return pFrame; + // Special case: found previous frame is a section + // Search for the last content in the section + auto pSectFrame = static_cast<const SwSectionFrame*>(pFrame); + pFrame = pSectFrame->FindLastContent(); + // If the last content is in a table _inside_ the section, + // take the table herself. + // Correction: Check directly, if table is inside table, instead of indirectly + // by checking, if section isn't inside a table + if (pFrame && pFrame->IsInTab()) + { + const SwTabFrame* pTableFrame = pFrame->FindTabFrame(); + if (pSectFrame->IsAnLower(pTableFrame)) + return pTableFrame; + } + } while (true); +} + /** helper method to determine previous frame for calculation of the upper space @@ -1294,74 +1326,21 @@ bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const */ const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const { - const SwFrame* pPrevFrame = _pProposedPrevFrame - ? _pProposedPrevFrame - : m_rThis.GetPrev(); - - // Skip hidden paragraphs and empty sections - while ( pPrevFrame && - ( ( pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) || - ( pPrevFrame->IsSctFrame() && - !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } + const SwFrame* pPrevFrame + = skipHiddenFrames_(_pProposedPrevFrame ? _pProposedPrevFrame : m_rThis.GetPrev()); + if (pPrevFrame || !m_rThis.IsInFootnote() + || !(m_rThis.IsSctFrame() || !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote())) + return pPrevFrame; // Special case: no direct previous frame is found but frame is in footnote // Search for a previous frame in previous footnote, // if frame isn't in a section, which is also in the footnote - if ( !pPrevFrame && m_rThis.IsInFootnote() && - ( m_rThis.IsSctFrame() || - !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote() ) ) - { - const SwFootnoteFrame* pPrevFootnoteFrame = - static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev()); - if ( pPrevFootnoteFrame ) - { - pPrevFrame = pPrevFootnoteFrame->GetLastLower(); - - // Skip hidden paragraphs and empty sections - while ( pPrevFrame && - ( ( pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) || - ( pPrevFrame->IsSctFrame() && - !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } - } - } - // Special case: found previous frame is a section - // Search for the last content in the section - if( pPrevFrame && pPrevFrame->IsSctFrame() ) - { - const SwSectionFrame* pPrevSectFrame = - static_cast<const SwSectionFrame*>(pPrevFrame); - pPrevFrame = pPrevSectFrame->FindLastContent(); - // If the last content is in a table _inside_ the section, - // take the table herself. - // OD 2004-02-18 #106629# - correction: - // Check directly, if table is inside table, instead of indirectly - // by checking, if section isn't inside a table - if ( pPrevFrame && pPrevFrame->IsInTab() ) - { - const SwTabFrame* pTableFrame = pPrevFrame->FindTabFrame(); - if ( pPrevSectFrame->IsAnLower( pTableFrame ) ) - { - pPrevFrame = pTableFrame; - } - } - // OD 2004-02-18 #106629# correction: skip hidden text frames - while ( pPrevFrame && - pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) - { - pPrevFrame = pPrevFrame->GetPrev(); - } - } + const SwFootnoteFrame* pPrevFootnoteFrame = + static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev()); + if ( pPrevFootnoteFrame ) + return skipHiddenFrames_(pPrevFootnoteFrame->GetLastLower()); - return pPrevFrame; + return nullptr; } /// Compare styles attached to these text frames. @@ -1770,6 +1749,8 @@ SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell( /// Moves the Frame forward if it seems necessary regarding the current conditions and attributes. bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue ) { + if (m_rThis.IsHiddenNow()) + return false; const SwFrame* pNxt = m_rThis.GetIndNext(); if ( bKeep && //!bMovedBwd && @@ -2145,7 +2126,8 @@ bool SwFlowFrame::MoveBwd( bool &rbReformat ) ) pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false ); } - else if ( IsPageBreak( true ) ) // Do we have to respect a PageBreak? + // Do we have to respect a PageBreak? + else if (IsPageBreak(true) && (!m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsHiddenNow())) { // If the previous page doesn't have a Frame in the body, // flowing back makes sense despite the PageBreak (otherwise, @@ -2212,7 +2194,7 @@ bool SwFlowFrame::MoveBwd( bool &rbReformat ) } } } - else if ( IsColBreak( true ) ) + else if (IsColBreak(true)) { // If the previous column doesn't contain a ContentFrame, flowing back // makes sense despite the ColumnBreak, as otherwise we'd get diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index a861dd60013b..111623518f24 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -69,6 +69,7 @@ #include <undobj.hxx> #include <DocumentSettingManager.hxx> #include <IDocumentDrawModelAccess.hxx> +#include <IDocumentLayoutAccess.hxx> #include <IDocumentTimerAccess.hxx> #include <IDocumentRedlineAccess.hxx> #include <IDocumentFieldsAccess.hxx> @@ -782,11 +783,7 @@ SwContentNotify::~SwContentNotify() SwFrame* pPrevFrame = pCnt->FindPrev(); // skip empty section frames and hidden text frames { - while ( pPrevFrame && - ( ( pPrevFrame->IsSctFrame() && - !static_cast<SwSectionFrame*>(pPrevFrame)->GetSection() ) || - ( pPrevFrame->IsTextFrame() && - static_cast<SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ) ) + while (pPrevFrame && pPrevFrame->IsHiddenNow()) { pPrevFrame = pPrevFrame->FindPrev(); } @@ -1067,6 +1064,12 @@ static bool IsShown(sal_uLong const nIndex, assert(pFirstNode); assert(pLastNode); assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY); + if (*pIter == *pEnd && rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { // tdf#149595 special case - it *could* be shown if first == last + return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(), + SwPosition(const_cast<SwTextNode&>(*pFirstNode), 0), + SwPosition(const_cast<SwTextNode&>(*pLastNode), pLastNode->Len())); + } for (auto iter = *pIter; iter != *pEnd; ++iter) { assert(iter->nStart != iter->nEnd); // TODO possible? @@ -1161,8 +1164,7 @@ void RemoveHiddenObjsOfNode(SwTextNode const& rNode, { SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor(); if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR - || (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR - && RES_DRAWFRMFMT == pFrameFormat->Which())) + || rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR) { assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex()); if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode)) @@ -1547,10 +1549,20 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, pFrame = pNode->IsTextNode() ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode) : pNode->MakeFrame(pLay); - if( pPageMaker ) + if (pPageMaker && !pLay->IsHiddenNow()) pPageMaker->CheckInsert( nIndex ); pFrame->InsertBehind( pLay, pPrv ); + if (!pPrv) + { + if (SwSectionFrame *const pSection = pLay->FindSctFrame()) + { + if (pSection && pSection->ContainsAny() == pFrame) + { // tdf#146258 section PrtArea depends on paragraph upper margin + pSection->InvalidatePrt(); + } + } + } // #i27138# // notify accessibility paragraphs objects about changed // CONTENT_FLOWS_FROM/_TO relation. @@ -1682,12 +1694,11 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, continue; // skip it } SwSectionNode *pNode = static_cast<SwSectionNode*>(pNd); - if( pNode->GetSection().CalcHiddenFlag() ) - // is hidden, skip the area - nIndex = pNode->EndOfSectionIndex(); - else { - pFrame = pNode->MakeFrame( pLay ); + if (pActualSection) + pActualSection->SetLastPos(pPrv); + + pFrame = pNode->MakeFrame(pLay, pNode->GetSection().CalcHiddenFlag()); pActualSection.reset( new SwActualSection( pActualSection.release(), static_cast<SwSectionFrame*>(pFrame), pNode ) ); if ( pActualSection->GetUpper() ) @@ -1719,7 +1730,10 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, static_cast<SwTextFrame*>(pPrv)->Prepare( PREP_QUOVADIS, nullptr, false ); } } - if (nIndex + 1 == nEndIndex) + + if (nIndex + 1 == nEndIndex + // tdf#136452 may also be needed at end of section + || pNode->EndOfSectionIndex() - 1 == nEndIndex) { // tdf#131684 tdf#132236 fix upper of frame moved in // SwUndoDelete; can't be done there unfortunately // because empty section frames are deleted here @@ -1831,33 +1845,31 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, } // new section frame - pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay ); - pFrame->InsertBehind( pLay, pPrv ); - static_cast<SwSectionFrame*>(pFrame)->Init(); - - // OD 12.08.2003 #i17969# - consider horizontal/vertical layout - // for setting position at newly inserted frame - lcl_SetPos( *pFrame, *pLay ); - - SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame(); - - // a follow has to be appended to the new section frame - SwSectionFrame* pFollow = pOuterSectionFrame ? pOuterSectionFrame->GetFollow() : nullptr; - if ( pFollow ) + if (SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame()) { - pOuterSectionFrame->SetFollow( nullptr ); - pOuterSectionFrame->InvalidateSize(); - static_cast<SwSectionFrame*>(pFrame)->SetFollow( pFollow ); - } + // Splitting moves the trailing content to the next frame + pFrame = pOuterSectionFrame->SplitSect(pActualSection->GetLastPos(), pPrv); - // We don't want to leave empty parts back. - if (pOuterSectionFrame && - ! pOuterSectionFrame->IsColLocked() && - ! pOuterSectionFrame->ContainsContent() ) + // We don't want to leave empty parts back. + if (! pOuterSectionFrame->IsColLocked() && + ! pOuterSectionFrame->ContainsContent() ) + { + pOuterSectionFrame->DelEmpty( true ); + SwFrame::DestroyFrame(pOuterSectionFrame); + } + } + else { - pOuterSectionFrame->DelEmpty( true ); - SwFrame::DestroyFrame(pOuterSectionFrame); + pFrame = pActualSection->GetSectionNode()->MakeFrame( + pLay, pActualSection->GetSectionNode()->GetSection().IsHiddenFlag()); + pFrame->InsertBehind( pLay, pPrv ); + static_cast<SwSectionFrame*>(pFrame)->Init(); + + // OD 12.08.2003 #i17969# - consider horizontal/vertical layout + // for setting position at newly inserted frame + lcl_SetPos( *pFrame, *pLay ); } + pActualSection->SetSectionFrame( static_cast<SwSectionFrame*>(pFrame) ); pLay = static_cast<SwLayoutFrame*>(pFrame); @@ -1941,8 +1953,10 @@ void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx, SwNodeIndex aTmp( rSttIdx ); sal_uLong nEndIdx = rEndIdx.GetIndex(); + // TODO for multiple layouts there should be a loop here SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp, - pDoc->GetNodes()[ nEndIdx-1 ]); + pDoc->GetNodes()[ nEndIdx-1 ], + pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); if ( pNd ) { bool bApres = aTmp < rSttIdx; @@ -2080,20 +2094,7 @@ void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx, } else { - bool bSplit; SwFrame* pPrv = bApres ? pFrame : pFrame->GetPrev(); - // If the section frame is inserted into another one, it must be split. - if( pSct && rSttIdx.GetNode().IsSectionNode() ) - { - bSplit = pSct->SplitSect( pFrame, bApres ); - if( !bSplit && !bApres ) - { - pUpper = pSct->GetUpper(); - pPrv = pSct->GetPrev(); - } - } - else - bSplit = false; ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), false, nEndIdx, pPrv, eMode ); @@ -2106,10 +2107,6 @@ void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx, AppendAllObjs( pTable, pUpper ); } - // If nothing was added (e.g. a hidden section), the split must be reversed. - if( bSplit && pSct && pSct->GetNext() - && pSct->GetNext()->IsSctFrame() ) - pSct->MergeNext( static_cast<SwSectionFrame*>(pSct->GetNext()) ); if( pFrame->IsInFly() ) pFrame->FindFlyFrame()->Invalidate_(); if( pFrame->IsInTab() ) @@ -2415,8 +2412,7 @@ void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame, // one as previous frame. const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev(); // OD 2004-02-13 #i25029# - skip hidden text frames. - while ( pPrevFrame && pPrevFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) + while (pPrevFrame && pPrevFrame->IsHiddenNow()) { pPrevFrame = pPrevFrame->GetPrev(); } @@ -2447,8 +2443,7 @@ void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame ) // corresponding attribute set is set at current text frame. // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames. const SwFrame* pNextFrame = _rFrame.GetNext(); - while ( pNextFrame && pNextFrame->IsTextFrame() && - static_cast<const SwTextFrame*>(pNextFrame)->IsHiddenNow() ) + while (pNextFrame && pNextFrame->IsHiddenNow()) { pNextFrame = pNextFrame->GetNext(); } diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx index df8a2e54a6f9..9498cd1e13a6 100644 --- a/sw/source/core/layout/ftnfrm.cxx +++ b/sw/source/core/layout/ftnfrm.cxx @@ -2955,13 +2955,9 @@ SwContentFrame* SwFootnoteFrame::FindLastContent() while ( pTmpLastLower && pTmpLastLower->GetNext() ) { pTmpLastLower = pTmpLastLower->GetNext(); - if ( ( pTmpLastLower->IsTextFrame() && - !static_cast<SwTextFrame*>(pTmpLastLower)->IsHiddenNow() ) || - ( pTmpLastLower->IsSctFrame() && - static_cast<SwSectionFrame*>(pTmpLastLower)->GetSection() && - static_cast<SwSectionFrame*>(pTmpLastLower)->ContainsContent() ) || - ( pTmpLastLower->IsTabFrame() && - static_cast<SwTabFrame*>(pTmpLastLower)->ContainsContent() ) ) + if (!pTmpLastLower->IsHiddenNow() + && (!pTmpLastLower->IsLayoutFrame() + || static_cast<SwLayoutFrame*>(pTmpLastLower)->ContainsContent())) { pLastLowerOfFootnote = pTmpLastLower; } diff --git a/sw/source/core/layout/layhelp.hxx b/sw/source/core/layout/layhelp.hxx index 9dc5a916b25f..6dfa4b507639 100644 --- a/sw/source/core/layout/layhelp.hxx +++ b/sw/source/core/layout/layhelp.hxx @@ -85,6 +85,7 @@ class SwActualSection { SwActualSection *pUpper; SwSectionFrame *pSectFrame; + SwFrame* m_pLastPos = nullptr; // Split it *after* this child frame SwSectionNode *pSectNode; public: SwActualSection( SwActualSection *pUpper, @@ -96,6 +97,8 @@ public: SwSectionNode *GetSectionNode() { return pSectNode;} void SetUpper(SwActualSection *p) { pUpper = p; } SwActualSection *GetUpper() { return pUpper; } + void SetLastPos(SwFrame* p) { m_pLastPos = p; } + SwFrame* GetLastPos() const { return m_pLastPos; } }; /// Helps during the InsertCnt_ function to create new pages. diff --git a/sw/source/core/layout/newfrm.cxx b/sw/source/core/layout/newfrm.cxx index 1d1f4597fcf2..2b3c18f981b0 100644 --- a/sw/source/core/layout/newfrm.cxx +++ b/sw/source/core/layout/newfrm.cxx @@ -500,6 +500,11 @@ void SwRootFrame::Init( SwFrameFormat* pFormat ) SwNodeIndex aTmp( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode(), 1 ); ::InsertCnt_( pLay, pDoc, aTmp.GetIndex(), true ); + + // tdf#156077 create all pages for at-page anchored flys now because all + // these flys must be attached to some page when Init() is finished + AssertFlyPages(); + //Remove masters that haven't been replaced yet from the list. RemoveMasterObjs( mpDrawPage ); if( rSettingAccess.get(DocumentSettingId::GLOBAL_DOCUMENT) ) diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx index a650179660ac..8056c85c2d4c 100644 --- a/sw/source/core/layout/pagechg.cxx +++ b/sw/source/core/layout/pagechg.cxx @@ -53,6 +53,7 @@ #include <tabfrm.hxx> #include <txtfrm.hxx> #include <notxtfrm.hxx> +#include <sectfrm.hxx> #include <layact.hxx> #include <flyfrms.hxx> #include <htmltbl.hxx> @@ -775,7 +776,13 @@ SwPageDesc *SwPageFrame::FindPageDesc() return pRet; } - SwFrame *pFlow = FindFirstBodyContent(); + SwContentFrame* pFirstContent = FindFirstBodyContent(); + while (pFirstContent && pFirstContent->IsInSct() + && pFirstContent->FindSctFrame()->IsHiddenNow()) + { + pFirstContent = pFirstContent->GetNextContentFrame(); + } + SwFrame* pFlow = pFirstContent; if ( pFlow && pFlow->IsInTab() ) pFlow = pFlow->FindTabFrame(); diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx index 48debbcc399a..4f4e36ca5711 100644 --- a/sw/source/core/layout/sectfrm.cxx +++ b/sw/source/core/layout/sectfrm.cxx @@ -182,6 +182,13 @@ SwSectionFrame::~SwSectionFrame() { } +//virtual +bool SwSectionFrame::IsHiddenNow() const +{ + const auto* pSection = GetSection(); + return !pSection || pSection->CalcHiddenFlag(); +} + void SwSectionFrame::DelEmpty( bool bRemove ) { if( IsColLocked() ) @@ -498,50 +505,53 @@ void SwSectionFrame::MergeNext( SwSectionFrame* pNxt ) } /** -|* Divides a SectionFrame into two parts. The second one starts with the -|* passed frame. +|* Divides a SectionFrame into two parts. The content of the second one +|* starts after pFrameStartAfter; the created second section frame itself +|* is put after pFramePutAfter. +|* If pFrameStartAfter is nullptr, the split happens at the start. |* This is required when inserting an inner section, because the MoveFwd |* cannot have the desired effect within a frame or a table cell. +|* Splitting at the start/end makes sense, because the empty frame would +|* be removed after the InsertCnt_ finished. |*/ -bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bApres ) +SwSectionFrame* SwSectionFrame::SplitSect( SwFrame* pFrameStartAfter, SwFrame* pFramePutAfter ) { - assert(pFrame && "SplitSect: Why?"); - SwFrame* pOther = bApres ? pFrame->FindNext() : pFrame->FindPrev(); - if( !pOther ) - return false; - SwSectionFrame* pSect = pOther->FindSctFrame(); - if( pSect != this ) - return false; + assert(!pFrameStartAfter || pFrameStartAfter->FindSctFrame() == this); + SwFrame* pSav = pFrameStartAfter ? pFrameStartAfter->FindNext() : ContainsAny(); + if (pSav && pSav->FindSctFrame() != this) + pSav = nullptr; // we are at the very end + // Put the content aside - SwFrame* pSav = ::SaveContent( this, bApres ? pOther : pFrame ); - OSL_ENSURE( pSav, "SplitSect: What's on?" ); - if( pSav ) // be robust - { // Create a new SctFrame, not as a Follower/master - SwSectionFrame* pNew = new SwSectionFrame( *pSect->GetSection(), pSect ); - pNew->InsertBehind( pSect->GetUpper(), pSect ); - pNew->Init(); - SwRectFnSet aRectFnSet(this); - aRectFnSet.MakePos( *pNew, nullptr, pSect, true ); - // OD 25.03.2003 #108339# - restore content: - // determine layout frame for restoring content after the initialization - // of the section frame. In the section initialization the columns are - // created. - { - SwLayoutFrame* pLay = pNew; - // Search for last layout frame, e.g. for columned sections. - while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() ) - pLay = static_cast<SwLayoutFrame*>(pLay->Lower()); - ::RestoreContent( pSav, pLay, nullptr ); - } - InvalidateSize_(); - if( HasFollow() ) - { - pNew->SetFollow( GetFollow() ); - SetFollow( nullptr ); - } - return true; + if (pSav) + pSav = ::SaveContent( this, pSav ); + + // Create a new SctFrame, not as a Follower/master + if (!pFramePutAfter) + pFramePutAfter = this; + SwSectionFrame* pNew = new SwSectionFrame( *GetSection(), this ); + pNew->InsertBehind( pFramePutAfter->GetUpper(), pFramePutAfter ); + pNew->Init(); + SwRectFnSet aRectFnSet(this); + aRectFnSet.MakePos( *pNew, nullptr, pFramePutAfter, true ); + // OD 25.03.2003 #108339# - restore content: + // determine layout frame for restoring content after the initialization + // of the section frame. In the section initialization the columns are + // created. + if (pSav) + { + SwLayoutFrame* pLay = pNew; + // Search for last layout frame, e.g. for columned sections. + while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() ) + pLay = static_cast<SwLayoutFrame*>(pLay->Lower()); + ::RestoreContent( pSav, pLay, nullptr ); + } + InvalidateSize_(); + if( HasFollow() ) + { + pNew->SetFollow( GetFollow() ); + SetFollow( nullptr ); } - return false; + return pNew; } /** @@ -1338,6 +1348,20 @@ void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderA SwRectFnSet aRectFnSet(this); + if (GetSection()->CalcHiddenFlag()) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.SetHeight(aFrm, 0); + } + { + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aRectFnSet.SetHeight(aPrt, 0); + } + setFrameAreaSizeValid(true); + setFramePrintAreaValid(true); + } + if ( !isFramePrintAreaValid() ) { PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr ) @@ -2145,6 +2169,11 @@ bool SwSectionFrame::Growable() const SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst ) { + if (GetSection()->CalcHiddenFlag()) + { + return 0; + } + if ( !IsColLocked() && !HasFixSize() ) { SwRectFnSet aRectFnSet(this); @@ -2582,6 +2611,18 @@ void SwSectionFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) if ( aOldSet.Count() || aNewSet.Count() ) SwLayoutFrame::Modify( &aOldSet, &aNewSet ); } + else if (pNew && (RES_SECTION_HIDDEN == pNew->Which() + || RES_SECTION_NOT_HIDDEN == pNew->Which())) + { + InvalidateAll(); + InvalidateObjs(false); + + for (SwFrame* pLowerFrame = Lower(); pLowerFrame; pLowerFrame = pLowerFrame->GetNext()) + { + pLowerFrame->InvalidateAll(); + pLowerFrame->InvalidateObjs(false); + } + } else UpdateAttr_( pOld, pNew, nInvFlags ); diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 59ab746ae9ec..dd0d6bbc9dd5 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1359,7 +1359,104 @@ namespace return true; } -} + + auto IsAllHiddenSection(SwSectionFrame const& rSection) -> bool + { + if (rSection.IsHiddenNow()) + return true; + for (SwFrame const* pFrame = rSection.Lower(); pFrame; pFrame = pFrame->GetNext()) + { + if (pFrame->IsColumnFrame()) + { + return false; // adds some padding + } + else if (pFrame->IsSctFrame()) + { + assert(false); // these aren't nested? + if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame))) + { + return false; + } + } + else if (pFrame->IsTabFrame()) + { + return false; // presumably + } + else if (pFrame->IsTextFrame()) + { + if (!pFrame->IsHiddenNow()) + { + return false; + } + } + } + return true; + } + + auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool; + + auto IsAllHiddenCell(SwCellFrame const& rCell, SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool + { + for (SwFrame const* pFrame = rCell.Lower(); pFrame; pFrame = pFrame->GetNext()) + { + if (pFrame->IsRowFrame()) + { + if (!IsAllHiddenRow(*static_cast<SwRowFrame const*>(pFrame), rTab)) + { + return false; + } + } + else if (pFrame->IsSctFrame()) + { + if (!IsAllHiddenSection(*static_cast<SwSectionFrame const*>(pFrame))) + { + return false; + } + } + else if (pFrame->IsTabFrame()) + { + return false; // presumably + } + else if (pFrame->IsTextFrame()) + { + if (!pFrame->IsHiddenNow()) + { + return false; + } + } + } + if (rTab.IsCollapsingBorders() && !rCell.Lower()->IsRowFrame()) + { + if (rRow.GetTopMarginForLowers() != 0 + || rRow.GetBottomMarginForLowers() != 0) + { + return false; + } + } + else + { + SwBorderAttrAccess border(SwFrame::GetCache(), &rCell); + if (border.Get()->CalcTop() != 0 || border.Get()->CalcBottom() != 0) + { + return false; + } + } + return true; + } + + auto IsAllHiddenRow(SwRowFrame const& rRow, SwTabFrame const& rTab) -> bool + { + for (SwFrame const* pCell = rRow.Lower(); pCell; pCell = pCell->GetNext()) + { + if (!IsAllHiddenCell(*static_cast<SwCellFrame const*>(pCell), rRow, rTab)) + { + return false; + } + } + return true; + } + +} // namespace void SwTabFrame::Join() { @@ -1378,11 +1475,20 @@ void SwTabFrame::Join() SwFrame* pPrv = GetLastLower(); SwTwips nHeight = 0; //Total height of the inserted rows as return value. + bool isAllHidden(true); while ( pRow ) { pNxt = pRow->GetNext(); nHeight += aRectFnSet.GetHeight(pRow->getFrameArea()); + if (nHeight != 0) + { + isAllHidden = false; + } + if (isAllHidden) + { + isAllHidden = IsAllHiddenRow(*static_cast<SwRowFrame *>(pRow), *this); + } pRow->RemoveFromLayout(); pRow->InvalidateAll_(); pRow->InsertBehind( this, pPrv ); @@ -1396,6 +1502,18 @@ void SwTabFrame::Join() SwFrame::DestroyFrame(pFoll); Grow( nHeight ); + + // In case the row does not have a height, Grow(nHeight) did nothing. + // If this is not invalidated, subsequent follows may never be joined. + // Try to guess if the height of the row will be 0. If the document + // was just loaded, it will be 0 in any case, but probably it's not a good + // idea to join *all* follows for a newly loaded document, it would be + // easier not to split the table in the first place; presumably it is split + // because that improves performance. + if (isAllHidden) + { + InvalidateSize_(); + } } } @@ -5524,9 +5642,12 @@ static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine ) const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower()); nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow ); } - else if ( pTmp->IsTabFrame() ) + else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame())) { - nTmpHeight = static_cast<const SwTabFrame*>(pTmp)->CalcHeightOfFirstContentLine(); + SwTabFrame const*const pTabFrame(pTmp->IsTabFrame() + ? static_cast<SwTabFrame const*>(pTmp) + : static_cast<SwTabFrame const*>(pTmp->GetLower())); + nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine(); } else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame())) { diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx index 9079d0a9cbab..3dd5cc223038 100644 --- a/sw/source/core/layout/trvlfrm.cxx +++ b/sw/source/core/layout/trvlfrm.cxx @@ -49,6 +49,7 @@ #include <frmtool.hxx> #include <ndtxt.hxx> #include <undobj.hxx> +#include <sectfrm.hxx> #include <cfloat> #include <swselectionlist.hxx> @@ -801,8 +802,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //If I'm in the DocumentBody, I want to stay there. if ( pStart->IsInDocBody() ) { - while ( pCnt && (!pCnt->IsInDocBody() || - (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()))) + while (pCnt && (!pCnt->IsInDocBody() || pCnt->IsHiddenNow())) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -813,8 +813,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //case of necessity. else if ( pStart->IsInFootnote() ) { - while ( pCnt && (!pCnt->IsInFootnote() || - (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()))) + while (pCnt && (!pCnt->IsInFootnote() || pCnt->IsHiddenNow())) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -824,7 +823,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, //In Flys we can go ahead blindly as long as we find a Content. else if ( pStart->IsInFly() ) { - if ( pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow() ) + if (pCnt && pCnt->IsHiddenNow()) { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -848,7 +847,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, } if ( !bSame ) pCnt = nullptr; - else if (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()) // i73332 + else if (pCnt->IsHiddenNow()) // i73332 { pCnt = (*fnNxtPrv)( pCnt ); pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel ); @@ -931,8 +930,7 @@ static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart, } } - } while ( !bEnd || - (pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())); + } while (!bEnd || (pCnt && pCnt->IsHiddenNow())); if (pCnt == nullptr) { @@ -1219,7 +1217,7 @@ const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint, if ( pComp != pContent ) continue; - if ( !pContent->IsTextFrame() || !static_cast<const SwTextFrame*>(pContent)->IsHiddenNow() ) + if (!pContent->IsHiddenNow()) { SwRect aContentFrame( pContent->UnionFrame() ); if ( aContentFrame.IsInside( rPoint ) ) @@ -1686,6 +1684,15 @@ bool SwFrame::IsProtected() const return false; } +// virtual +bool SwFrame::IsHiddenNow() const +{ + if (const auto* pSectFrame = FindSctFrame()) + return pSectFrame->IsHiddenNow(); + + return false; +} + /** @return the physical page number */ sal_uInt16 SwFrame::GetPhyPageNum() const { diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index c1e85421cce3..d8875dbc89cd 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -3973,6 +3973,18 @@ void SwLayoutFrame::FormatWidthCols( const SwBorderAttrs &rAttrs, } } +void SwLayoutFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const +{ + SwFrame::dumpAsXmlAttributes(writer); + + const SwFrameFormat* pFormat = GetFormat(); + if (pFormat) + { + (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "format" ), "%p", pFormat); + (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "formatName" ), "%s", BAD_CAST(pFormat->GetName().toUtf8().getStr())); + } +} + static SwContentFrame* lcl_InvalidateSection( SwFrame *pCnt, SwInvalidateFlags nInv ) { SwSectionFrame* pSect = pCnt->FindSctFrame(); diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx index 9000d6ef1b9e..5094b9f777a1 100644 --- a/sw/source/core/ole/ndole.cxx +++ b/sw/source/core/ole/ndole.cxx @@ -147,6 +147,8 @@ void SAL_CALL SwOLEListener_Impl::disposing( const lang::EventObject& ) // TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control // embedded object different link objects with the same functionality had to be implemented +namespace { + class SwEmbedObjectLink : public sfx2::SvBaseLink { SwOLENode* pOleNode; @@ -209,6 +211,44 @@ void SwEmbedObjectLink::Closed() SvBaseLink::Closed(); } +class SwIFrameLink : public sfx2::SvBaseLink +{ + SwOLENode* m_pOleNode; + +public: + explicit SwIFrameLink(SwOLENode* pNode) + : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB) + , m_pOleNode(pNode) + { + SetSynchron( false ); + } + + ::sfx2::SvBaseLink::UpdateResult DataChanged( + const OUString&, const uno::Any& ) + { + uno::Reference<embed::XEmbeddedObject> xObject = m_pOleNode->GetOLEObj().GetOleRef(); + uno::Reference<embed::XCommonEmbedPersist> xPersObj(xObject, uno::UNO_QUERY); + if (xPersObj.is()) + { + // let the IFrameObject reload the link + try + { + xPersObj->reload(uno::Sequence<beans::PropertyValue>(), uno::Sequence<beans::PropertyValue>()); + } + catch (const uno::Exception&) + { + } + + m_pOleNode->SetChanged(); + } + + return SUCCESS; + } + +}; + +} + SwOLENode::SwOLENode( const SwNodeIndex &rWhere, const svt::EmbeddedObjectRef& xObj, SwGrfFormatColl *pGrfColl, @@ -572,22 +612,22 @@ void SwOLENode::BreakFileLink_Impl() { SfxObjectShell* pPers = GetDoc()->GetPersist(); - if ( pPers ) + if ( !pPers ) + return; + + uno::Reference< embed::XStorage > xStorage = pPers->GetStorage(); + if ( !xStorage.is() ) + return; + + try + { + uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.GetOleRef(), uno::UNO_QUERY_THROW ); + xLinkSupport->breakLink( xStorage, maOLEObj.GetCurrentPersistName() ); + DisconnectFileLink_Impl(); + maLinkURL.clear(); + } + catch( uno::Exception& ) { - uno::Reference< embed::XStorage > xStorage = pPers->GetStorage(); - if ( xStorage.is() ) - { - try - { - uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.GetOleRef(), uno::UNO_QUERY_THROW ); - xLinkSupport->breakLink( xStorage, maOLEObj.GetCurrentPersistName() ); - DisconnectFileLink_Impl(); - maLinkURL.clear(); - } - catch( uno::Exception& ) - { - } - } } } @@ -602,28 +642,59 @@ void SwOLENode::DisconnectFileLink_Impl() void SwOLENode::CheckFileLink_Impl() { - if ( maOLEObj.m_xOLERef.GetObject().is() && !mpObjectLink ) + if ( !(maOLEObj.m_xOLERef.GetObject().is() && !mpObjectLink) ) + return; + + try { - try + uno::Reference<embed::XEmbeddedObject> xObject = maOLEObj.m_xOLERef.GetObject(); + if (!xObject) + return; + + bool bIFrame = false; + + OUString aLinkURL; + uno::Reference<embed::XLinkageSupport> xLinkSupport(xObject, uno::UNO_QUERY); + if (xLinkSupport) + { + if (xLinkSupport->isLink()) + aLinkURL = xLinkSupport->getLinkURL(); + } + else { - uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY_THROW ); - if ( xLinkSupport->isLink() ) + // get IFrame (Floating Frames) listed and updatable from the + // manage links dialog + SvGlobalName aClassId(xObject->getClassID()); + if (aClassId == SvGlobalName(SO3_IFRAME_CLASSID)) { - const OUString aLinkURL = xLinkSupport->getLinkURL(); - if ( !aLinkURL.isEmpty() ) - { - // this is a file link so the model link manager should handle it - mpObjectLink = new SwEmbedObjectLink( this ); - maLinkURL = aLinkURL; - GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink, OBJECT_CLIENT_OLE, aLinkURL ); - mpObjectLink->Connect(); - } + uno::Reference<beans::XPropertySet> xSet(xObject->getComponent(), uno::UNO_QUERY); + if (xSet.is()) + xSet->getPropertyValue("FrameURL") >>= aLinkURL; + bIFrame = true; } } - catch( uno::Exception& ) + + if (!aLinkURL.isEmpty()) // this is a file link so the model link manager should handle it { + SwEmbedObjectLink* pEmbedObjectLink = nullptr; + if (!bIFrame) + { + pEmbedObjectLink = new SwEmbedObjectLink(this); + mpObjectLink = pEmbedObjectLink; + } + else + { + mpObjectLink = new SwIFrameLink(this); + } + maLinkURL = aLinkURL; + GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink, OBJECT_CLIENT_OLE, aLinkURL ); + if (pEmbedObjectLink) + pEmbedObjectLink->Connect(); } } + catch( uno::Exception& ) + { + } } // #i99665# @@ -862,39 +933,39 @@ SwOLEObj::~SwOLEObj() COVERITY_NOEXCEPT_FALSE void SwOLEObj::SetNode( SwOLENode* pNode ) { m_pOLENode = pNode; - if ( m_aName.isEmpty() ) - { - SwDoc* pDoc = pNode->GetDoc(); - - // If there's already a SvPersist instance, we use it - SfxObjectShell* p = pDoc->GetPersist(); - if( !p ) - { - // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here? - // What happens to the document? - OSL_ENSURE( false, "Why are we creating a DocShell here??" ); - p = new SwDocShell( pDoc, SfxObjectCreateMode::INTERNAL ); - p->DoInitNew(); - } + if ( !m_aName.isEmpty() ) + return; - OUString aObjName; - uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY ); - if ( xChild.is() && xChild->getParent() != p->GetModel() ) - // it is possible that the parent was set already - xChild->setParent( p->GetModel() ); - if (!p->GetEmbeddedObjectContainer().InsertEmbeddedObject( m_xOLERef.GetObject(), aObjName ) ) - { - OSL_FAIL( "InsertObject failed" ); - if ( xChild.is() ) - xChild->setParent( nullptr ); - } - else - m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName ); + SwDoc* pDoc = pNode->GetDoc(); - const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required + // If there's already a SvPersist instance, we use it + SfxObjectShell* p = pDoc->GetPersist(); + if( !p ) + { + // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here? + // What happens to the document? + OSL_ENSURE( false, "Why are we creating a DocShell here??" ); + p = new SwDocShell( pDoc, SfxObjectCreateMode::INTERNAL ); + p->DoInitNew(); + } - m_aName = aObjName; + OUString aObjName; + uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY ); + if ( xChild.is() && xChild->getParent() != p->GetModel() ) + // it is possible that the parent was set already + xChild->setParent( p->GetModel() ); + if (!p->GetEmbeddedObjectContainer().InsertEmbeddedObject( m_xOLERef.GetObject(), aObjName ) ) + { + OSL_FAIL( "InsertObject failed" ); + if ( xChild.is() ) + xChild->setParent( nullptr ); } + else + m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName ); + + const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required + + m_aName = aObjName; } OUString SwOLEObj::GetStyleString() @@ -980,6 +1051,18 @@ bool SwOLEObj::UnloadObject() return bRet; } +PurgeGuard::PurgeGuard(const SwDoc& rDoc) + : m_rManager(const_cast<SwDoc&>(rDoc).GetDocumentSettingManager()) + , m_bOrigPurgeOle(m_rManager.get(DocumentSettingId::PURGE_OLE)) +{ + m_rManager.set(DocumentSettingId::PURGE_OLE, false); +} + +PurgeGuard::~PurgeGuard() +{ + m_rManager.set(DocumentSettingId::PURGE_OLE, m_bOrigPurgeOle); +} + bool SwOLEObj::UnloadObject( uno::Reference< embed::XEmbeddedObject > const & xObj, const SwDoc* pDoc, sal_Int64 nAspect ) { if ( !pDoc ) @@ -1006,6 +1089,8 @@ bool SwOLEObj::UnloadObject( uno::Reference< embed::XEmbeddedObject > const & xO { uno::Reference < embed::XEmbedPersist > xPers( xObj, uno::UNO_QUERY ); assert(xPers.is() && "Modified object without persistence in cache!"); + + PurgeGuard aGuard(*pDoc); xPers->storeOwn(); } @@ -1167,7 +1252,7 @@ void SwOLELRUCache::Load() if (nVal < m_nLRU_InitSize) { - std::shared_ptr<SwOLELRUCache> tmp(g_pOLELRU_Cache); // prevent delete this + std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this // size of cache has been changed sal_Int32 nCount = m_OleObjects.size(); sal_Int32 nPos = nCount; @@ -1197,20 +1282,20 @@ void SwOLELRUCache::InsertObj( SwOLEObj& rObj ) m_OleObjects.erase(it); it = m_OleObjects.end(); } - if (it == m_OleObjects.end()) + if (it != m_OleObjects.end()) + return; + + std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this + // try to remove objects if necessary + sal_Int32 nCount = m_OleObjects.size(); + sal_Int32 nPos = nCount-1; + while (nPos >= 0 && nCount >= m_nLRU_InitSize) { - std::shared_ptr<SwOLELRUCache> tmp(g_pOLELRU_Cache); // prevent delete this - // try to remove objects if necessary - sal_Int32 nCount = m_OleObjects.size(); - sal_Int32 nPos = nCount-1; - while (nPos >= 0 && nCount >= m_nLRU_InitSize) - { - pObj = m_OleObjects[ nPos-- ]; - if ( pObj->UnloadObject() ) - nCount--; - } - m_OleObjects.push_front(&rObj); + pObj = m_OleObjects[ nPos-- ]; + if ( pObj->UnloadObject() ) + nCount--; } + m_OleObjects.push_front(&rObj); } void SwOLELRUCache::RemoveObj( SwOLEObj& rObj ) diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx index 7eae6a55198e..07c8944d0a03 100644 --- a/sw/source/core/text/frmcrsr.cxx +++ b/sw/source/core/text/frmcrsr.cxx @@ -181,7 +181,7 @@ bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos, { OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::GetCharRect with swapped frame" ); - if( IsLocked() || IsHiddenNow() ) + if (IsLocked()) return false; // Find the right frame first. We need to keep in mind that: diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx index 5ef68ca3010d..7a1b0ed7e3ff 100755 --- a/sw/source/core/text/frmform.cxx +++ b/sw/source/core/text/frmform.cxx @@ -1599,9 +1599,27 @@ void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf, // If we're finished formatting the text and we still // have other line objects left, these are superfluous // now because the text has gotten shorter. + bool bTruncLines = false; if( rLine.GetStart() + rLine.GetLength() >= nStrLen && rLine.GetCurr()->GetNext() ) { + bTruncLines = true; + } + else if (GetMergedPara() && rLine.GetCurr()->GetNext()) + { + // We can also have superfluous lines with redlining in case the current line is shorter + // than the text length, but the total length of lines is still more than expected. + // Truncate in this case as well. + TextFrameIndex nLen(0); + for (const SwLineLayout* pLine = pPara; pLine; pLine = pLine->GetNext()) + { + nLen += pLine->GetLen(); + } + bTruncLines = nLen > nStrLen; + } + + if (bTruncLines) + { rLine.TruncLines(); rLine.SetTruncLines( true ); } diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx index 5872e5688ba1..ad3acbafefa4 100644 --- a/sw/source/core/text/porfld.cxx +++ b/sw/source/core/text/porfld.cxx @@ -1069,6 +1069,9 @@ void SwTextFrame::StopAnimation( OutputDevice* pOut ) */ SwCombinedPortion::SwCombinedPortion( const OUString &rText ) : SwFieldPortion( rText ) + , aWidth{ static_cast<sal_uInt16>(0), + static_cast<sal_uInt16>(0), + static_cast<sal_uInt16>(0) } , nUpPos(0) , nLowPos(0) , nProportion(55) diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx index c8d7b7942ac5..7678298495d7 100644 --- a/sw/source/core/text/porfld.hxx +++ b/sw/source/core/text/porfld.hxx @@ -199,7 +199,7 @@ public: class SwCombinedPortion : public SwFieldPortion { sal_uInt16 aPos[6]; // up to six X positions - o3tl::enumarray<SwFontScript,sal_uInt16> aWidth = {}; // one width for every scripttype + o3tl::enumarray<SwFontScript,sal_uInt16> aWidth; // one width for every scripttype SwFontScript aScrType[6]; // scripttype of every character sal_uInt16 nUpPos; // the Y position of the upper baseline sal_uInt16 nLowPos; // the Y position of the lower baseline diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index 13453253a837..a4287066c828 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -1016,7 +1016,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode, { const Range& rRange = aHiddenMulti.GetRange( i ); const sal_Int32 nStart = rRange.Min(); - const sal_Int32 nEnd = rRange.Max() + 1; + const sal_Int32 nEnd = rRange.Max() + (rText.isEmpty() ? 0 : 1); m_HiddenChg.push_back( TextFrameIndex(nStart) ); m_HiddenChg.push_back( TextFrameIndex(nEnd) ); diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx index 3bd45fee2180..9ef4c25ccf85 100644 --- a/sw/source/core/text/pormulti.cxx +++ b/sw/source/core/text/pormulti.cxx @@ -2357,6 +2357,11 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf, return bRet; } +static bool IsIncompleteRuby(const SwMultiPortion& rHelpMulti) +{ + return rHelpMulti.IsRuby() && static_cast<const SwRubyPortion&>(rHelpMulti).GetRubyOffset() < TextFrameIndex(COMPLETE_STRING); +} + // When a fieldportion at the end of line breaks and needs a following // fieldportion in the next line, then the "restportion" of the formatinfo // has to be set. Normally this happens during the formatting of the first @@ -2465,19 +2470,19 @@ SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine, if (!pCreate) return pRest; - if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() && - static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset() < TextFrameIndex(COMPLETE_STRING))) + if( pRest || nMultiPos > nPosition || IsIncompleteRuby(*pHelpMulti)) { SwMultiPortion* pTmp; if( pHelpMulti->IsDouble() ) pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos ); else if( pHelpMulti->IsBidi() ) pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel ); - else if( pHelpMulti->IsRuby() ) + else if (IsIncompleteRuby(*pHelpMulti) && pCreate->pAttr) { + TextFrameIndex nRubyOffset = static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(); pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(), m_pFrame->GetDoc().getIDocumentSettingAccess(), - nMultiPos, static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(), + nMultiPos, nRubyOffset, GetInfo() ); } else if( pHelpMulti->HasRotation() ) diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index b8d450779ee7..676210c0c800 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -1373,6 +1373,9 @@ bool SwTextFrame::IsHiddenNow() const return true; } + if (SwContentFrame::IsHiddenNow()) + return true; + bool bHiddenCharsHidePara(false); bool bHiddenParaField(false); if (m_pMergedPara) @@ -1438,6 +1441,19 @@ bool SwTextFrame::IsHiddenNow() const ( bHiddenCharsHidePara && !pVsh->GetViewOptions()->IsShowHiddenChar() ) ) { + // in order to put the cursor in the body text, one paragraph must + // be visible - check this for the 1st body paragraph + if (IsInDocBody() && FindPrevCnt() == nullptr) + { + for (SwContentFrame const* pNext = FindNextCnt(true); + pNext != nullptr; pNext = pNext->FindNextCnt(true)) + { + if (!pNext->IsHiddenNow()) + return true; + } + SAL_INFO("sw.core", "unhiding one body paragraph"); + return false; + } return true; } } @@ -1922,7 +1938,6 @@ void UpdateMergedParaForMove(sw::MergedPara & rMerged, } if (nLastEnd != rNode.Len()) // without nLen, string yet to be removed { - assert(rNode.Len() == 0 || nLastEnd < nSourceEnd); if (nLastEnd < nSourceEnd) { deleted.emplace_back(std::max(nLastEnd, nSourceStart), nSourceEnd); diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx index df0c779d6dbb..43afbd1e45e6 100644 --- a/sw/source/core/text/widorp.cxx +++ b/sw/source/core/text/widorp.cxx @@ -138,6 +138,8 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const bFit = nDiff >= 0; if (!bFit && rLine.MaybeHasHints() && m_pFrame->GetFollow() + // tdf#153319 RemoveFootnote only works if this frame doesn't + && !rLine.GetNext() // contain the footnote portion // if using same footnote container as the follow, pointless to try? && m_pFrame->FindFootnoteBossFrame() != m_pFrame->GetFollow()->FindFootnoteBossFrame()) { diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx index 3a93b1f25121..08f9b83bb57b 100644 --- a/sw/source/core/text/xmldump.cxx +++ b/sw/source/core/text/xmldump.cxx @@ -488,13 +488,6 @@ void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const } xmlTextWriterWriteAttribute(writer, BAD_CAST("WritingMode"), BAD_CAST(aMode.getStr())); } - if (IsHeaderFrame() || IsFooterFrame()) - { - const SwHeadFootFrame *pHeadFootFrame = static_cast<const SwHeadFootFrame*>(this); - OUString aFormatName = pHeadFootFrame->GetFormat()->GetName(); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtName" ), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr())); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtPtr" ), "%p", pHeadFootFrame->GetFormat()); - } } void SwFrame::dumpChildrenAsXml( xmlTextWriterPtr writer ) const @@ -549,7 +542,7 @@ void SwTextFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const void SwSectionFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const { - SwFrame::dumpAsXmlAttributes( writer ); + SwLayoutFrame::dumpAsXmlAttributes( writer ); if ( HasFollow() ) xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() ); diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index 3d721ebee00b..a1f17358efa5 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -64,6 +64,7 @@ #include <txtfrm.hxx> #include <ftnfrm.hxx> #include <ftnboss.hxx> +#include <pagefrm.hxx> #include <rootfrm.hxx> #include <pagedesc.hxx> #include <expfld.hxx> @@ -456,9 +457,7 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, { // optimization for SplitNode: If a split is at the end of a node then // move the frames from the current to the new one and create new ones - // for the current one. As a result, no need for recreating the layout. - - LockModify(); // disable notifications + // for the current one. // If fly frames are moved, they don't need to destroy their layout // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor. @@ -566,28 +565,6 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, SetInCache( false ); } - UnlockModify(); // enable notify again - - // If there is an accessible layout we must call modify even - // with length zero, because we have to notify about the changed - // text node. - const SwRootFrame *pRootFrame; - if ( (nTextLen != nSplitPos) || - ( (pRootFrame = pNode->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()) != nullptr && - pRootFrame->IsAnyShellAccessible() ) ) - { - // tell the frames that something was "deleted" at the end - if( 1 == nTextLen - nSplitPos ) - { - SwDelChr aHint( nSplitPos ); - pNode->NotifyClients( nullptr, &aHint ); - } - else - { - SwDelText aHint( nSplitPos, nTextLen - nSplitPos ); - pNode->NotifyClients( nullptr, &aHint ); - } - } if ( HasHints() ) { MoveTextAttr_To_AttrSet(); @@ -690,9 +667,9 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, // Update the extents with new node; also inits merge flag, // so the MakeFramesForAdjacentContentNode below respects it pFrame->RegisterToNode(*pNode); - if (pFrame->GetText().isEmpty()) + if (nSplitPos == 0) { - // turns out it's empty - in this case, it was not + // in this case, it was not // invalidated because Cut didn't sent it any hints, // so we have to invalidate it here! pFrame->Prepare(PREP_CLEAR, nullptr, false); @@ -918,9 +895,18 @@ void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerg assert(rFirstNode.GetIndex() <= rNode.GetIndex()); pFrame->SetMergedPara(sw::CheckParaRedlineMerge( *pFrame, rFirstNode, eMode)); - assert(pFrame->GetMergedPara()); - assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode)); - assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex()); + // there is no merged para in case the deleted node had one but + // nothing was actually hidden + if (pFrame->GetMergedPara()) + { + assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode)); + assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex()); + // tdf#135978 Join: recreate fly frames anchored to subsequent nodes + if (eRecreateMerged == sw::Recreate::ThisNode) + { + AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr); + } + } eMode = sw::FrameMode::New; // Existing is not idempotent! } } @@ -1031,14 +1017,29 @@ SwContentNode *SwTextNode::JoinNext() pDoc->CorrAbs( aIdx, SwPosition( *this ), nOldLen, true ); } SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag()); + auto eRecreateMerged(eOldMergeFlag == SwNode::Merge::First + ? sw::Recreate::ThisNode + : sw::Recreate::No); + if (eRecreateMerged == sw::Recreate::No) + { + // tdf#137318 if a delete is inside one node, flag is still None! + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->GetMergedPara()) + { + eRecreateMerged = sw::Recreate::ThisNode; + break; + } + } + } + rNds.Delete(aIdx); SetWrong( pList, false ); SetGrammarCheck( pList3, false ); SetSmartTags( pList2, false ); InvalidateNumRule(); - CheckResetRedlineMergeFlag(*this, eOldMergeFlag == SwNode::Merge::First - ? sw::Recreate::ThisNode - : sw::Recreate::No); + CheckResetRedlineMergeFlag(*this, eRecreateMerged); } else { OSL_FAIL( "No TextNode." ); @@ -1175,8 +1176,7 @@ void SwTextNode::NewAttrSet( SwAttrPool& rPool ) void SwTextNode::Update( SwIndex const & rPos, const sal_Int32 nChangeLen, - const bool bNegative, - const bool bDelete ) + UpdateMode const eMode) { SetAutoCompleteWordDirty( true ); @@ -1185,7 +1185,7 @@ void SwTextNode::Update( if ( HasHints() ) { - if ( bNegative ) + if (eMode & UpdateMode::Negative) { std::vector<SwTextInputField*> aTextInputFields; @@ -1357,7 +1357,7 @@ void SwTextNode::Update( bool bSortMarks = false; SwIndexReg aTmpIdxReg; - if ( !bNegative && !bDelete ) + if (!(eMode & UpdateMode::Negative) && !(eMode & UpdateMode::Delete)) { const SwRedlineTable& rTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); for (SwRangeRedline* pRedl : rTable) @@ -1392,6 +1392,7 @@ void SwTextNode::Update( // Bookmarks must never grow to either side, when editing (directly) // to the left or right (i#29942)! Exception: if the bookmark has // 2 positions and start == end, then expand it (tdf#96479) + if (!(eMode & UpdateMode::Replace)) // Exception: Replace { bool bAtLeastOneBookmarkMoved = false; bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false; @@ -1514,7 +1515,7 @@ void SwTextNode::Update( } // base class - SwIndexReg::Update( rPos, nChangeLen, bNegative, bDelete ); + SwIndexReg::Update(rPos, nChangeLen, eMode); if (pCollector) { @@ -1533,10 +1534,21 @@ void SwTextNode::Update( //Any drawing objects anchored into this text node may be sorted by their //anchor position which may have changed here, so resort them - SwContentFrame* pContentFrame = getLayoutFrame(GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()); - SwSortedObjs* pSortedObjs = pContentFrame ? pContentFrame->GetDrawObjs() : nullptr; - if (pSortedObjs) - pSortedObjs->UpdateAll(); + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this); + for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next()) + { + SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs()); + if (pSortedObjs) + { + pSortedObjs->UpdateAll(); + } + // also sort the objs on the page frame + pSortedObjs = pFrame->FindPageFrame()->GetSortedObjs(); + if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly + { + pSortedObjs->UpdateAll(); + } + } // Update the paragraph signatures. if (SwEditShell* pEditShell = GetDoc()->GetEditShell()) @@ -2311,7 +2323,7 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx, SetIgnoreDontExpand( true ); } - Update( rIdx, nLen ); // text content changed! + Update(rIdx, nLen, UpdateMode::Default); // text content changed! if (nMode & SwInsertFlags::FORCEHINTEXPAND) { @@ -2447,7 +2459,7 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart, if (bUpdate) { // Update all SwIndex - pDest->Update( rDestStart, nLen, false, false/*??? why was it true*/); + pDest->Update(rDestStart, nLen, UpdateMode::Default); } CHECK_SWPHINTS(pDest); @@ -2640,7 +2652,7 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart, ++nAttrCnt; } } - Update( rStart, nLen, true, true ); + Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete); for (SwTextAttr* pHt : aArr) { @@ -2651,7 +2663,7 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart, } else { - Update( rStart, nLen, true, true ); + Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete); } // set after moving hints @@ -2744,7 +2756,7 @@ void SwTextNode::EraseText(const SwIndex &rIdx, const sal_Int32 nCount, TryDeleteSwpHints(); - Update( rIdx, nCnt, true ); + Update(rIdx, nCnt, UpdateMode::Negative); if( 1 == nCnt ) { @@ -3094,11 +3106,11 @@ sal_uInt16 lcl_BoundListLevel(const int nActualLevel) } // -> #i29560# -bool SwTextNode::HasNumber() const +bool SwTextNode::HasNumber(SwRootFrame const*const pLayout) const { bool bResult = false; - const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr; + const SwNumRule *const pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr; if ( pRule ) { const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel()))); @@ -3732,19 +3744,19 @@ void SwTextNode::ReplaceText( const SwIndex& rStart, const sal_Int32 nDelLen, ++const_cast<SwIndex&>(rStart); m_Text = m_Text.replaceAt(rStart.GetIndex(), nLen - 1, ""); - Update( rStart, nLen - 1, true ); + Update(rStart, nLen - 1, UpdateMode::Negative); OUString aTmpText( sInserted.copy(1) ); m_Text = m_Text.replaceAt(rStart.GetIndex(), 0, aTmpText); - Update( rStart, aTmpText.getLength() ); + Update(rStart, aTmpText.getLength(), UpdateMode::Replace); } else { m_Text = m_Text.replaceAt(nStartPos, nLen, ""); - Update( rStart, nLen, true ); + Update(rStart, nLen, UpdateMode::Negative); m_Text = m_Text.replaceAt(nStartPos, 0, sInserted); - Update( rStart, sInserted.getLength() ); + Update(rStart, sInserted.getLength(), UpdateMode::Replace); } SetIgnoreDontExpand( bOldExpFlg ); diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx index 5b363f2d9119..784babd9a3a9 100644 --- a/sw/source/core/txtnode/thints.cxx +++ b/sw/source/core/txtnode/thints.cxx @@ -1378,7 +1378,7 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode ) m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, ""); // Update SwIndexes SwIndex aTmpIdx( this, pAttr->GetStart() ); - Update( aTmpIdx, 1, true ); + Update(aTmpIdx, 1, UpdateMode::Negative); } // do not record deletion of Format! ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); @@ -1409,7 +1409,7 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode ) m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, ""); // Update SwIndexes SwIndex aTmpIdx( this, pAttr->GetStart() ); - Update( aTmpIdx, 1, true ); + Update(aTmpIdx, 1, UpdateMode::Negative); } DestroyAttr( pAttr ); return false; diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index 5c599d78afab..00b6d9ab8d05 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -1947,7 +1947,7 @@ void SwTextNode::ReplaceTextOnly( sal_Int32 nPos, sal_Int32 nLen, while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] ) ++nCnt; - Update( SwIndex( this, nMyOff ), nCnt ); + Update(SwIndex( this, nMyOff ), nCnt, UpdateMode::Default); nMyOff = nOff; //nMyOff -= nCnt; nI += nCnt - 1; @@ -1955,14 +1955,14 @@ void SwTextNode::ReplaceTextOnly( sal_Int32 nPos, sal_Int32 nLen, else if( nOff > nMyOff ) { // something is deleted - Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, true ); + Update(SwIndex(this, nMyOff+1), nOff - nMyOff, UpdateMode::Negative); nMyOff = nOff; } ++nMyOff; } if( nMyOff < nLen ) // something is deleted at the end - Update( SwIndex( this, nMyOff ), nLen - nMyOff, true ); + Update(SwIndex(this, nMyOff), nLen - nMyOff, UpdateMode::Negative); // notify the layout! SwDelText aDelHint( nPos, nTLen ); diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx index 50ff24aa4d98..ce36b4a1c37c 100644 --- a/sw/source/core/undo/rolbck.cxx +++ b/sw/source/core/undo/rolbck.cxx @@ -599,44 +599,37 @@ void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool ) std::unique_ptr<SwPaM> pPam; ::sw::mark::IMark* pMark = nullptr; + // now the situation is that m_bSavePos and m_bSaveOtherPos don't determine + // whether the mark was deleted + if (auto const iter = pMarkAccess->findMark(m_aName); iter != pMarkAccess->getAllMarksEnd()) + { + pMark = *iter; + } if(m_bSavePos) { SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode(); - OSL_ENSURE(pContentNd, - "<SwHistoryBookmark::SetInDoc(..)>" - " - wrong node for a mark"); - - // #111660# don't crash when nNode1 doesn't point to content node. - if(pContentNd) - pPam.reset(new SwPaM(*pContentNd, m_nContent)); + assert(pContentNd); + pPam.reset(new SwPaM(*pContentNd, m_nContent)); } else { - pMark = *pMarkAccess->findMark(m_aName); + assert(pMark); pPam.reset(new SwPaM(pMark->GetMarkPos())); } + assert(pPam); if(m_bSaveOtherPos) { SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode(); - OSL_ENSURE(pContentNd, - "<SwHistoryBookmark::SetInDoc(..)>" - " - wrong node for a mark"); - - if (pPam != nullptr && pContentNd) - { - pPam->SetMark(); - pPam->GetMark()->nNode = m_nOtherNode; - pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent); - } + assert(pContentNd); + pPam->SetMark(); + pPam->GetMark()->nNode = m_nOtherNode; + pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent); } else if(m_bHadOtherPos) { - if(!pMark) - pMark = *pMarkAccess->findMark(m_aName); - OSL_ENSURE(pMark->IsExpanded(), - "<SwHistoryBookmark::SetInDoc(..)>" - " - missing pos on old mark"); + assert(pMark); + assert(pMark->IsExpanded()); pPam->SetMark(); *pPam->GetMark() = pMark->GetOtherMarkPos(); } diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx index 4e55a46196b4..dc24ce96e2fd 100644 --- a/sw/source/core/undo/undel.cxx +++ b/sw/source/core/undo/undel.cxx @@ -172,6 +172,7 @@ static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange, // move the paragraph into this section and to record this in nSectDiff. SwUndoDelete::SwUndoDelete( SwPaM& rPam, + SwDeleteFlags const flags, bool bFullPara, bool bCalledByTableCpy ) : SwUndo(SwUndoId::DELETE, rPam.GetDoc()), @@ -190,7 +191,9 @@ SwUndoDelete::SwUndoDelete( m_bResetPgDesc( false ), m_bResetPgBrk( false ), m_bFromTableCopy( bCalledByTableCpy ) + , m_DeleteFlags(flags) { + assert(!m_bDelFullPara || !(m_DeleteFlags & SwDeleteFlags::ArtificialSelection)); m_bCacheComment = false; @@ -226,7 +229,9 @@ SwUndoDelete::SwUndoDelete( } else { - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), + DelContentType::AllMask + | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0))); ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); if (m_nEndNode - m_nSttNode > 1) // check for fully selected nodes { @@ -542,6 +547,7 @@ bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam ) if( m_bGroup && !m_bBackSp ) return false; m_bBackSp = true; } + // note: compare m_nSttContent here because the text isn't there any more! else if( pStt->nContent == m_nSttContent ) { if( m_bGroup && m_bBackSp ) return false; @@ -570,6 +576,30 @@ bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam ) return false; } + if ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) && m_pHistory) + { + IDocumentMarkAccess const& rIDMA(*pDoc->getIDocumentMarkAccess()); + for (auto i = m_pHistory->Count(); 0 < i; ) + { + --i; + SwHistoryHint const*const pHistory((*m_pHistory)[i]); + if (pHistory->Which() == HSTRY_BOOKMARK) + { + SwHistoryBookmark const*const pHistoryBM( + static_cast<SwHistoryBookmark const*>(pHistory)); + auto const ppMark(rIDMA.findMark(pHistoryBM->GetName())); + if (ppMark != rIDMA.getAllMarksEnd() + && (m_bBackSp + ? ((**ppMark).GetMarkPos() == *pStt) + : ((**ppMark).IsExpanded() + && (**ppMark).GetOtherMarkPos() == *pEnd))) + { // prevent grouping that would delete this mark on Redo() + return false; + } + } + } + } + { SwRedlineSaveDatas aTmpSav; const bool bSaved = FillSaveData( rDelPam, aTmpSav, false ); @@ -1135,7 +1165,10 @@ void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext) // don't include end node in the range: it may have been merged already // by the start node, or it may be merged by one of the moved nodes, // but if it isn't merged, its current frame(s) should be good... - SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara ? delFullParaEndNode : m_nEndNode); + SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara + ? delFullParaEndNode + // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table! + : (m_nEndNode + (rDoc.GetNodes()[m_nSttNode]->IsTableNode() && rDoc.GetNodes()[m_nEndNode]->IsEndNode() ? 1 : 0))); ::MakeFrames(&rDoc, start, end); } @@ -1197,7 +1230,11 @@ void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode); } else - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + { + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), + DelContentType::AllMask + | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0))); + } m_nSetPos = m_pHistory ? m_pHistory->Count() : 0; m_pHistory->Move( m_nSetPos, &aHstr ); @@ -1213,7 +1250,11 @@ void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode ); } else - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + { + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), + DelContentType::AllMask + | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0))); + } m_nSetPos = m_pHistory ? m_pHistory->Count() : 0; } @@ -1288,7 +1329,7 @@ void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) rDoc.getIDocumentContentOperations().DelFullPara( rPam ); } else - rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam ); + rDoc.getIDocumentContentOperations().DeleteAndJoin(rPam, m_DeleteFlags); } void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext) diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx index 3fe7f107f052..8ff858013b72 100644 --- a/sw/source/core/undo/undobj.cxx +++ b/sw/source/core/undo/undobj.cxx @@ -798,30 +798,26 @@ void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx, } } -// These two methods move the Point of Pam backwards/forwards. With that, one -// can span an area for a Undo/Redo. (The Point is then positioned in front of -// the area to manipulate!) -// The flag indicates if there is still content in front of Point. -bool SwUndoSaveContent::MovePtBackward( SwPaM& rPam ) +// These two methods save and restore the Point of PaM. +// If the point cannot be moved, a "backup" is created on the previous node. +// Either way, returned, inserting at its original position will not move it. +::std::optional<SwNodeIndex> SwUndoSaveContent::MovePtBackward(SwPaM & rPam) { rPam.SetMark(); if( rPam.Move( fnMoveBackward )) - return true; + return {}; - // If there is no content onwards, set Point simply to the previous position - // (Node and Content, so that Content will be detached!) - --rPam.GetPoint()->nNode; - rPam.GetPoint()->nContent.Assign( nullptr, 0 ); - return false; + return { SwNodeIndex(rPam.GetPoint()->nNode, -1) }; } -void SwUndoSaveContent::MovePtForward( SwPaM& rPam, bool bMvBkwrd ) +void SwUndoSaveContent::MovePtForward(SwPaM& rPam, ::std::optional<SwNodeIndex> && oMvBkwrd) { // Was there content before this position? - if( bMvBkwrd ) + if (!oMvBkwrd) rPam.Move( fnMoveForward ); else { + *rPam.GetPoint() = SwPosition(*oMvBkwrd); ++rPam.GetPoint()->nNode; SwContentNode* pCNd = rPam.GetContentNode(); if( pCNd ) @@ -981,10 +977,14 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, // Moving the anchor? else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd) & nDelContentType) && - // at least for calls from SwUndoDelete, - // this should work - other Undos don't - // remember the order of the cursor - (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex()) + // for SwUndoDelete: rPoint is the node that + // will be Joined - so anchor should be moved + // off it - but UndoImpl() split will insert + // new node *before* existing one so a no-op + // may need to be done here to add it to + // history for Undo. + (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex() + || pStt->nNode.GetIndex() == pAPos->nNode.GetIndex()) // Do not try to move the anchor to a table! && rMark.nNode.GetNode().IsTextNode()) { @@ -1061,6 +1061,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, // #i81002# bool bSavePos = false; bool bSaveOtherPos = false; + bool bDelete = false; const ::sw::mark::IMark *const pBkmk = pMarkAccess->getAllMarksBegin()[n]; auto const type(IDocumentMarkAccess::GetType(*pBkmk)); @@ -1077,6 +1078,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, { bSaveOtherPos = true; } + bDelete = bSavePos && bSaveOtherPos; } else { @@ -1108,13 +1110,26 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, && ( type == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK || type == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK || type == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK - || type == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))) + || type == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)) + || (bMaybe + && !(nDelContentType & DelContentType::Replace) + && type == IDocumentMarkAccess::MarkType::BOOKMARK + && pStt->nContent == 0 // entire paragraph deleted? + && pEnd->nContent == pEnd->nNode.GetNode().GetTextNode()->Len())) { if( bMaybe ) bSavePos = true; - bSaveOtherPos = true; + bDelete = true; + } + if (bDelete || pBkmk->GetOtherMarkPos() == *pEnd) + { + bSaveOtherPos = true; // tdf#148389 always undo if at end } } + if (!bSavePos && bMaybe && pBkmk->IsExpanded() && *pStt == pBkmk->GetMarkPos()) + { + bSavePos = true; // tdf#148389 always undo if at start + } if ( !bSavePos && !bSaveOtherPos && dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) ) @@ -1152,6 +1167,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, { bSavePos = true; bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only save the other pos if there is one + bDelete = true; } } } @@ -1165,8 +1181,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, m_pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos ); } if ( bSavePos - && ( bSaveOtherPos - || !pBkmk->IsExpanded() ) ) + && (bDelete || !pBkmk->IsExpanded())) { pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, false); n--; diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx index 85b20fae911b..ff3f4777329c 100644 --- a/sw/source/core/undo/unins.cxx +++ b/sw/source/core/undo/unins.cxx @@ -328,7 +328,7 @@ void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext) if( nLen ) { - const bool bMvBkwrd = MovePtBackward( *pPam ); + ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(*pPam); if (maText) { @@ -355,7 +355,7 @@ void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext) nNode = pPam->GetMark()->nNode.GetIndex(); nContent = pPam->GetMark()->nContent.GetIndex(); - MovePtForward( *pPam, bMvBkwrd ); + MovePtForward(*pPam, ::std::move(oMvBkwrd)); pPam->Exchange(); if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) { @@ -689,7 +689,8 @@ void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext) if( m_bSplitNext ) { - SwPosition aPos(*pNd, pNd->Len()); + assert(m_nSttCnt + m_sOld.getLength() <= pNd->Len()); + SwPosition aPos(*pNd, m_nSttCnt + m_sOld.getLength()); pDoc->getIDocumentContentOperations().SplitNode( aPos, false ); pNd->RestoreMetadata(m_pMetadataUndoEnd); pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); @@ -723,7 +724,7 @@ void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext) } rPam.GetPoint()->nNode = m_nSttNd; - rPam.GetPoint()->nContent = m_nSttCnt; + rPam.GetPoint()->nContent.Assign(rPam.GetPoint()->nNode.GetNode().GetTextNode(), m_nSttCnt); } void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) @@ -917,7 +918,7 @@ void SwUndoInsertLabel::UndoImpl(::sw::UndoRedoContext & rContext) aPam.GetPoint()->nNode = NODE.nNode; aPam.SetMark(); aPam.GetPoint()->nNode = NODE.nNode + 1; - NODE.pUndoInsNd = new SwUndoDelete( aPam, true ); + NODE.pUndoInsNd = new SwUndoDelete(aPam, SwDeleteFlags::Default, true); } } diff --git a/sw/source/core/undo/unredln.cxx b/sw/source/core/undo/unredln.cxx index 8aae3c055b63..c66d8eed133e 100644 --- a/sw/source/core/undo/unredln.cxx +++ b/sw/source/core/undo/unredln.cxx @@ -26,6 +26,8 @@ #include <pam.hxx> #include <ndtxt.hxx> #include <txtfrm.hxx> +#include <mvsave.hxx> +#include <rolbck.hxx> #include <UndoCore.hxx> #include <UndoDelete.hxx> #include <strings.hrc> @@ -153,7 +155,8 @@ void SwUndoRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); } -SwUndoRedlineDelete::SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUsrId ) +SwUndoRedlineDelete::SwUndoRedlineDelete( + const SwPaM& rRange, SwUndoId const nUsrId, SwDeleteFlags const flags) : SwUndoRedline( nUsrId != SwUndoId::EMPTY ? nUsrId : SwUndoId::DELETE, rRange ), bCanGroup( false ), bIsDelim( false ), bIsBackspace( false ) { @@ -174,6 +177,24 @@ SwUndoRedlineDelete::SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUsrId ) } m_bCacheComment = false; + if (flags & SwDeleteFlags::ArtificialSelection) + { + InitHistory(rRange); + } +} + +void SwUndoRedlineDelete::InitHistory(SwPaM const& rRedline) +{ + m_pHistory.reset(new SwHistory); + // try to rely on direction of rPam here so it works for + // backspacing/deleting consecutive characters + SaveFlyArr flys; + SaveFlyInRange(rRedline, *rRedline.GetMark(), flys, false, m_pHistory.get()); + RestFlyInRange(flys, *rRedline.GetPoint(), &rRedline.GetPoint()->nNode, true); + if (m_pHistory->Count()) + { + bCanGroup = false; // how to group history? + } } // bit of a hack, replace everything... @@ -197,12 +218,21 @@ void SwUndoRedlineDelete::SetRedlineText(const OUString & rText) void SwUndoRedlineDelete::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) { rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); + if (m_pHistory) + { + m_pHistory->TmpRollback(&rDoc, 0); + } } void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) { if (rPam.GetPoint() != rPam.GetMark()) { + if (m_pHistory) // if it was created before, it must be recreated now + { + rPam.Normalize(bIsBackspace); // to check the correct edge + InitHistory(rPam); + } rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(*mpRedlData, rPam), false ); } sw::UpdateFramesForAddDeleteRedline(rDoc, rPam); @@ -212,7 +242,7 @@ bool SwUndoRedlineDelete::CanGrouping( const SwUndoRedlineDelete& rNext ) { bool bRet = false; if( SwUndoId::DELETE == mnUserId && mnUserId == rNext.mnUserId && - bCanGroup == rNext.bCanGroup && + bCanGroup && rNext.bCanGroup && bIsDelim == rNext.bIsDelim && bIsBackspace == rNext.bIsBackspace && m_nSttNode == m_nEndNode && @@ -449,7 +479,7 @@ void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext & rContext) bool bJoinText, bJoinPrev; sw_GetJoinFlags(rPam, bJoinText, bJoinPrev); - pUnDel.reset( new SwUndoDelete(rPam, false) ); + pUnDel.reset( new SwUndoDelete(rPam, SwDeleteFlags::Default, false) ); if( bJoinText ) sw_JoinText(rPam, bJoinPrev); @@ -466,7 +496,7 @@ void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext & rContext) ++rPam.GetPoint()->nNode; rPam.GetBound().nContent.Assign( nullptr, 0 ); rPam.GetBound( false ).nContent.Assign( nullptr, 0 ); - pUnDel2.reset( new SwUndoDelete(rPam, true) ); + pUnDel2.reset( new SwUndoDelete(rPam, SwDeleteFlags::Default, true) ); } } rPam.DeleteMark(); diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index 9d1675c0f304..a07d951dc288 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -277,6 +277,8 @@ void SwUndoInsTable::UndoImpl(::sw::UndoRedoContext & rContext) if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, false, &pItem ) ) pNextNd->SetAttr( *pItem ); + + ::sw::NotifyTableCollapsedParagraph(pNextNd, nullptr); } m_sTableName = pTableNd->GetTable().GetFrameFormat()->GetName(); @@ -2420,11 +2422,11 @@ void SwUndoTableCpyTable::UndoImpl(::sw::UndoRedoContext & rContext) else *aPam.GetPoint() = SwPosition( aTmpIdx ); } - pUndo = std::make_unique<SwUndoDelete>( aPam, bDeleteCompleteParagraph, true ); + pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, bDeleteCompleteParagraph, true); } else { - pUndo = std::make_unique<SwUndoDelete>( aPam, true ); + pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true); if( pEntry->pUndo ) { pEntry->pUndo->UndoImpl(rContext); @@ -2501,7 +2503,9 @@ void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) // b62341295: Redline for copying tables - Start. rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() ); SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode()); - std::unique_ptr<SwUndo> pUndo = IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ? nullptr : std::make_unique<SwUndoDelete>( aPam, true ); + std::unique_ptr<SwUndo> pUndo(IDocumentRedlineAccess::IsRedlineOn(GetRedlineFlags()) + ? nullptr + : std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true)); if( pEntry->pUndo ) { pEntry->pUndo->UndoImpl(rContext); @@ -2513,7 +2517,7 @@ void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) // Otherwise aInsIdx has been moved during the Undo operation if( pEntry->bJoin ) { - SwPaM const& rLastPam = + SwPaM& rLastPam = rContext.GetCursorSupplier().GetCurrentShellCursor(); pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(), pEntry->bJoin, true ); @@ -2582,7 +2586,7 @@ void SwUndoTableCpyTable::AddBoxBefore( const SwTableBox& rBox, bool bDelContent SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() ); if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) - pEntry->pUndo = std::make_unique<SwUndoDelete>( aPam, true ); + pEntry->pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true); } pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>( @@ -2628,7 +2632,7 @@ void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex // rJoin is true if Redo() is calling and the content has already been merged std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox, - const SwPosition& rPos, bool& rJoin, bool bRedo ) + SwPosition& rPos, bool& rJoin, bool bRedo ) { std::unique_ptr<SwUndo> pUndo; // b62341295: Redline for copying tables @@ -2652,6 +2656,10 @@ std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const if( !bRedo && rPos.nNode.GetNode().GetTextNode() ) { // Try to merge, if not called by Redo() rJoin = true; + + // Park this somewhere else so nothing points to the to-be-deleted node. + rPos.nContent.Assign(pText, 0); + pText->JoinNext(); } } @@ -2682,7 +2690,7 @@ std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const aCellEnd = SwPosition( SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode() )); SwPaM aTmpPam( aDeleteStart, aCellEnd ); - pUndo = std::make_unique<SwUndoDelete>( aTmpPam, true ); + pUndo = std::make_unique<SwUndoDelete>(aTmpPam, SwDeleteFlags::Default, true); } SwPosition aCellStart( SwNodeIndex( *rBox.GetSttNd(), 2 ) ); pText = aCellStart.nNode.GetNode().GetTextNode(); @@ -2754,7 +2762,7 @@ void SwUndoCpyTable::UndoImpl(::sw::UndoRedoContext & rContext) } SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), 0 , 1 ); - pDel.reset( new SwUndoDelete( aPam, true ) ); + pDel.reset( new SwUndoDelete( aPam, SwDeleteFlags::Default, true ) ); } void SwUndoCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx index 28f41f817061..8258498b9181 100644 --- a/sw/source/core/undo/untblk.cxx +++ b/sw/source/core/undo/untblk.cxx @@ -125,11 +125,11 @@ void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys, m_nSttNode = pTmpPos->nNode.GetIndex(); m_nSttContent = pTmpPos->nContent.GetIndex(); + m_nDeleteTextNodes = nDeleteTextNodes; if (m_nDeleteTextNodes == 0) // if a table selection is added... { ++m_nSttNode; // ... then the CopyPam is not fully correct } - m_nDeleteTextNodes = nDeleteTextNodes; } // Fill m_FlyUndos with flys anchored to first and last paragraphs @@ -357,21 +357,25 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext) auto const pFlysAtInsPos(sw::GetFlysAnchoredAt(*pDoc, rPam.GetPoint()->nNode.GetIndex())); - const bool bMvBkwrd = MovePtBackward(rPam); + ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(rPam); + bool const isMoveFlyAnchors(!oMvBkwrd // equivalent to bCanMoveBack + || m_pUndoNodeIndex->GetNode().IsTextNode() + || (oMvBkwrd->GetNode().IsStartNode() + && m_pUndoNodeIndex->GetNode().IsSectionNode())); // re-insert content again (first detach m_pUndoNodeIndex!) sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex(); m_pUndoNodeIndex.reset(); MoveFromUndoNds(*pDoc, nMvNd, *rPam.GetMark()); - if (m_nDeleteTextNodes != 0) + if (m_nDeleteTextNodes != 0 || oMvBkwrd) { - MovePtForward(rPam, bMvBkwrd); + MovePtForward(rPam, ::std::move(oMvBkwrd)); } rPam.Exchange(); // at-char anchors post SplitNode are on index 0 of 2nd node and will // remain there - move them back to the start (end would also work?) - if (pFlysAtInsPos) + if (pFlysAtInsPos && isMoveFlyAnchors) { for (SwFrameFormat * pFly : *pFlysAtInsPos) { diff --git a/sw/source/core/unocore/unobkm.cxx b/sw/source/core/unocore/unobkm.cxx index ddeaccf1966b..a4d719016f68 100644 --- a/sw/source/core/unocore/unobkm.cxx +++ b/sw/source/core/unocore/unobkm.cxx @@ -410,6 +410,8 @@ void SAL_CALL SwXBookmark::setPropertyValue(const OUString& PropertyName, const uno::Any& rValue) { + SolarMutexGuard g; + if (PropertyName == UNO_NAME_BOOKMARK_HIDDEN) { bool bNewValue = false; diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx index e2e5c9411715..e5ced2a27fd0 100644 --- a/sw/source/core/unocore/unoframe.cxx +++ b/sw/source/core/unocore/unoframe.cxx @@ -2771,8 +2771,13 @@ void SwXFrame::attachToRange(uno::Reference<text::XTextRange> const& xTextRange, aFrameSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, 1 )); } - aPam.DeleteMark(); // mark position node will be deleted! - aIntPam.DeleteMark(); // mark position node will be deleted! + // park these no longer needed PaMs somewhere safe so MakeFlyAndMove + // can delete what it likes without any assert these are pointing to + // that content + aPam.DeleteMark(); + aIntPam.DeleteMark(); + *aPam.GetPoint() = *aIntPam.GetPoint() = SwPosition(pDoc->GetNodes()); + pFormat = pDoc->MakeFlyAndMove( *pCopySource, aFrameSet, nullptr, pParentFrameFormat ); diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx index 1f8b4d02edfd..e3ac0563be12 100644 --- a/sw/source/core/unocore/unoobj.cxx +++ b/sw/source/core/unocore/unoobj.cxx @@ -765,7 +765,7 @@ SwXTextCursor::~SwXTextCursor() } void SwXTextCursor::DeleteAndInsert(const OUString& rText, - const bool bForceExpandHints) + ::sw::DeleteAndInsertMode const eMode) { auto pUnoCursor = static_cast<SwCursor*>(m_pImpl->m_pUnoCursor.get()); if (pUnoCursor) @@ -780,13 +780,15 @@ void SwXTextCursor::DeleteAndInsert(const OUString& rText, { if (pCurrent->HasMark()) { - pDoc->getIDocumentContentOperations().DeleteAndJoin(*pCurrent); + pDoc->getIDocumentContentOperations().DeleteAndJoin(*pCurrent, + // is it "delete" or "replace"? + (nTextLen != 0 || eMode & ::sw::DeleteAndInsertMode::ForceReplace) ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default); } if(nTextLen) { const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR( - *pDoc, *pCurrent, rText, bForceExpandHints ) ); + *pDoc, *pCurrent, rText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints)) ); OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." ); SwUnoCursorHelper::SelectPam(*pUnoCursor, true); @@ -1754,7 +1756,7 @@ SwXTextCursor::setString(const OUString& aString) const bool bForceExpandHints( (CursorType::Meta == m_pImpl->m_eType) && dynamic_cast<SwXMeta*>(m_pImpl->m_xParentText.get()) ->CheckForOwnMemberMeta(*GetPaM(), true) ); - DeleteAndInsert(aString, bForceExpandHints); + DeleteAndInsert(aString, bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default); } uno::Any SwUnoCursorHelper::GetPropertyValue( diff --git a/sw/source/core/unocore/unoobj2.cxx b/sw/source/core/unocore/unoobj2.cxx index 502e2e74c2dd..53c881a8400a 100644 --- a/sw/source/core/unocore/unoobj2.cxx +++ b/sw/source/core/unocore/unoobj2.cxx @@ -790,8 +790,22 @@ void SwXTextRange::SetPositions(const SwPaM& rPam) m_pImpl->SetMark(*pMark); } +#if 0 +static void DeleteTable(SwDoc & rDoc, SwTable& rTable) +{ + SwSelBoxes aSelBoxes; + for (auto& rBox : rTable.GetTabSortBoxes()) + { + aSelBoxes.insert(rBox); + } + // note: if the table is the content in the section, this will create + // a new text node - that's desirable here + rDoc.DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected); +} +#endif + void SwXTextRange::DeleteAndInsert( - const OUString& rText, const bool bForceExpandHints) + const OUString& rText, ::sw::DeleteAndInsertMode const eMode) { if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition) { @@ -807,13 +821,14 @@ void SwXTextRange::DeleteAndInsert( m_pImpl->m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr); if (aCursor.HasMark()) { - m_pImpl->m_rDoc.getIDocumentContentOperations().DeleteAndJoin(aCursor); + m_pImpl->m_rDoc.getIDocumentContentOperations().DeleteAndJoin(aCursor, + (!rText.isEmpty() || eMode & ::sw::DeleteAndInsertMode::ForceReplace) ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default); } if (!rText.isEmpty()) { SwUnoCursorHelper::DocInsertStringSplitCR( - m_pImpl->m_rDoc, aCursor, rText, bForceExpandHints); + m_pImpl->m_rDoc, aCursor, rText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints)); SwUnoCursorHelper::SelectPam(aCursor, true); aCursor.Left(rText.getLength()); @@ -961,7 +976,7 @@ void SAL_CALL SwXTextRange::setString(const OUString& rString) { SolarMutexGuard aGuard; - DeleteAndInsert(rString, false); + DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::Default); } bool SwXTextRange::GetPositions(SwPaM& rToFill) const diff --git a/sw/source/core/unocore/unosett.cxx b/sw/source/core/unocore/unosett.cxx index 1f68135f7a4a..6d1752881418 100644 --- a/sw/source/core/unocore/unosett.cxx +++ b/sw/source/core/unocore/unosett.cxx @@ -1148,6 +1148,7 @@ void SwXNumberingRules::replaceByIndex(sal_Int32 nIndex, const uno::Any& rElemen SwXNumberingRules::SetNumberingRuleByIndex( aNumRule, *rProperties, nIndex); // set character format if needed + // this code appears to be dead - except when a style is assigned for BITMAP numbering? const SwCharFormats* pFormats = m_pDocShell->GetDoc()->GetCharFormats(); const size_t nChCount = pFormats->size(); for(sal_uInt16 i = 0; i < MAXLEVEL;i++) @@ -1492,7 +1493,7 @@ void SwXNumberingRules::SetNumberingRuleByIndex( SetPropertiesToNumFormat(aFormat, m_sNewCharStyleNames[nIndex], &m_sNewBulletFontNames[nIndex], &sHeadingStyleName, &sParagraphStyleName, - m_pDoc, rProperties); + m_pDoc, m_pDocShell, rProperties); if (m_pDoc && !sParagraphStyleName.isEmpty()) @@ -1539,8 +1540,11 @@ void SwXNumberingRules::SetPropertiesToNumFormat( OUString *const pHeadingStyleName, OUString *const pParagraphStyleName, SwDoc *const pDoc, + SwDocShell *const pDocShell, const uno::Sequence<beans::PropertyValue>& rProperties) { + assert(pDoc == nullptr || pDocShell == nullptr); // can't be both ordinary and chapter numbering + bool bWrongArg = false; std::unique_ptr<SvxBrushItem> pSetBrush; std::unique_ptr<Size> pSetSize; @@ -1588,14 +1592,15 @@ void SwXNumberingRules::SetPropertiesToNumFormat( rProp.Value >>= uTmp; OUString sCharFormatName; SwStyleNameMapper::FillUIName( uTmp, sCharFormatName, SwGetPoolIdFromName::ChrFmt ); + SwDoc *const pLocalDoc = pDocShell ? pDocShell->GetDoc() : pDoc; if (sCharFormatName == UNO_NAME_CHARACTER_FORMAT_NONE) { rCharStyleName = aInvalidStyle; aFormat.SetCharFormat(nullptr); } - else if(pDoc) + else if (pLocalDoc) { - const SwCharFormats* pFormats = pDoc->GetCharFormats(); + const SwCharFormats* pFormats = pLocalDoc->GetCharFormats(); const size_t nChCount = pFormats->size(); SwCharFormat* pCharFormat = nullptr; @@ -1614,7 +1619,7 @@ void SwXNumberingRules::SetPropertiesToNumFormat( { SfxStyleSheetBase* pBase; - SfxStyleSheetBasePool* pPool = pDoc->GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBasePool* pPool = pLocalDoc->GetDocShell()->GetStyleSheetPool(); pBase = pPool->Find(sCharFormatName, SfxStyleFamily::Char); if(!pBase) pBase = &pPool->Make(sCharFormatName, SfxStyleFamily::Char); @@ -1626,7 +1631,7 @@ void SwXNumberingRules::SetPropertiesToNumFormat( // If the character format has been found its name should not be in the // char style names array rCharStyleName.clear(); - } + } else rCharStyleName = sCharFormatName; } @@ -1779,7 +1784,7 @@ void SwXNumberingRules::SetPropertiesToNumFormat( { OUString sBulletFontName; rProp.Value >>= sBulletFontName; - SwDocShell* pLclDocShell = pDoc->GetDocShell(); + SwDocShell *const pLclDocShell = pDocShell ? pDocShell : pDoc ? pDoc->GetDocShell() : nullptr; if( !sBulletFontName.isEmpty() && pLclDocShell ) { const SvxFontListItem* pFontListItem = @@ -1878,7 +1883,8 @@ void SwXNumberingRules::SetPropertiesToNumFormat( } pSetVOrient->PutValue(rProp.Value, MID_VERTORIENT_ORIENT); } - else if (rProp.Name == UNO_NAME_HEADING_STYLE_NAME) + else if (rProp.Name == UNO_NAME_HEADING_STYLE_NAME + && pDocShell) // only on chapter numbering { if (pHeadingStyleName) { diff --git a/sw/source/core/unocore/unotbl.cxx b/sw/source/core/unocore/unotbl.cxx index fa378c7e8ae1..f149c3a4ce16 100644 --- a/sw/source/core/unocore/unotbl.cxx +++ b/sw/source/core/unocore/unotbl.cxx @@ -2227,7 +2227,7 @@ void SwXTextTable::dispose() SwSelBoxes aSelBoxes; for(auto& rBox : pTable->GetTabSortBoxes() ) aSelBoxes.insert(rBox); - pFormat->GetDoc()->DeleteRowCol(aSelBoxes); + pFormat->GetDoc()->DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected); } void SAL_CALL SwXTextTable::addEventListener( diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx index 3887a11191d7..4b5e16c028e2 100644 --- a/sw/source/core/unocore/unotext.cxx +++ b/sw/source/core/unocore/unotext.cxx @@ -364,7 +364,8 @@ SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange, dynamic_cast<SwXTextCursor*>(pCursor) ); if (pTextCursor) { - pTextCursor->DeleteAndInsert(rString, bForceExpandHints); + pTextCursor->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace + | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default)); } else { @@ -373,7 +374,8 @@ SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange, } else { - pRange->DeleteAndInsert(rString, bForceExpandHints); + pRange->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace + | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default)); } } else @@ -605,7 +607,21 @@ SwXText::insertTextContent( if (bAbsorb && !bAttribute) { - xRange->setString(OUString()); + uno::Reference<lang::XUnoTunnel> const xRangeTunnel(xRange, uno::UNO_QUERY); + if (SwXTextRange *const pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel)) + { + pRange->DeleteAndInsert(OUString(), ::sw::DeleteAndInsertMode::ForceReplace + | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default)); + } + else if (SwXTextCursor *const pCursor = dynamic_cast<SwXTextCursor*>(::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel))) + { + pCursor->DeleteAndInsert(OUString(), ::sw::DeleteAndInsertMode::ForceReplace + | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default)); + } + else + { + xRange->setString(OUString()); + } } uno::Reference< text::XTextRange > xTempRange = (bAttribute && bAbsorb) ? xRange : xRange->getStart(); @@ -1566,6 +1582,8 @@ SwXText::convertToTextFrame( } bool bParaAfterInserted = false; bool bParaBeforeInserted = false; + ::std::optional<SwPaM> oAnchorCheckPam; + oAnchorCheckPam.emplace(*pStartPam->Start(), *pEndPam->End()); if ( pStartStartNode && pEndStartNode && (pStartStartNode != pEndStartNode || pStartStartNode != GetStartNode()) @@ -1646,6 +1664,7 @@ SwXText::convertToTextFrame( bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd ); pEndPam->DeleteMark(); *pEndPam->GetPoint() = aEnd; + *oAnchorCheckPam->End() = aEnd; } pStartPam->SetMark(); *pStartPam->End() = *pEndPam->End(); @@ -1660,10 +1679,17 @@ SwXText::convertToTextFrame( { const SwFrameFormat* pFrameFormat = (*m_pImpl->m_pDoc->GetSpzFrameFormats())[i]; const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); - if ( !isGraphicNode(pFrameFormat) && - (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() || RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) && - pStartPam->Start()->nNode.GetIndex() <= rAnchor.GetContentAnchor()->nNode.GetIndex() && - pStartPam->End()->nNode.GetIndex() >= rAnchor.GetContentAnchor()->nNode.GetIndex()) + // note: Word can do at-char anchors in text frames - sometimes! + // see testFlyInFly for why this checks only the edges of the selection, + // and testFloatingTablesAnchor for why it excludes pre/post table + // added nodes + if (!isGraphicNode(pFrameFormat) + && ( (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() + && ( oAnchorCheckPam->Start()->nNode.GetIndex() == rAnchor.GetContentAnchor()->nNode.GetIndex() + || oAnchorCheckPam->End()->nNode.GetIndex() == rAnchor.GetContentAnchor()->nNode.GetIndex())) + || (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() + && ( *oAnchorCheckPam->Start() == *rAnchor.GetContentAnchor() + || *oAnchorCheckPam->End() == *rAnchor.GetContentAnchor())))) { if (pFrameFormat->GetName().isEmpty()) { @@ -1675,6 +1701,7 @@ SwXText::convertToTextFrame( } } } + oAnchorCheckPam.reset(); // clear SwIndex before deleting nodes const uno::Reference<text::XTextFrame> xNewFrame( SwXTextFrame::CreateXTextFrame(*m_pImpl->m_pDoc, nullptr)); @@ -1692,7 +1719,7 @@ SwXText::convertToTextFrame( new SwXTextRange(*pStartPam, this); assert(rNewFrame.IsDescriptor()); rNewFrame.attachToRange(xInsertTextRange, pStartPam.get()); - rNewFrame.setName(m_pImpl->m_pDoc->GetUniqueFrameName()); + assert(!rNewFrame.getName().isEmpty()); } SwTextNode *const pTextNode(pStartPam->GetNode().GetTextNode()); @@ -2259,12 +2286,6 @@ SwXText::copyText( // us, even if we have only a single paragraph. m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false); } - if (!pFirstNode) - { // the node at rPos was split; get rid of the first empty one so - // that the pasted table is first - auto pDelCursor(m_pImpl->m_pDoc->CreateUnoCursor(SwPosition(SwNodeIndex(*GetStartNode(), 1)))); - m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pDelCursor); - } } else { diff --git a/sw/source/filter/basflt/shellio.cxx b/sw/source/filter/basflt/shellio.cxx index 47657980602f..06bbbe14e88c 100644 --- a/sw/source/filter/basflt/shellio.cxx +++ b/sw/source/filter/basflt/shellio.cxx @@ -58,6 +58,7 @@ #include <poolfmt.hxx> #include <fltini.hxx> #include <docsh.hxx> +#include <ndole.hxx> #include <ndtxt.hxx> #include <redline.hxx> #include <swerror.h> @@ -377,7 +378,7 @@ ErrCode SwReader::Read( const Reader& rOptions ) // not insert: set the redline mode read from settings.xml eOld = ePostReadRedlineFlags & ~RedlineFlags::Ignore; - mxDoc->getIDocumentFieldsAccess().SetFieldsDirty(false, nullptr, 0); + mxDoc->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, 0); } mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); @@ -854,8 +855,7 @@ ErrCode SwWriter::Write( WriterRef const & rxWriter, const OUString* pRealFileNa pESh->StartAllAction(); } - const bool bOrigPurgeOle = pOutDoc->getIDocumentSettingAccess().get(DocumentSettingId::PURGE_OLE); - pOutDoc->getIDocumentSettingAccess().set(DocumentSettingId::PURGE_OLE, false); + auto xGuard = std::make_unique<PurgeGuard>(*pOutDoc); ErrCode nError = ERRCODE_NONE; if( pMedium ) @@ -865,7 +865,7 @@ ErrCode SwWriter::Write( WriterRef const & rxWriter, const OUString* pRealFileNa else if( xStg.is() ) nError = rxWriter->Write( *pPam, xStg, pRealFileName ); - pOutDoc->getIDocumentSettingAccess().set(DocumentSettingId::PURGE_OLE, bOrigPurgeOle ); + xGuard.reset(); if( pESh ) { diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx index a0da671de733..112975f98511 100644 --- a/sw/source/filter/html/htmlplug.cxx +++ b/sw/source/filter/html/htmlplug.cxx @@ -1087,7 +1087,12 @@ void SwHTMLParser::InsertFloatingFrame() bool bHasBorder = aFrameDesc.HasFrameBorder(); Size aMargin = aFrameDesc.GetMargin(); - xSet->setPropertyValue("FrameURL", uno::makeAny( aFrameDesc.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); + OUString sHRef = aFrameDesc.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if (INetURLObject(sHRef).IsExoticProtocol()) + NotifyMacroEventRead(); + + xSet->setPropertyValue("FrameURL", uno::makeAny( sHRef ) ); xSet->setPropertyValue("FrameName", uno::makeAny( aName ) ); if ( eScroll == ScrollingMode::Auto ) diff --git a/sw/source/filter/html/htmltab.cxx b/sw/source/filter/html/htmltab.cxx index 670ed3ae7aeb..8a7c2c8bbd72 100644 --- a/sw/source/filter/html/htmltab.cxx +++ b/sw/source/filter/html/htmltab.cxx @@ -4925,7 +4925,7 @@ void SwHTMLParser::ClearFootnotesMarksInRange(const SwNodeIndex& rMkNdIdx, const //ofz#9733 drop bookmarks in this range IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); - pMarkAccess->deleteMarks(rMkNdIdx, SwNodeIndex(rPtNdIdx, 1), nullptr, nullptr, nullptr); + pMarkAccess->deleteMarks(rMkNdIdx, SwNodeIndex(rPtNdIdx, 1), nullptr, nullptr, nullptr, false); SwFrameFormats& rTable = *pDoc->GetSpzFrameFormats(); for ( auto i = rTable.size(); i; ) diff --git a/sw/source/filter/ww8/wrtw8num.cxx b/sw/source/filter/ww8/wrtw8num.cxx index d0514482e5e1..05e860caec74 100644 --- a/sw/source/filter/ww8/wrtw8num.cxx +++ b/sw/source/filter/ww8/wrtw8num.cxx @@ -61,6 +61,17 @@ SwNumRule* MSWordExportBase::DuplicateNumRuleImpl(const SwNumRule *pRule) return pMyNumRule; } +sal_uInt16 MSWordExportBase::DuplicateNumRule(const SwNumRule* pRule, sal_uInt8 nLevel, sal_uInt16 nVal) +{ + SwNumRule* const pMyNumRule = DuplicateNumRuleImpl(pRule); + + SwNumFormat aNumFormat(pMyNumRule->Get(nLevel)); + aNumFormat.SetStart(nVal); + pMyNumRule->Set(nLevel, aNumFormat); + + return GetNumberingId(*pMyNumRule); +} + // multiple SwList can be based on the same SwNumRule; ensure one w:abstractNum // per SwList sal_uInt16 MSWordExportBase::DuplicateAbsNum(OUString const& rListId, diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index a64f5627a2b4..0433332493ef 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -657,6 +657,7 @@ public: /// List is set to restart at a particular value so for export make a /// completely new list based on this one and export that instead, /// which duplicates words behaviour in this respect. + sal_uInt16 DuplicateNumRule(const SwNumRule* pRule, sal_uInt8 nLevel, sal_uInt16 nVal); SwNumRule * DuplicateNumRuleImpl(const SwNumRule *pRule); /// check if a new abstractNum is needed for this list diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 0b2c6527c0ea..5868928c5bf6 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -3649,6 +3649,13 @@ void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule ) } } } + else if (pTextNd->IsListRestart()) + { + sal_uInt16 nStartWith = static_cast<sal_uInt16>(pTextNd->GetActualListStartValue()); + nNumId = GetExport().DuplicateNumRule(pRule, nLvl, nStartWith); + if (USHRT_MAX != nNumId) + ++nNumId; + } } else { diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx index bfd4e488fcb8..b42a28eeacb7 100644 --- a/sw/source/filter/xml/XMLRedlineImportHelper.cxx +++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx @@ -33,6 +33,9 @@ #include <IDocumentStylePoolAccess.hxx> #include <tools/datetime.hxx> #include <poolfmt.hxx> +#include <fmtanchr.hxx> +#include <ftnidx.hxx> +#include <txtftn.hxx> #include <unoredline.hxx> #include <DocumentRedlineManager.hxx> #include "xmlimp.hxx" @@ -571,6 +574,73 @@ inline bool XMLRedlineImportHelper::IsReady(const RedlineInfo* pRedline) !pRedline->bNeedsAdjustment ); } +/// recursively check if rPos or its anchor (if in fly or footnote) is in redline section +static auto RecursiveContains(SwStartNode const& rRedlineSection, SwNode const& rPos) -> bool +{ + if (rRedlineSection.GetIndex() <= rPos.GetIndex() + && rPos.GetIndex() <= rRedlineSection.EndOfSectionIndex()) + { + return true; + } + // loop to iterate "up" in the node tree and find an anchored XText + for (SwStartNode const* pStartNode = rPos.StartOfSectionNode(); + pStartNode != nullptr && pStartNode->GetIndex() != 0; + pStartNode = pStartNode->StartOfSectionNode()) + { + switch (pStartNode->GetStartNodeType()) + { + case SwNormalStartNode: + case SwTableBoxStartNode: + continue; + break; + case SwFlyStartNode: + { + SwFrameFormat const*const pFormat(pStartNode->GetFlyFormat()); + assert(pFormat); + SwFormatAnchor const& rAnchor(pFormat->GetAnchor()); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE) + { + return false; + } + else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_FLY) + { // anchor is on a start node, avoid skipping it: + pStartNode = rAnchor.GetContentAnchor()->nNode.GetNode().GetStartNode(); + assert(pStartNode); + // pass the next node to recursive call - it will call + // call StartOfSectionNode on it and go back to pStartNode + SwNodeIndex const next(*pStartNode, +1); + return RecursiveContains(rRedlineSection, next.GetNode()); + } + else + { + return RecursiveContains(rRedlineSection, rAnchor.GetContentAnchor()->nNode.GetNode()); + } + } + break; + case SwFootnoteStartNode: + { // sigh ... need to search + for (SwTextFootnote const*const pFootnote : rRedlineSection.GetDoc()->GetFootnoteIdxs()) + { + if (pStartNode == pFootnote->GetStartNode()->GetNode().GetStartNode()) + { + return RecursiveContains(rRedlineSection, pFootnote->GetTextNode()); + } + } + assert(false); + } + break; + case SwHeaderStartNode: + case SwFooterStartNode: + return false; // headers aren't anchored + break; + default: + assert(false); + break; + } + } + return false; +} + void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo) { OSL_ENSURE(nullptr != pRedlineInfo, "need redline info"); @@ -649,6 +719,16 @@ void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo) } } } + else if (pRedlineInfo->pContentIndex != nullptr + // should be enough to check 1 position of aPaM bc CheckNodesRange() above + && RecursiveContains(*pRedlineInfo->pContentIndex->GetNode().GetStartNode(), aPaM.GetPoint()->nNode.GetNode())) + { + SAL_WARN("sw.xml", "Recursive change tracking, removing"); + // reuse aPaM to remove it from nodes that will be deleted + *aPaM.GetPoint() = SwPosition(pRedlineInfo->pContentIndex->GetNode()); + aPaM.DeleteMark(); + pDoc->getIDocumentContentOperations().DeleteSection(&aPaM.GetPoint()->nNode.GetNode()); + } else { // regular file loading: insert redline diff --git a/sw/source/filter/xml/xmlexp.hxx b/sw/source/filter/xml/xmlexp.hxx index 22e6a42368a7..a5abc5baf15f 100644 --- a/sw/source/filter/xml/xmlexp.hxx +++ b/sw/source/filter/xml/xmlexp.hxx @@ -23,6 +23,8 @@ #include <xmloff/xmlexp.hxx> #include "xmlitmap.hxx" #include <xmloff/xmltoken.hxx> + +#include <optional> #include <vector> class SwDoc; @@ -73,7 +75,8 @@ class SwXMLExport : public SvXMLExport SwXMLTableInfo_Impl& rTableInfo, bool bTop=false ); - void ExportFormat( const SwFormat& rFormat, enum ::xmloff::token::XMLTokenEnum eClass ); + void ExportFormat(const SwFormat& rFormat, enum ::xmloff::token::XMLTokenEnum eClass, + ::std::optional<OUString> const oStyleName); void ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth ); void ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol ); diff --git a/sw/source/filter/xml/xmlfmte.cxx b/sw/source/filter/xml/xmlfmte.cxx index 209cdd5a3904..b7aa337eede3 100644 --- a/sw/source/filter/xml/xmlfmte.cxx +++ b/sw/source/filter/xml/xmlfmte.cxx @@ -46,7 +46,8 @@ using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::lang; using namespace ::xmloff::token; -void SwXMLExport::ExportFormat( const SwFormat& rFormat, enum XMLTokenEnum eFamily ) +void SwXMLExport::ExportFormat(const SwFormat& rFormat, enum XMLTokenEnum eFamily, + ::std::optional<OUString> const oStyleName) { // <style:style ...> CheckAttrList(); @@ -57,11 +58,14 @@ void SwXMLExport::ExportFormat( const SwFormat& rFormat, enum XMLTokenEnum eFami return; OSL_ENSURE( eFamily != XML_TOKEN_INVALID, "family must be specified" ); // style:name="..." + assert(oStyleName || (eFamily != XML_TABLE_ROW && eFamily != XML_TABLE_CELL)); bool bEncoded = false; - AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName( - rFormat.GetName(), &bEncoded ) ); + OUString const name(oStyleName ? *oStyleName : rFormat.GetName()); + AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName(name, &bEncoded)); if( bEncoded ) - AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rFormat.GetName() ); + { + AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name); + } if( eFamily != XML_TOKEN_INVALID ) AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, eFamily ); diff --git a/sw/source/filter/xml/xmliteme.cxx b/sw/source/filter/xml/xmliteme.cxx index b307a5c10872..8972bd14a51e 100644 --- a/sw/source/filter/xml/xmliteme.cxx +++ b/sw/source/filter/xml/xmliteme.cxx @@ -219,7 +219,7 @@ void SwXMLExport::ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nA { static_cast<SwXMLTableItemMapper_Impl *>(m_pTableItemMapper.get()) ->SetAbsWidth( nAbsWidth ); - ExportFormat( rFormat, XML_TABLE ); + ExportFormat(rFormat, XML_TABLE, {}); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltble.cxx b/sw/source/filter/xml/xmltble.cxx index 6a4fa1c9902b..d840509ecdc0 100644 --- a/sw/source/filter/xml/xmltble.cxx +++ b/sw/source/filter/xml/xmltble.cxx @@ -179,13 +179,18 @@ class SwXMLTableFrameFormatsSort_Impl { private: SwXMLFrameFormats_Impl aFormatList; + SwXMLTextParagraphExport::FormatMap & m_rFormatMap; + public: - bool AddRow( SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nLine ); - bool AddCell( SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, + SwXMLTableFrameFormatsSort_Impl(SwXMLTextParagraphExport::FormatMap & rFormatMap) + : m_rFormatMap(rFormatMap) + {} + ::std::optional<OUString> AddRow(SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nLine ); + ::std::optional<OUString> AddCell(SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ); }; -bool SwXMLTableFrameFormatsSort_Impl::AddRow( SwFrameFormat& rFrameFormat, +::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddRow(SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nLine ) { @@ -206,10 +211,12 @@ bool SwXMLTableFrameFormatsSort_Impl::AddRow( SwFrameFormat& rFrameFormat, // empty styles have not to be exported if( !pFrameSize && !pBrush && !pRowSplit ) - return false; + { + m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>()); // empty just to enable assert + return {}; + } // order is: -/brush, size/-, size/brush - bool bInsert = true; SwXMLFrameFormats_Impl::iterator i; for( i = aFormatList.begin(); i < aFormatList.end(); ++i ) { @@ -272,19 +279,19 @@ bool SwXMLTableFrameFormatsSort_Impl::AddRow( SwFrameFormat& rFrameFormat, continue; // found! - rFrameFormat.SetName( pTestFormat->GetName() ); - bInsert = false; - break; + auto const oName(m_rFormatMap.find(pTestFormat)->second); + assert(oName); + m_rFormatMap.emplace(&rFrameFormat, oName); + return {}; } - if( bInsert ) { - rFrameFormat.SetName( rNamePrefix + "." + OUString::number(nLine+1) ); + OUString const name(rNamePrefix + "." + OUString::number(nLine+1)); + m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>(name)); if ( i != aFormatList.end() ) ++i; aFormatList.insert( i, &rFrameFormat ); + return ::std::optional<OUString>(name); } - - return bInsert; } static OUString lcl_xmltble_appendBoxPrefix(const OUString& rNamePrefix, @@ -301,7 +308,7 @@ static OUString lcl_xmltble_appendBoxPrefix(const OUString& rNamePrefix, + "." + OUString::number(nRow + 1); } -bool SwXMLTableFrameFormatsSort_Impl::AddCell( SwFrameFormat& rFrameFormat, +::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddCell(SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ) { @@ -336,7 +343,10 @@ bool SwXMLTableFrameFormatsSort_Impl::AddCell( SwFrameFormat& rFrameFormat, // empty styles have not to be exported if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt ) - return false; + { + m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>()); // empty just to enable assert + return {}; + } // order is: -/-/-/num, // -/-/box/-, -/-/box/num, @@ -344,7 +354,6 @@ bool SwXMLTableFrameFormatsSort_Impl::AddCell( SwFrameFormat& rFrameFormat, // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num, // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-, // vert/brush/box/num - bool bInsert = true; SwXMLFrameFormats_Impl::iterator i; for( i = aFormatList.begin(); i < aFormatList.end(); ++i ) { @@ -462,19 +471,19 @@ bool SwXMLTableFrameFormatsSort_Impl::AddCell( SwFrameFormat& rFrameFormat, continue; // found! - rFrameFormat.SetName( pTestFormat->GetName() ); - bInsert = false; - break; + auto const oName(m_rFormatMap.find(pTestFormat)->second); + assert(oName); + m_rFormatMap.emplace(&rFrameFormat, oName); + return {}; } - if( bInsert ) { - rFrameFormat.SetName( lcl_xmltble_appendBoxPrefix( rNamePrefix, nCol, nRow, bTop ) ); + OUString const name(lcl_xmltble_appendBoxPrefix(rNamePrefix, nCol, nRow, bTop)); + m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>(name)); if ( i != aFormatList.end() ) ++i; aFormatList.insert( i, &rFrameFormat ); + return ::std::optional<OUString>(name); } - - return bInsert; } class SwXMLTableInfo_Impl @@ -483,10 +492,21 @@ class SwXMLTableInfo_Impl Reference<XTextSection> m_xBaseSection; bool m_bBaseSectionValid; sal_uInt32 const m_nPrefix; + SwXMLTextParagraphExport::FormatMap const& m_rLineFormats; + SwXMLTextParagraphExport::FormatMap const& m_rBoxFormats; public: - inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix ); + inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix, + SwXMLTextParagraphExport::FormatMap const& rLineFormats, + SwXMLTextParagraphExport::FormatMap const& rBoxFormats) + : m_pTable(pTable) + , m_bBaseSectionValid(false) + , m_nPrefix(nPrefix) + , m_rLineFormats(rLineFormats) + , m_rBoxFormats(rBoxFormats) + { + } const SwTable *GetTable() const { return m_pTable; } const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); } @@ -496,15 +516,10 @@ public: inline void SetBaseSection( const Reference < XTextSection >& rBase ); /// The namespace (table or loext) that should be used for the elements. sal_uInt16 GetPrefix() const { return m_nPrefix; } + SwXMLTextParagraphExport::FormatMap const& GetLineFormats() const { return m_rLineFormats; } + SwXMLTextParagraphExport::FormatMap const& GetBoxFormats() const { return m_rBoxFormats; } }; -inline SwXMLTableInfo_Impl::SwXMLTableInfo_Impl(const SwTable *pTable, sal_uInt16 nPrefix) : - m_pTable(pTable), - m_bBaseSectionValid(false), - m_nPrefix(nPrefix) -{ -} - inline void SwXMLTableInfo_Impl::SetBaseSection( const Reference < XTextSection >& rBaseSection ) { @@ -638,8 +653,10 @@ void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines, SwTableLine *pLine = rLines[nLine]; SwFrameFormat *pFrameFormat = pLine->GetFrameFormat(); - if( rExpRows.AddRow( *pFrameFormat, rNamePrefix, nLine ) ) - ExportFormat( *pFrameFormat, XML_TABLE_ROW ); + if (auto oNew = rExpRows.AddRow(*pFrameFormat, rNamePrefix, nLine)) + { + ExportFormat(*pFrameFormat, XML_TABLE_ROW, oNew); + } const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); const size_t nBoxes = rBoxes.size(); @@ -666,9 +683,11 @@ void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines, if( pBoxSttNd ) { SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat(); - if( rExpCells.AddCell( *pFrameFormat2, rNamePrefix, nOldCol, nLine, + if (auto oNew = rExpCells.AddCell(*pFrameFormat2, rNamePrefix, nOldCol, nLine, bTop) ) - ExportFormat( *pFrameFormat2, XML_TABLE_CELL ); + { + ExportFormat(*pFrameFormat2, XML_TABLE_CELL, oNew); + } Reference < XCell > xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()), @@ -714,8 +733,13 @@ void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines, } } -void SwXMLExport::ExportTableAutoStyles( const SwTableNode& rTableNd ) +void SwXMLExport::ExportTableAutoStyles(const SwTableNode& rTableNd) { + auto & rFormats(static_cast<SwXMLTextParagraphExport *>(GetTextParagraphExport().get())->GetTableFormats()); + auto const it(rFormats.find(&rTableNd)); + assert(it != rFormats.end()); + SwXMLTextParagraphExport::FormatMap & rRowFormats(it->second.first); + SwXMLTextParagraphExport::FormatMap & rBoxFormats(it->second.second); const SwTable& rTable = rTableNd.GetTable(); const SwFrameFormat *pTableFormat = rTable.GetFrameFormat(); @@ -743,9 +767,9 @@ void SwXMLExport::ExportTableAutoStyles( const SwTableNode& rTableNd ) ExportTableFormat( *pTableFormat, nAbsWidth ); SwXMLTableColumnsSortByWidth_Impl aExpCols; - SwXMLTableFrameFormatsSort_Impl aExpRows; - SwXMLTableFrameFormatsSort_Impl aExpCells; - SwXMLTableInfo_Impl aTableInfo( &rTable, XML_NAMESPACE_TABLE ); + SwXMLTableFrameFormatsSort_Impl aExpRows(rRowFormats); + SwXMLTableFrameFormatsSort_Impl aExpCells(rBoxFormats); + SwXMLTableInfo_Impl aTableInfo(&rTable, XML_NAMESPACE_TABLE, rRowFormats, rBoxFormats); ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth, pTableFormat->GetName(), aExpCols, aExpRows, aExpCells, aTableInfo, true); @@ -763,10 +787,12 @@ void SwXMLExport::ExportTableBox( const SwTableBox& rBox, const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat(); if( pFrameFormat ) { - const OUString& sName = pFrameFormat->GetName(); - if( !sName.isEmpty() ) + auto const it(rTableInfo.GetBoxFormats().find(pFrameFormat)); + assert(it != rTableInfo.GetBoxFormats().end()); + if (it->second) { - AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(sName) ); + assert(!it->second->isEmpty()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second)); } } } @@ -896,10 +922,12 @@ void SwXMLExport::ExportTableLine( const SwTableLine& rLine, const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat(); if( pFrameFormat ) { - const OUString& sName = pFrameFormat->GetName(); - if( !sName.isEmpty() ) + auto const it(rTableInfo.GetLineFormats().find(pFrameFormat)); + assert(it != rTableInfo.GetLineFormats().end()); + if (it->second) { - AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(sName) ); + assert(!it->second->isEmpty()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second)); } } @@ -1051,29 +1079,6 @@ void SwXMLExport::ExportTableLines( const SwTableLines& rLines, delete pLines; } -static void lcl_xmltble_ClearName_Line( SwTableLine* pLine ); - -static void lcl_xmltble_ClearName_Box( SwTableBox* pBox ) -{ - if( !pBox->GetSttNd() ) - { - for( SwTableLine* pLine : pBox->GetTabLines() ) - lcl_xmltble_ClearName_Line( pLine ); - } - else - { - SwFrameFormat *pFrameFormat = pBox->GetFrameFormat(); - if( pFrameFormat && !pFrameFormat->GetName().isEmpty() ) - pFrameFormat->SetName( OUString() ); - } -} - -void lcl_xmltble_ClearName_Line( SwTableLine* pLine ) -{ - for( SwTableBox* pBox : pLine->GetTabBoxes() ) - lcl_xmltble_ClearName_Box( pBox ); -} - void SwXMLExport::ExportTable( const SwTableNode& rTableNd ) { const SwTable& rTable = rTableNd.GetTable(); @@ -1132,15 +1137,16 @@ void SwXMLExport::ExportTable( const SwTableNode& rTableNd ) XML_DDE_SOURCE, true, false); } - SwXMLTableInfo_Impl aTableInfo( &rTable, nPrefix ); + auto const& rFormats(static_cast<SwXMLTextParagraphExport const*>(GetTextParagraphExport().get())->GetTableFormats()); + auto const it(rFormats.find(&rTableNd)); + assert(it != rFormats.end()); + SwXMLTableInfo_Impl aTableInfo(&rTable, nPrefix, it->second.first, it->second.second); ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() ); - - for( SwTableLine *pLine : const_cast<SwTable &>(rTable).GetTabLines() ) - lcl_xmltble_ClearName_Line( pLine ); } } void SwXMLTextParagraphExport::exportTableAutoStyles() { + // note: maTableNodes is used here only to keep the iteration order as before for (const auto* pTableNode : maTableNodes) { static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode); @@ -1187,6 +1193,7 @@ void SwXMLTextParagraphExport::exportTable( && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(aIdx))) { maTableNodes.push_back(pTableNd); + m_TableFormats.emplace(pTableNd, ::std::make_pair(SwXMLTextParagraphExport::FormatMap(), SwXMLTextParagraphExport::FormatMap())); // Collect all tables inside cells of this table, too const auto aCellNames = pXTable->getCellNames(); for (const OUString& rCellName : aCellNames) diff --git a/sw/source/filter/xml/xmltexte.hxx b/sw/source/filter/xml/xmltexte.hxx index 4432e4ce0166..78e0271384c3 100644 --- a/sw/source/filter/xml/xmltexte.hxx +++ b/sw/source/filter/xml/xmltexte.hxx @@ -23,6 +23,9 @@ #include <xmloff/txtparae.hxx> #include <tools/globname.hxx> +#include <optional> +#include <unordered_map> + #define XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE "vnd.sun.star.GraphicObject:" class SwXMLExport; @@ -41,6 +44,10 @@ class SwXMLTextParagraphExport : public XMLTextParagraphExport // Collected autostyles for use in exportTextAutoStyles std::vector<const SwTableNode*> maTableNodes; +public: + typedef ::std::unordered_map<SwFrameFormat const*, ::std::optional<OUString>> FormatMap; +private: + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> m_TableFormats; static SwNoTextNode *GetNoTextNode( const css::uno::Reference < css::beans::XPropertySet >& rPropSet ); @@ -63,6 +70,11 @@ public: SwXMLExport& rExp, SvXMLAutoStylePoolP& rAutoStylePool ); virtual ~SwXMLTextParagraphExport() override; + + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> const& + GetTableFormats() const { return m_TableFormats; } + ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> & + GetTableFormats() { return m_TableFormats; } }; #endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX diff --git a/sw/source/filter/xml/xmltexti.cxx b/sw/source/filter/xml/xmltexti.cxx index 788bec5c2d47..caf300f239c6 100644 --- a/sw/source/filter/xml/xmltexti.cxx +++ b/sw/source/filter/xml/xmltexti.cxx @@ -853,9 +853,14 @@ uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertFloatingFra uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); if ( xSet.is() ) { + OUString sHRef = URIHelper::SmartRel2Abs( + INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ); + + if (INetURLObject(sHRef).IsExoticProtocol()) + GetXMLImport().NotifyMacroEventRead(); + xSet->setPropertyValue("FrameURL", - makeAny( URIHelper::SmartRel2Abs( - INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ) ); + makeAny( sHRef ) ); xSet->setPropertyValue("FrameName", makeAny( rName ) ); diff --git a/sw/source/ui/config/optpage.cxx b/sw/source/ui/config/optpage.cxx index 88b6740e8a9a..98359cc2ae05 100644 --- a/sw/source/ui/config/optpage.cxx +++ b/sw/source/ui/config/optpage.cxx @@ -1218,6 +1218,7 @@ SwShdwCursorOptionsTabPage::SwShdwCursorOptionsTabPage(weld::Container* pPage, w , m_xFillSpaceRB(m_xBuilder->weld_radio_button("fillspace")) , m_xCursorProtFrame(m_xBuilder->weld_frame("crsrprotframe")) , m_xCursorInProtCB(m_xBuilder->weld_check_button("cursorinprot")) + , m_xDefaultAnchorType(m_xBuilder->weld_combo_box("cxDefaultAnchor")) , m_xMathBaselineAlignmentCB(m_xBuilder->weld_check_button("mathbaseline")) { const SfxPoolItem* pItem = nullptr; diff --git a/sw/source/ui/dialog/swdlgfact.cxx b/sw/source/ui/dialog/swdlgfact.cxx index 05dccd0da12a..247e86b9e71b 100644 --- a/sw/source/ui/dialog/swdlgfact.cxx +++ b/sw/source/ui/dialog/swdlgfact.cxx @@ -807,9 +807,9 @@ VclPtr<AbstractSwAsciiFilterDlg> SwAbstractDialogFactory_Impl::CreateSwAsciiFilt } VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwInsertBookmarkDlg(weld::Window *pParent, - SwWrtShell &rSh, SfxRequest& rReq) + SwWrtShell &rSh, SfxRequest& rReq, OUString const*const pSelected) { - return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_unique<SwInsertBookmarkDlg>(pParent, rSh, rReq)); + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_unique<SwInsertBookmarkDlg>(pParent, rSh, rReq, pSelected)); } VclPtr<AbstractSwBreakDlg> SwAbstractDialogFactory_Impl::CreateSwBreakDlg(weld::Window* pParent, SwWrtShell &rSh) diff --git a/sw/source/ui/dialog/swdlgfact.hxx b/sw/source/ui/dialog/swdlgfact.hxx index 579fe94562c2..a18a8df53998 100644 --- a/sw/source/ui/dialog/swdlgfact.hxx +++ b/sw/source/ui/dialog/swdlgfact.hxx @@ -654,7 +654,7 @@ public: virtual VclPtr<SfxAbstractDialog> CreateSwAddressAbstractDlg(weld::Window* pParent, const SfxItemSet& rSet) override; virtual VclPtr<AbstractSwAsciiFilterDlg> CreateSwAsciiFilterDlg(weld::Window* pParent, SwDocShell& rDocSh, SvStream* pStream) override; - virtual VclPtr<VclAbstractDialog> CreateSwInsertBookmarkDlg(weld::Window *pParent, SwWrtShell &rSh, SfxRequest& rReq) override; + virtual VclPtr<VclAbstractDialog> CreateSwInsertBookmarkDlg(weld::Window *pParent, SwWrtShell &rSh, SfxRequest& rReq, OUString const* pSelected) override; virtual VclPtr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window *pParent, SwWrtShell &rSh) override; virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) override; virtual VclPtr<SfxAbstractTabDialog> CreateSwCharDlg(weld::Window* pParent, SwView& pVw, const SfxItemSet& rCoreSet, diff --git a/sw/source/ui/fldui/fldtdlg.cxx b/sw/source/ui/fldui/fldtdlg.cxx index 8c1bb998fb85..6172d2bc7b8d 100644 --- a/sw/source/ui/fldui/fldtdlg.cxx +++ b/sw/source/ui/fldui/fldtdlg.cxx @@ -99,10 +99,10 @@ SwFieldDlg::~SwFieldDlg() { } -void SwFieldDlg::EndDialog() +void SwFieldDlg::EndDialog(int nResponse) { m_bClosing = true; - SfxTabDialogController::EndDialog(); + SfxTabDialogController::EndDialog(nResponse); m_bClosing = false; } @@ -110,9 +110,16 @@ void SwFieldDlg::Close() { if (m_bClosing) return; - m_pBindings->GetDispatcher()-> + const SfxPoolItem* pResult = m_pBindings->GetDispatcher()-> Execute(m_bDataBaseMode ? FN_INSERT_FIELD_DATA_ONLY : FN_INSERT_FIELD, SfxCallMode::SYNCHRON|SfxCallMode::RECORD); + if (!pResult) + { + // If Execute action did fail for whatever reason, this means that request + // to close did fail or wasn't delivered to SwTextShell::ExecField(). + // Just explicitly close dialog in this case. + SfxTabDialogController::EndDialog(RET_CLOSE); + } } void SwFieldDlg::Initialize(SfxChildWinInfo const *pInfo) diff --git a/sw/source/ui/index/swuiidxmrk.cxx b/sw/source/ui/index/swuiidxmrk.cxx index 33a0eab8cd69..5eca069b963b 100644 --- a/sw/source/ui/index/swuiidxmrk.cxx +++ b/sw/source/ui/index/swuiidxmrk.cxx @@ -955,7 +955,8 @@ SwIndexMarkFloatDlg::SwIndexMarkFloatDlg(SfxBindings* _pBindings, "modules/swriter/ui/indexentry.ui", "IndexEntryDialog") , m_aContent(m_xDialog, *m_xBuilder, bNew, *::GetActiveWrtShell()) { - m_aContent.ReInitDlg(*::GetActiveWrtShell()); + if (SwWrtShell* pSh = ::GetActiveWrtShell()) + m_aContent.ReInitDlg(*pSh); Initialize(pInfo); } diff --git a/sw/source/ui/misc/bookmark.cxx b/sw/source/ui/misc/bookmark.cxx index 730d9f1d575d..4133b28a8914 100644 --- a/sw/source/ui/misc/bookmark.cxx +++ b/sw/source/ui/misc/bookmark.cxx @@ -89,6 +89,7 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, ModifyHdl, weld::Entry&, void) // allow to delete only if all bookmarks are recognized m_xDeleteBtn->set_sensitive(nEntries > 0 && nSelectedEntries == nEntries && !m_bAreProtected); m_xGotoBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1); + m_xEditTextBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1); m_xRenameBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1 && !m_bAreProtected); } @@ -127,6 +128,7 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, DeleteHdl, weld::Button&, void) m_xDeleteBtn->set_sensitive(false); m_xGotoBtn->set_sensitive(false); + m_xEditTextBtn->set_sensitive(false); m_xRenameBtn->set_sensitive(false); m_xInsertBtn->set_sensitive(false); } @@ -151,6 +153,11 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void) if (!m_xBookmarksBox->has_focus()) return; + SelectionChanged(); +} + +void SwInsertBookmarkDlg::SelectionChanged() +{ OUStringBuffer sEditBoxText; int nSelectedRows = 0; m_xBookmarksBox->selected_foreach([this, &sEditBoxText, &nSelectedRows](weld::TreeIter& rEntry){ @@ -166,6 +173,7 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void) { m_xInsertBtn->set_sensitive(false); m_xGotoBtn->set_sensitive(nSelectedRows == 1); + m_xEditTextBtn->set_sensitive(nSelectedRows == 1); m_xRenameBtn->set_sensitive(nSelectedRows == 1 && !m_bAreProtected); m_xDeleteBtn->set_sensitive(!m_bAreProtected); m_xEditBox->set_text(sEditBoxText.makeStringAndClear()); @@ -174,11 +182,23 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void) { m_xInsertBtn->set_sensitive(!m_bAreProtected); m_xGotoBtn->set_sensitive(false); + m_xEditTextBtn->set_sensitive(false); m_xRenameBtn->set_sensitive(false); m_xDeleteBtn->set_sensitive(false); } } +IMPL_LINK_NOARG(SwInsertBookmarkDlg, EditTextHdl, weld::Button&, void) +{ + if (!ValidateBookmarks()) + return; + auto pSelected = m_xBookmarksBox->get_selected(); + if (!pSelected) + return; + + m_xBookmarksBox->start_editing(*pSelected); +} + IMPL_LINK_NOARG(SwInsertBookmarkDlg, RenameHdl, weld::Button&, void) { if (!ValidateBookmarks()) @@ -204,6 +224,7 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, RenameHdl, weld::Button&, void) ValidateBookmarks(); m_xDeleteBtn->set_sensitive(false); m_xGotoBtn->set_sensitive(false); + m_xEditTextBtn->set_sensitive(false); m_xRenameBtn->set_sensitive(false); m_xInsertBtn->set_sensitive(false); } @@ -229,6 +250,45 @@ IMPL_LINK(SwInsertBookmarkDlg, ChangeHideHdl, weld::ToggleButton&, rBox, void) m_xConditionFT->set_sensitive(bHide); } +IMPL_LINK(SwInsertBookmarkDlg, EditingHdl, weld::TreeIter const&, rIter, bool) +{ + sw::mark::IMark const* const pBookmark( + reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(rIter).toInt64())); + assert(pBookmark); + return pBookmark->IsExpanded() + && pBookmark->GetMarkPos().nNode == pBookmark->GetOtherMarkPos().nNode + && !m_xBookmarksBox->get_text(rIter).endsWith(u"…"); +} + +IMPL_LINK(SwInsertBookmarkDlg, EditedHdl, comma_issue, rIterString, bool) +{ + sw::mark::IMark const* const pBookmark( + reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(rIterString.first).toInt64())); + assert(pBookmark); + bool bRet(false); + if (pBookmark->GetMarkPos() != pBookmark->GetOtherMarkPos()) + { + if (pBookmark->GetMarkPos().nNode != pBookmark->GetOtherMarkPos().nNode) + { + return false; // don't allow editing if it spans multiple nodes + } + rSh.Push(); + rSh.GotoMark(pBookmark); + // GetSelText only works for 1 paragraph, but it's checked above + if (rSh.GetSelText() != rIterString.second) + { + bRet = rSh.Replace(rIterString.second, false); + } + rSh.Pop(SwEditShell::PopMode::DeleteCurrent); + } + else if (pBookmark->IsExpanded() && !rIterString.second.isEmpty()) + { // SwEditShell::Replace does nothing for empty selection + rSh.Insert(rIterString.second); + bRet = true; + } + return bRet; +} + void SwInsertBookmarkDlg::GotoSelectedBookmark() { if (!ValidateBookmarks()) @@ -292,14 +352,15 @@ void SwInsertBookmarkDlg::PopulateTable() { if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) { - m_xBookmarksBox->InsertBookmark(*ppBookmark); + m_xBookmarksBox->InsertBookmark(rSh, *ppBookmark); aTableBookmarks.emplace_back(*ppBookmark, (*ppBookmark)->GetName()); } } m_nLastBookmarksCount = pMarkAccess->getBookmarksCount(); } -SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, SfxRequest& rRequest) +SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, SfxRequest& rRequest, + OUString const* const pSelected) : SfxDialogController(pParent, "modules/swriter/ui/insertbookmark.ui", "InsertBookmarkDialog") , rSh(rS) , rReq(rRequest) @@ -309,6 +370,7 @@ SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, , m_xInsertBtn(m_xBuilder->weld_button("insert")) , m_xDeleteBtn(m_xBuilder->weld_button("delete")) , m_xGotoBtn(m_xBuilder->weld_button("goto")) + , m_xEditTextBtn(m_xBuilder->weld_button("edittext")) , m_xRenameBtn(m_xBuilder->weld_button("rename")) , m_xHideCB(m_xBuilder->weld_check_button("hide")) , m_xConditionFT(m_xBuilder->weld_label("condlabel")) @@ -319,17 +381,24 @@ SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, m_xBookmarksBox->connect_changed(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl)); m_xBookmarksBox->connect_row_activated(LINK(this, SwInsertBookmarkDlg, DoubleClickHdl)); m_xBookmarksBox->connect_column_clicked(LINK(this, SwInsertBookmarkDlg, HeaderBarClick)); + m_xBookmarksBox->connect_editing(LINK(this, SwInsertBookmarkDlg, EditingHdl), + LINK(this, SwInsertBookmarkDlg, EditedHdl)); m_xEditBox->connect_changed(LINK(this, SwInsertBookmarkDlg, ModifyHdl)); m_xInsertBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, InsertHdl)); m_xDeleteBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, DeleteHdl)); m_xGotoBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, GotoHdl)); + m_xEditTextBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, EditTextHdl)); m_xRenameBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, RenameHdl)); m_xHideCB->connect_toggled(LINK(this, SwInsertBookmarkDlg, ChangeHideHdl)); m_xDeleteBtn->set_sensitive(false); m_xGotoBtn->set_sensitive(false); + m_xEditTextBtn->set_sensitive(false); m_xRenameBtn->set_sensitive(false); + // select 3rd column, otherwise it'll pick 1st one + m_xBookmarksBox->set_column_editables({ false, false, true, false, false }); + PopulateTable(); m_xEditBox->set_text(m_xBookmarksBox->GetNameProposal()); @@ -352,6 +421,18 @@ SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, // disabled until "Hide" flag is not checked m_xConditionED->set_sensitive(false); m_xConditionFT->set_sensitive(false); + + if (pSelected) + { + if (m_xBookmarksBox->SelectByName(*pSelected)) + { + SelectionChanged(); + // which is better, focus on a button or focus on the table row? + // as long as editing doesn't work via the TreeView with VCL + // widgets, better on button. + m_xEditTextBtn->grab_focus(); + } + } } IMPL_LINK(SwInsertBookmarkDlg, HeaderBarClick, int, nColumn, void) @@ -410,40 +491,27 @@ std::unique_ptr<weld::TreeIter> BookmarkTable::get_selected() const return xIter; } -void BookmarkTable::InsertBookmark(sw::mark::IMark* pMark) +void BookmarkTable::InsertBookmark(SwWrtShell& rSh, sw::mark::IMark* const pMark) { sw::mark::IBookmark* pBookmark = dynamic_cast<sw::mark::IBookmark*>(pMark); assert(pBookmark); - OUString sBookmarkNodeText = pBookmark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText(); - sal_Int32 nBookmarkNodeTextPos = pBookmark->GetMarkStart().nContent.GetIndex(); - sal_Int32 nBookmarkTextLen = 0; - bool bPulledAll = false; - bool bPulling = false; + OUString sBookmarkNodeText; static const sal_Int32 nMaxTextLen = 50; if (pBookmark->IsExpanded()) { - nBookmarkTextLen = pBookmark->GetMarkEnd().nContent.GetIndex() - nBookmarkNodeTextPos; + rSh.Push(); + rSh.GotoMark(pBookmark); + rSh.GetSelectedText(sBookmarkNodeText, ParaBreakType::ToBlank); + rSh.Pop(SwEditShell::PopMode::DeleteCurrent); } - else + if (nMaxTextLen < sBookmarkNodeText.getLength()) { - if (nBookmarkNodeTextPos == sBookmarkNodeText.getLength()) // no text after bookmark - { - nBookmarkNodeTextPos = std::max<sal_Int32>(0, nBookmarkNodeTextPos - nMaxTextLen); - bPulling = true; - if (nBookmarkNodeTextPos == 0) - bPulledAll = true; - } - nBookmarkTextLen = sBookmarkNodeText.getLength() - nBookmarkNodeTextPos; + sBookmarkNodeText = sBookmarkNodeText.copy(0, nMaxTextLen); + ; + sBookmarkNodeText += u"…"; } - bool bExceedsLength = nBookmarkTextLen > nMaxTextLen; - nBookmarkTextLen = std::min<sal_Int32>(nMaxTextLen, nBookmarkTextLen); - sBookmarkNodeText = sBookmarkNodeText.copy(nBookmarkNodeTextPos, nBookmarkTextLen).trim(); - if (bExceedsLength) - sBookmarkNodeText += "..."; - else if (bPulling && !bPulledAll) - sBookmarkNodeText = "..." + sBookmarkNodeText; OUString sHidden = SwResId(STR_BOOKMARK_NO); if (pBookmark->IsHidden()) @@ -482,12 +550,13 @@ sw::mark::IMark* BookmarkTable::GetBookmarkByName(const OUString& sName) return reinterpret_cast<sw::mark::IMark*>(m_xControl->get_id(*xEntry).toInt64()); } -void BookmarkTable::SelectByName(const OUString& sName) +bool BookmarkTable::SelectByName(const OUString& sName) { auto xEntry = GetRowByBookmarkName(sName); if (!xEntry) - return; + return false; select(*xEntry); + return true; } OUString BookmarkTable::GetNameProposal() const diff --git a/sw/source/uibase/config/StoredChapterNumbering.cxx b/sw/source/uibase/config/StoredChapterNumbering.cxx index 5c94fc56110b..eea0c260bbf5 100644 --- a/sw/source/uibase/config/StoredChapterNumbering.cxx +++ b/sw/source/uibase/config/StoredChapterNumbering.cxx @@ -152,7 +152,7 @@ public: SwXNumberingRules::SetPropertiesToNumFormat( aNumberFormat, charStyleName, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, props); SwNumRulesWithName *const pRules(GetOrCreateRules()); pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName); diff --git a/sw/source/uibase/config/cfgitems.cxx b/sw/source/uibase/config/cfgitems.cxx index efbbdf4154b4..a5e8718aa718 100644 --- a/sw/source/uibase/config/cfgitems.cxx +++ b/sw/source/uibase/config/cfgitems.cxx @@ -41,6 +41,7 @@ SwDocDisplayItem::SwDocDisplayItem() : bCharHiddenText = bBookmarks = bManualBreak = true; + m_xDefaultAnchor = 1; //FLY_TO_CHAR }; // Item for the Settings dialog, page document view @@ -55,6 +56,7 @@ SwDocDisplayItem::SwDocDisplayItem(const SwViewOption& rVOpt ) : bCharHiddenText = rVOpt.IsShowHiddenChar(true); bBookmarks = rVOpt.IsShowBookmarks(true); bManualBreak = rVOpt.IsLineBreak(true); + m_xDefaultAnchor = rVOpt.GetDefaultAnchor(); } SfxPoolItem* SwDocDisplayItem::Clone( SfxItemPool* ) const @@ -75,7 +77,8 @@ bool SwDocDisplayItem::operator==( const SfxPoolItem& rAttr ) const bSoftHyphen == rItem.bSoftHyphen && bCharHiddenText == rItem.bCharHiddenText && bBookmarks == rItem.bBookmarks && - bManualBreak == rItem.bManualBreak ); + bManualBreak == rItem.bManualBreak && + m_xDefaultAnchor == rItem.m_xDefaultAnchor); } void SwDocDisplayItem::FillViewOptions( SwViewOption& rVOpt) const @@ -88,6 +91,7 @@ void SwDocDisplayItem::FillViewOptions( SwViewOption& rVOpt) const rVOpt.SetShowHiddenChar(bCharHiddenText ); rVOpt.SetShowBookmarks(bBookmarks ); rVOpt.SetLineBreak (bManualBreak ); + rVOpt.SetDefaultAnchor( m_xDefaultAnchor ); } SwElemItem::SwElemItem() : diff --git a/sw/source/uibase/config/usrpref.cxx b/sw/source/uibase/config/usrpref.cxx index cfc6142314c6..7f368346f66c 100644 --- a/sw/source/uibase/config/usrpref.cxx +++ b/sw/source/uibase/config/usrpref.cxx @@ -83,6 +83,7 @@ SwMasterUsrPref::~SwMasterUsrPref() } static const auto g_UpdateLinkIndex = 17; +const auto g_DefaultAnchor = 22; Sequence<OUString> SwContentViewConfig::GetPropertyNames() const { @@ -93,8 +94,8 @@ Sequence<OUString> SwContentViewConfig::GetPropertyNames() const "Display/DrawingControl", // 2 "Display/FieldCode", // 3 "Display/Note", // 4 - "Display/ShowContentTips", // 5 - "NonprintingCharacter/MetaCharacters", // 6 + "Display/ShowContentTips", // 5 + "NonprintingCharacter/MetaCharacters", // 6 "NonprintingCharacter/ParagraphEnd", // 7 "NonprintingCharacter/OptionalHyphen", // 8 "NonprintingCharacter/Space", // 9 @@ -109,7 +110,8 @@ Sequence<OUString> SwContentViewConfig::GetPropertyNames() const "Update/Field", // 18 "Update/Chart", // 19 "Display/ShowInlineTooltips", // 20 - "Display/UseHeaderFooterMenu" // 21 + "Display/UseHeaderFooterMenu", // 21 + "Display/DefaultAnchor" // 22 }; #if defined(__GNUC__) && !defined(__clang__) // clang 8.0.0 says strcmp isn't constexpr @@ -177,8 +179,9 @@ void SwContentViewConfig::ImplCommit() case 19: bVal = rParent.IsUpdateCharts(); break;// "Update/Chart" case 20: bVal = rParent.IsShowInlineTooltips(); break;// "Display/ShowInlineTooltips" case 21: bVal = rParent.IsUseHeaderFooterMenu(); break;// "Display/UseHeaderFooterMenu" + case 22: pValues[nProp] <<= rParent.GetDefaultAnchor(); break;// "Display/DefaultAnchor" } - if (nProp != g_UpdateLinkIndex) + if ((nProp != g_UpdateLinkIndex) && (nProp != g_DefaultAnchor)) pValues[nProp] <<= bVal; } PutProperties(aNames, aValues); @@ -196,7 +199,8 @@ void SwContentViewConfig::Load() { if(pValues[nProp].hasValue()) { - bool bSet = nProp != g_UpdateLinkIndex && *o3tl::doAccess<bool>(pValues[nProp]); + bool bSet = ((nProp != g_UpdateLinkIndex) && (nProp != g_DefaultAnchor)) + && *o3tl::doAccess<bool>(pValues[nProp]); switch(nProp) { case 0: rParent.SetGraphic(bSet); break;// "Display/GraphicObject", @@ -227,6 +231,13 @@ void SwContentViewConfig::Load() case 19: rParent.SetUpdateCharts(bSet); break;// "Update/Chart" case 20: rParent.SetShowInlineTooltips(bSet); break;// "Display/ShowInlineTooltips" case 21: rParent.SetUseHeaderFooterMenu(bSet); break;// "Display/UseHeaderFooterMenu" + case 22: + { + sal_Int32 nSet = 0; + pValues[nProp] >>= nSet; + rParent.SetDefaultAnchor(nSet); + } + break; // "Display/DefaultAnchor" } } } diff --git a/sw/source/uibase/config/viewopt.cxx b/sw/source/uibase/config/viewopt.cxx index 6059ef5ec8c8..6fa49a4258f0 100644 --- a/sw/source/uibase/config/viewopt.cxx +++ b/sw/source/uibase/config/viewopt.cxx @@ -83,6 +83,7 @@ bool SwViewOption::IsEqualFlags( const SwViewOption &rOpt ) const && mbHideWhitespaceMode == rOpt.mbHideWhitespaceMode && m_bShowPlaceHolderFields == rOpt.m_bShowPlaceHolderFields && m_bIdle == rOpt.m_bIdle + && m_nDefaultAnchor == rOpt.m_nDefaultAnchor #ifdef DBG_UTIL // correspond to the statements in ui/config/cfgvw.src && m_bTest1 == rOpt.IsTest1() @@ -210,6 +211,8 @@ SwViewOption::SwViewOption() : m_bIdle = true; + m_nDefaultAnchor = 1; //FLY_TO_CHAR + #ifdef DBG_UTIL // correspond to the statements in ui/config/cfgvw.src m_bTest1 = m_bTest2 = m_bTest3 = m_bTest4 = @@ -248,6 +251,7 @@ SwViewOption::SwViewOption(const SwViewOption& rVOpt) mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode; m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields; m_bIdle = rVOpt.m_bIdle; + m_nDefaultAnchor = rVOpt.m_nDefaultAnchor; #ifdef DBG_UTIL m_bTest1 = rVOpt.m_bTest1; @@ -289,6 +293,7 @@ SwViewOption& SwViewOption::operator=( const SwViewOption &rVOpt ) mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode; m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields; m_bIdle = rVOpt.m_bIdle; + m_nDefaultAnchor = rVOpt.m_nDefaultAnchor; #ifdef DBG_UTIL m_bTest1 = rVOpt.m_bTest1; @@ -360,6 +365,24 @@ sal_uInt16 GetHtmlMode(const SwDocShell* pShell) return nRet; } +RndStdIds SwViewOption::GetDefaultAnchorType() +{ + switch ( m_nDefaultAnchor ) + { + case 0: + return RndStdIds::FLY_AT_PARA; //0 + break; + case 1: + return RndStdIds::FLY_AT_CHAR; //4 + break; + case 2: + return RndStdIds::FLY_AS_CHAR; //1 + break; + default: + return RndStdIds::FLY_AT_CHAR; //4 + }//switch +} + Color& SwViewOption::GetDocColor() { return s_aDocColor; diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index d63d58850aa0..51ca02aaa524 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -3950,7 +3950,7 @@ bool SwTransferable::PrivateDrop( SwWrtShell& rSh, const Point& rDragPt, if ( bTableSel ) { /* delete table contents not cells */ - rSrcSh.Delete(); + rSrcSh.Delete(false); } else { diff --git a/sw/source/uibase/docvw/OverlayRanges.hxx b/sw/source/uibase/docvw/OverlayRanges.hxx index f8ea5694b128..8deecac241fd 100644 --- a/sw/source/uibase/docvw/OverlayRanges.hxx +++ b/sw/source/uibase/docvw/OverlayRanges.hxx @@ -23,6 +23,7 @@ #include <svx/sdr/overlay/overlayobject.hxx> #include <basegfx/range/b2drange.hxx> +#include <memory> #include <vector> #include <memory> diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.hxx b/sw/source/uibase/docvw/ShadowOverlayObject.hxx index 4b333cf4c06f..506b801d0991 100644 --- a/sw/source/uibase/docvw/ShadowOverlayObject.hxx +++ b/sw/source/uibase/docvw/ShadowOverlayObject.hxx @@ -20,6 +20,10 @@ #ifndef INCLUDED_SW_SOURCE_UIBASE_DOCVW_SHADOWOVERLAYOBJECT_HXX #define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SHADOWOVERLAYOBJECT_HXX +#include <sal/config.h> + +#include <memory> + #include <svx/sdr/overlay/overlayobject.hxx> #include <memory> diff --git a/sw/source/uibase/docvw/SidebarTxtControl.cxx b/sw/source/uibase/docvw/SidebarTxtControl.cxx index bdbb0e2804e4..62387bc03330 100644 --- a/sw/source/uibase/docvw/SidebarTxtControl.cxx +++ b/sw/source/uibase/docvw/SidebarTxtControl.cxx @@ -270,9 +270,7 @@ void SidebarTextControl::KeyInput( const KeyEvent& rKeyEvt ) } else { - std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); - std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InfoReadonlyDialog")); - xQuery->run(); + mrDocView.GetWrtShell().InfoReadOnlyDialog(); } } if (bDone) diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 20f5761f1361..28a672963e83 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -163,6 +163,9 @@ #include <sfx2/event.hxx> #include <memory> +#include "../../core/crsr/callnk.hxx" + + using namespace sw::mark; using namespace ::com::sun::star; @@ -1838,9 +1841,7 @@ KEYINPUT_CHECKTABLE_INSDEL: } else if (!rSh.IsCursorInParagraphMetadataField()) { - std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); - std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); - xInfo->run(); + rSh.InfoReadOnlyDialog(); eKeyState = SwKeyState::End; } break; @@ -1993,9 +1994,7 @@ KEYINPUT_CHECKTABLE_INSDEL: } else if (!rSh.IsCursorInParagraphMetadataField()) { - std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); - std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); - xInfo->run(); + rSh.InfoReadOnlyDialog(); eKeyState = SwKeyState::End; } break; @@ -3700,7 +3699,7 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt) // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART // and CH_TXT_ATR_INPUTFIELDEND rSh.SttSelect(); - rSh.SelectText( aFieldAtPos.pFndTextAttr->GetStart() + 1, + rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1, *(aFieldAtPos.pFndTextAttr->End()) - 1 ); } // don't reset here any longer so that, in case through MouseMove @@ -3730,8 +3729,8 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt) // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART // and CH_TXT_ATR_INPUTFIELDEND rSh.SttSelect(); - rSh.SelectText( aFieldAtPos.pFndTextAttr->GetStart() + 1, - *(aFieldAtPos.pFndTextAttr->End()) - 1 ); + rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1, + *(aFieldAtPos.pFndTextAttr->End()) - 1 ); } } @@ -6287,8 +6286,7 @@ OUString SwEditWin::GetSurroundingText() const rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); else if( !rSh.HasSelection() ) { - SwPosition *pPos = rSh.GetCursor()->GetPoint(); - const sal_Int32 nPos = pPos->nContent.GetIndex(); + rSh.Push(); // get the sentence around the cursor rSh.HideCursor(); @@ -6297,8 +6295,7 @@ OUString SwEditWin::GetSurroundingText() const rSh.GoEndSentence(); rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); - pPos->nContent = nPos; - rSh.ClearMark(); + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); rSh.HideCursor(); } @@ -6318,18 +6315,20 @@ Selection SwEditWin::GetSurroundingTextSelection() const { // Return the position of the visible cursor in the sentence // around the visible cursor. - SwPosition *pPos = rSh.GetCursor()->GetPoint(); - const sal_Int32 nPos = pPos->nContent.GetIndex(); + TextFrameIndex const nPos(rSh.GetCursorPointAsViewIndex()); + + // store shell state *before* Push + ::std::unique_ptr<SwCallLink> pLink(::std::make_unique<SwCallLink>(rSh)); + rSh.Push(); rSh.HideCursor(); rSh.GoStartSentence(); - const sal_Int32 nStartPos = rSh.GetCursor()->GetPoint()->nContent.GetIndex(); + TextFrameIndex const nStartPos(rSh.GetCursorPointAsViewIndex()); - pPos->nContent = nPos; - rSh.ClearMark(); + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent, ::std::move(pLink)); rSh.ShowCursor(); - return Selection( nPos - nStartPos, nPos - nStartPos ); + return Selection(sal_Int32(nPos - nStartPos), sal_Int32(nPos - nStartPos)); } } diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx index e09d42623ee1..db6b036a7cc8 100644 --- a/sw/source/uibase/frmdlg/frmmgr.cxx +++ b/sw/source/uibase/frmdlg/frmmgr.cxx @@ -88,7 +88,9 @@ SwFlyFrameAttrMgr::SwFlyFrameAttrMgr( bool bNew, SwWrtShell* pSh, Frmmgr_Type nT if (nType == Frmmgr_Type::GRF || nType == Frmmgr_Type::OLE) { - m_aSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_CHAR)); + // Default anchor for new graphics and objects is at-char, except for Math objects. + SwViewOption aViewOpt(*pSh->GetViewOptions()); + m_aSet.Put(SwFormatAnchor(aViewOpt.GetDefaultAnchorType()));//RndStdIds::FLY_AT_CHAR } } else if ( nType == Frmmgr_Type::NONE ) diff --git a/sw/source/uibase/inc/bookmark.hxx b/sw/source/uibase/inc/bookmark.hxx index 3a8ee1506d58..01ae7f416c56 100644 --- a/sw/source/uibase/inc/bookmark.hxx +++ b/sw/source/uibase/inc/bookmark.hxx @@ -33,8 +33,8 @@ class BookmarkTable std::unique_ptr<weld::TreeIter> GetRowByBookmarkName(const OUString& sName); public: BookmarkTable(std::unique_ptr<weld::TreeView> xControl); - void InsertBookmark(sw::mark::IMark* pMark); - void SelectByName(const OUString& sName); + void InsertBookmark(SwWrtShell & rSh, sw::mark::IMark* pMark); + bool SelectByName(const OUString& sName); sw::mark::IMark* GetBookmarkByName(const OUString& sName); OUString GetNameProposal() const; @@ -45,6 +45,7 @@ public: void remove(const weld::TreeIter& rIter) { m_xControl->remove(rIter); } void select(const weld::TreeIter& rIter) { m_xControl->select(rIter); } void remove_selection() { m_xControl->remove_selection(); } + OUString get_text(const weld::TreeIter& rIter) const { return m_xControl->get_text(rIter, 2); } OUString get_id(const weld::TreeIter& rIter) const { return m_xControl->get_id(rIter); } void set_sort_indicator(TriState eState, int nColumn = -1) { m_xControl->set_sort_indicator(eState, nColumn); } void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) { m_xControl->selected_foreach(func); } @@ -52,6 +53,10 @@ public: void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xControl->connect_changed(rLink); } void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { m_xControl->connect_row_activated(rLink); } void connect_column_clicked(const Link<int, void>& rLink) { m_xControl->connect_column_clicked(rLink); } + void connect_editing(const Link<const weld::TreeIter&, bool>& rStartLink, + const Link<::std::pair<weld::TreeIter const&, OUString> const&, bool>& rEndLink) { m_xControl->connect_editing(rStartLink, rEndLink); } + void set_column_editables(::std::vector<bool> const& rEditables) { m_xControl->set_column_editables(rEditables); } + void start_editing(weld::TreeIter const& rIter) { m_xControl->start_editing(rIter); } void make_sorted() { m_xControl->make_sorted(); } bool get_sort_order() const { return m_xControl->get_sort_order(); } void set_sort_order(bool bAscending) { m_xControl->set_sort_order(bAscending); } @@ -75,6 +80,7 @@ class SwInsertBookmarkDlg : public SfxDialogController std::unique_ptr<weld::Button> m_xInsertBtn; std::unique_ptr<weld::Button> m_xDeleteBtn; std::unique_ptr<weld::Button> m_xGotoBtn; + std::unique_ptr<weld::Button> m_xEditTextBtn; std::unique_ptr<weld::Button> m_xRenameBtn; std::unique_ptr<weld::CheckButton> m_xHideCB; std::unique_ptr<weld::Label> m_xConditionFT; @@ -85,12 +91,16 @@ class SwInsertBookmarkDlg : public SfxDialogController DECL_LINK(ModifyHdl, weld::Entry&, void); DECL_LINK(InsertHdl, weld::Button&, void); DECL_LINK(DeleteHdl, weld::Button&, void); + DECL_LINK(EditTextHdl, weld::Button&, void); DECL_LINK(RenameHdl, weld::Button&, void); DECL_LINK(GotoHdl, weld::Button&, void); DECL_LINK(SelectionChangedHdl, weld::TreeView&, void); DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); DECL_LINK(HeaderBarClick, int, void); DECL_LINK(ChangeHideHdl, weld::ToggleButton&, void); + DECL_LINK(EditingHdl, weld::TreeIter const&, bool); + typedef ::std::pair<weld::TreeIter const&, OUString> const& comma_issue; + DECL_LINK(EditedHdl, comma_issue, bool); // Fill table with bookmarks void PopulateTable(); @@ -101,9 +111,10 @@ class SwInsertBookmarkDlg : public SfxDialogController bool ValidateBookmarks(); bool HaveBookmarksChanged(); void GotoSelectedBookmark(); + void SelectionChanged(); public: - SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rSh, SfxRequest& rReq); + SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rSh, SfxRequest& rReq, OUString const* pSelected); virtual ~SwInsertBookmarkDlg() override; }; diff --git a/sw/source/uibase/inc/cfgitems.hxx b/sw/source/uibase/inc/cfgitems.hxx index 297ac1846faa..11fc13baecc0 100644 --- a/sw/source/uibase/inc/cfgitems.hxx +++ b/sw/source/uibase/inc/cfgitems.hxx @@ -51,6 +51,7 @@ class SW_DLLPUBLIC SwDocDisplayItem : public SfxPoolItem bool bCharHiddenText :1; bool bBookmarks :1; bool bManualBreak :1; + sal_Int32 m_xDefaultAnchor; public: SwDocDisplayItem(); diff --git a/sw/source/uibase/inc/fldtdlg.hxx b/sw/source/uibase/inc/fldtdlg.hxx index 159881ed1192..9546b99ffa0d 100644 --- a/sw/source/uibase/inc/fldtdlg.hxx +++ b/sw/source/uibase/inc/fldtdlg.hxx @@ -53,7 +53,7 @@ public: void ActivateDatabasePage(); void ShowReferencePage(); virtual void Close() override; - virtual void EndDialog() override; + virtual void EndDialog(int nResponse) override; virtual void Activate() override; }; diff --git a/sw/source/uibase/inc/optpage.hxx b/sw/source/uibase/inc/optpage.hxx index 8de1394b633e..d85091a4572e 100644 --- a/sw/source/uibase/inc/optpage.hxx +++ b/sw/source/uibase/inc/optpage.hxx @@ -244,6 +244,8 @@ class SwShdwCursorOptionsTabPage : public SfxTabPage std::unique_ptr<weld::Frame> m_xCursorProtFrame; std::unique_ptr<weld::CheckButton> m_xCursorInProtCB; + std::unique_ptr<weld::ComboBox> m_xDefaultAnchorType; + std::unique_ptr<weld::CheckButton> m_xMathBaselineAlignmentCB; public: diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx index 6ec17d5b0e78..decf24bb4a08 100644 --- a/sw/source/uibase/inc/wrtsh.hxx +++ b/sw/source/uibase/inc/wrtsh.hxx @@ -140,6 +140,7 @@ public: // is there a text- or frameselection? bool HasSelection() const { return SwCursorShell::HasSelection() || IsMultiSelection() || IsSelFrameMode() || IsObjSelected(); } + bool Pop(SwCursorShell::PopMode, ::std::unique_ptr<SwCallLink> const pLink); bool Pop(SwCursorShell::PopMode = SwCursorShell::PopMode::DeleteStack); void EnterStdMode(); @@ -279,7 +280,7 @@ typedef bool (SwWrtShell:: *FNSimpleMove)(); bool DelLeft(); // also deletes the frame or sets the cursor in the frame when bDelFrame == false - bool DelRight(); + bool DelRight(bool isReplaceHeuristic = false); void DelToEndOfPara(); void DelToStartOfPara(); bool DelToEndOfSentence(); @@ -489,6 +490,9 @@ typedef bool (SwWrtShell:: *FNSimpleMove)(); /// Inserts a new annotation/comment at the current cursor position / selection. void InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq); + virtual void InfoReadOnlyDialog() const override; + virtual bool WarnHiddenSectionDialog() const override; + private: SAL_DLLPRIVATE void OpenMark(); diff --git a/sw/source/uibase/lingu/hhcwrp.cxx b/sw/source/uibase/lingu/hhcwrp.cxx index 52ee334ec21d..55f09447c623 100644 --- a/sw/source/uibase/lingu/hhcwrp.cxx +++ b/sw/source/uibase/lingu/hhcwrp.cxx @@ -326,7 +326,7 @@ void SwHHCWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttribut // restore those for the new text m_rWrtShell.GetCurAttr( aItemSet ); - m_rWrtShell.Delete(); + m_rWrtShell.Delete(true); m_rWrtShell.Insert( rNewText ); // select new inserted text (currently the Point is right after the new text) @@ -346,7 +346,7 @@ void SwHHCWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttribut } else { - m_rWrtShell.Delete(); + m_rWrtShell.Delete(true); m_rWrtShell.Insert( rNewText ); } } diff --git a/sw/source/uibase/ribbar/inputwin.cxx b/sw/source/uibase/ribbar/inputwin.cxx index 1c278137cb16..fdab2b6ec5de 100644 --- a/sw/source/uibase/ribbar/inputwin.cxx +++ b/sw/source/uibase/ribbar/inputwin.cxx @@ -245,7 +245,7 @@ void SwInputWindow::ShowWin() if( pWrtShell->SwCursorShell::HasSelection() ) { pWrtShell->StartUndo( SwUndoId::DELETE ); - pWrtShell->Delete(); + pWrtShell->Delete(false); if( SwUndoId::EMPTY != pWrtShell->EndUndo( SwUndoId::DELETE )) { m_bCallUndo = true; @@ -451,7 +451,7 @@ void SwInputWindow::DelBoxContent() pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); pWrtShell->SetMark(); pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); - pWrtShell->SwEditShell::Delete(); + pWrtShell->SwEditShell::Delete(false); pWrtShell->EndAllAction(); } } diff --git a/sw/source/uibase/ribbar/workctrl.cxx b/sw/source/uibase/ribbar/workctrl.cxx index f62059f661e5..d7260fdfe1d2 100644 --- a/sw/source/uibase/ribbar/workctrl.cxx +++ b/sw/source/uibase/ribbar/workctrl.cxx @@ -97,15 +97,18 @@ VclPtr<SfxPopupWindow> SwTbxAutoTextCtrl::CreatePopupWindow() ScopedVclPtrInstance<PopupMenu> pPopup; SwGlossaryList* pGlossaryList = ::GetGlossaryList(); const size_t nGroupCount = pGlossaryList->GetGroupCount(); + o3tl::sorted_vector<OUString> titles; for(size_t i = 1; i <= nGroupCount; ++i) { OUString sTitle = pGlossaryList->GetGroupTitle(i - 1); const sal_uInt16 nBlockCount = pGlossaryList->GetBlockCount(i -1); + auto const [it, _] = titles.insert(sTitle); + size_t const menuIndex(::std::distance(titles.begin(), it)); if(nBlockCount) { sal_uInt16 nIndex = static_cast<sal_uInt16>(100*i); // but insert without extension - pPopup->InsertItem( i, sTitle); + pPopup->InsertItem(i, sTitle, MenuItemBits::NONE, {}, menuIndex); VclPtrInstance<PopupMenu> pSub; pSub->SetSelectHdl(aLnk); pPopup->SetPopupMenu(i, pSub); diff --git a/sw/source/uibase/shells/drwtxtex.cxx b/sw/source/uibase/shells/drwtxtex.cxx index 60bdf16380be..0e3ba07c3973 100644 --- a/sw/source/uibase/shells/drwtxtex.cxx +++ b/sw/source/uibase/shells/drwtxtex.cxx @@ -545,12 +545,8 @@ void SwDrawTextShell::Execute( SfxRequest &rReq ) const SvxFieldData* pField = pOLV->GetFieldAtCursor(); if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField)) { - SfxStringItem aUrl(SID_FILE_NAME, pURLField->GetURL()); - SfxStringItem aTarget(SID_TARGETNAME, pURLField->GetTargetFrame()); - SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, false); - SfxBoolItem aBrowsing(SID_BROWSE, true); - GetView().GetViewFrame()->GetDispatcher()->ExecuteList( - SID_OPENDOC, SfxCallMode::SYNCHRON, { &aUrl, &aTarget, &aNewView, &aBrowsing }); + ::LoadURL(GetShell(), pURLField->GetURL(), LoadUrlFlags::NONE, + pURLField->GetTargetFrame()); } } break; diff --git a/sw/source/uibase/shells/tabsh.cxx b/sw/source/uibase/shells/tabsh.cxx index 073fe280c924..e7260ef1492f 100644 --- a/sw/source/uibase/shells/tabsh.cxx +++ b/sw/source/uibase/shells/tabsh.cxx @@ -181,7 +181,7 @@ static std::shared_ptr<SwTableRep> lcl_TableParamToItemSet( SfxItemSet& rSet, Sw rSet.Put(*aBoxDirection); } - bool bSelectAll = rSh.StartsWithTable() && rSh.ExtendedSelectedAll(); + bool bSelectAll = rSh.StartsWith_() == SwCursorShell::StartsWith::Table && rSh.ExtendedSelectedAll(); bool bTableSel = rSh.IsTableMode() || bSelectAll; if(!bTableSel) { diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx index c65d5d65da6a..072a38ab81b5 100644 --- a/sw/source/uibase/shells/textfld.cxx +++ b/sw/source/uibase/shells/textfld.cxx @@ -199,7 +199,7 @@ void SwTextShell::ExecField(SfxRequest &rReq) SwCursorShell::GetTextFieldAtCursor(rSh.GetCursor(), true)))) { rSh.SttSelect(); - rSh.SelectText( + rSh.SelectTextModel( SwCursorShell::StartOfInputFieldAtPos( *(rSh.GetCursor()->Start()) ) + 1, SwCursorShell::EndOfInputFieldAtPos( *(rSh.GetCursor()->Start()) ) - 1 ); } diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index aa91c39de2e8..c65efe7c1c12 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -377,10 +377,7 @@ void SwTextShell::Execute(SfxRequest &rReq) if (rWrtSh.HasReadonlySel() && !rWrtSh.CursorInsideInputField()) { // Only break if there's something to do; don't nag with the dialog otherwise - auto xInfo(std::make_unique<weld::GenericDialogController>( - rWrtSh.GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", - "InfoReadonlyDialog")); - xInfo->run(); + rWrtSh.InfoReadOnlyDialog(); break; } SwRewriter aRewriter; @@ -693,11 +690,20 @@ void SwTextShell::Execute(SfxRequest &rReq) { OUString sName = static_cast<const SfxStringItem*>(pItem)->GetValue(); rWrtSh.SetBookmark( vcl::KeyCode(), sName ); + break; + } + [[fallthrough]]; + } + case FN_EDIT_BOOKMARK: + { + ::std::optional<OUString> oName; + if (pItem) + { + oName.emplace(static_cast<const SfxStringItem*>(pItem)->GetValue()); } - else { SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); - ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwInsertBookmarkDlg(GetView().GetFrameWeld(), rWrtSh, rReq)); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwInsertBookmarkDlg(GetView().GetFrameWeld(), rWrtSh, rReq, oName ? &*oName : nullptr)); pDlg->Execute(); } diff --git a/sw/source/uibase/uitest/uiobject.cxx b/sw/source/uibase/uitest/uiobject.cxx index ecdfd68ad6a8..f44f2b5a70fb 100644 --- a/sw/source/uibase/uitest/uiobject.cxx +++ b/sw/source/uibase/uitest/uiobject.cxx @@ -13,6 +13,7 @@ #include <view.hxx> #include <wrtsh.hxx> #include <navipi.hxx> +#include <ndtxt.hxx> #include <sfx2/sidebar/Sidebar.hxx> #include <sfx2/viewfrm.hxx> @@ -89,14 +90,30 @@ void SwEditWinUIObject::execute(const OUString& rAction, { auto itr = rParameters.find("START_POS"); OUString aStartPos = itr->second; - sal_Int32 nStartPos = aStartPos.toInt32(); + TextFrameIndex const nStartPos(aStartPos.toInt32()); itr = rParameters.find("END_POS"); assert(itr != rParameters.end()); OUString aEndPos = itr->second; - sal_Int32 nEndPos = aEndPos.toInt32(); - - getWrtShell(mxEditWin).SelectText(nStartPos, nEndPos); + TextFrameIndex const nEndPos(aEndPos.toInt32()); + + auto & shell = getWrtShell(mxEditWin); + if (shell.GetCursor_()->GetPoint()->nNode.GetNode().GetTextNode()) + { + shell.Push(); + shell.MovePara(GoCurrPara, fnParaEnd); + TextFrameIndex const len(shell.GetCursorPointAsViewIndex()); + shell.Pop(SwCursorShell::PopMode::DeleteCurrent); + SAL_WARN_IF( + sal_Int32(nStartPos) < 0 || nStartPos > len || sal_Int32(nEndPos) < 0 || nEndPos > len, "sw.ui", + "SELECT START/END_POS " << sal_Int32(nStartPos) << ".." << sal_Int32(nEndPos) << " outside 0.." << sal_Int32(len)); + shell.SelectTextView( + std::clamp(nStartPos, TextFrameIndex(0), len), std::clamp(nEndPos, TextFrameIndex(0), len)); + } + else + { + SAL_WARN("sw.ui", "SELECT without SwTextNode"); + } } } else if (rAction == "SIDEBAR") diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx index 296140abbae7..03df8259d09f 100644 --- a/sw/source/uibase/uiview/view.cxx +++ b/sw/source/uibase/uiview/view.cxx @@ -1166,6 +1166,10 @@ void SwView::WriteUserData( OUString &rUserData, bool bBrowse ) static bool lcl_IsOwnDocument( SwView& rView ) { + if (::officecfg::Office::Common::Load::ViewPositionForAnyUser::get()) + { + return true; + } uno::Reference<document::XDocumentPropertiesSupplier> xDPS( rView.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW); uno::Reference<document::XDocumentProperties> xDocProps diff --git a/sw/source/uibase/uiview/viewmdi.cxx b/sw/source/uibase/uiview/viewmdi.cxx index 4fd706e84667..5fb06156c1a2 100644 --- a/sw/source/uibase/uiview/viewmdi.cxx +++ b/sw/source/uibase/uiview/viewmdi.cxx @@ -127,14 +127,15 @@ void SwView::SetZoom_( const Size &rEditSize, SvxZoomType eZoomType, const MapMode aTmpMap( MapUnit::MapTwip ); const Size aWindowSize( GetEditWin().PixelToLogic( rEditSize, aTmpMap ) ); - if( UseOnPage::Mirror == rDesc.GetUseOn() ) // mirrored pages - { - const SvxLRSpaceItem &rLeftLRSpace = rDesc.GetLeft().GetLRSpace(); - aPageSize.AdjustWidth(std::abs( rLeftLRSpace.GetLeft() - rLRSpace.GetLeft() ) ); - } - if( SvxZoomType::OPTIMAL == eZoomType ) { + // unclear if this is useful for OPTIMAL, or completely useless? + if( UseOnPage::Mirror == rDesc.GetUseOn() ) // mirrored pages + { + const SvxLRSpaceItem &rLeftLRSpace = rDesc.GetLeft().GetLRSpace(); + aPageSize.AdjustWidth(std::abs( rLeftLRSpace.GetLeft() - rLRSpace.GetLeft() ) ); + } + if (!pPostItMgr->HasNotes() || !pPostItMgr->ShowNotes()) aPageSize.AdjustWidth( -( rLRSpace.GetLeft() + rLRSpace.GetRight() + nLeftOfst * 2 ) ); lLeftMargin = rLRSpace.GetLeft() + DOCUMENTBORDER + nLeftOfst; diff --git a/sw/source/uibase/uiview/viewport.cxx b/sw/source/uibase/uiview/viewport.cxx index 2fa7e12f5afe..fb40aa453798 100644 --- a/sw/source/uibase/uiview/viewport.cxx +++ b/sw/source/uibase/uiview/viewport.cxx @@ -323,8 +323,9 @@ void SwView::SetVisArea( const Point &rPt, bool bUpdateScrollbar ) void SwView::CheckVisArea() { - m_pHScrollbar->SetAuto( m_pWrtShell->GetViewOptions()->getBrowseMode() && - !GetViewFrame()->GetFrame().IsInPlace() ); + if (m_pHScrollbar) + m_pHScrollbar->SetAuto( m_pWrtShell->GetViewOptions()->getBrowseMode() && + !GetViewFrame()->GetFrame().IsInPlace() ); if ( IsDocumentBorder() ) { if ( m_aVisArea.Left() != DOCUMENTBORDER || diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index ff05af4f2a3b..891e786f63d9 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -19,6 +19,7 @@ #include <comphelper/string.hxx> #include <svl/urlbmk.hxx> +#include <svl/stritem.hxx> #include <osl/thread.h> #include <sal/log.hxx> #include <tools/urlobj.hxx> @@ -348,7 +349,7 @@ void SwContentType::Init(bool* pbInvalidateWindow) &lcl_IsUiVisibleBookmark); m_sTypeToken.clear(); const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); - m_bEdit = !bProtectedBM; + m_bEdit = true; m_bDelete = !bProtectedBM; } break; @@ -1296,7 +1297,7 @@ VclPtr<PopupMenu> SwContentTree::CreateContextMenu() const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType) && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); const bool bEditable = pContType->IsEditable() && - ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + ((bVisible && !bProtected) || ContentTypeId::REGION == nContentType); const bool bDeletable = pContType->IsDeletable() && ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); const bool bRenamable = bEditable && !bReadonly && @@ -3534,20 +3535,27 @@ void SwContentTree::EditEntry(SvTreeListEntry const * pEntry, EditEntryMode nMod nSlot = FN_FORMAT_FRAME_DLG; break; case ContentTypeId::BOOKMARK : - assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); if(nMode == EditEntryMode::DELETE) { + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false); } else if(nMode == EditEntryMode::RENAME) { + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); uno::Reference< text::XBookmarksSupplier > xBkms(xModel, uno::UNO_QUERY); xNameAccess = xBkms->getBookmarks(); } else - nSlot = FN_INSERT_BOOKMARK; + { + // allowed despite PROTECT_BOOKMARKS: the dialog itself enforces it + SfxStringItem const name(FN_EDIT_BOOKMARK, pCnt->GetName()); + SfxPoolItem const* args[2] = { &name, nullptr }; + m_pActiveShell->GetView().GetViewFrame()-> + GetDispatcher()->Execute(FN_EDIT_BOOKMARK, SfxCallMode::SYNCHRON, args); + } break; case ContentTypeId::REGION : diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx index 5f6ed36c25e2..ec0a88e64cf0 100644 --- a/sw/source/uibase/wrtsh/delete.cxx +++ b/sw/source/uibase/wrtsh/delete.cxx @@ -104,7 +104,7 @@ void SwWrtShell::DelLine() SetMark(); SwCursorShell::RightMargin(); - bool bRet = Delete(); + bool bRet = Delete(false); Pop(SwCursorShell::PopMode::DeleteCurrent); if( bRet ) UpdateAttr(); @@ -114,7 +114,7 @@ void SwWrtShell::DelToStartOfLine() { OpenMark(); SwCursorShell::LeftMargin(); - bool bRet = Delete(); + bool bRet = Delete(false); CloseMark( bRet ); } @@ -122,7 +122,7 @@ void SwWrtShell::DelToEndOfLine() { OpenMark(); SwCursorShell::RightMargin(); - bool bRet = Delete(); + bool bRet = Delete(false); CloseMark( bRet ); } @@ -164,7 +164,7 @@ bool SwWrtShell::DelLeft() { SwActContext aActContext(this); ResetCursorStack(); - Delete(); + Delete(false); UpdateAttr(); } if( IsBlockMode() ) @@ -275,20 +275,15 @@ bool SwWrtShell::DelLeft() } } } - bool bRet = Delete(); + bool bRet = Delete(true); if( !bRet && bSwap ) SwCursorShell::SwapPam(); CloseMark( bRet ); - if (!bRet) - { // false indicates HasReadonlySel failed - std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); - std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); - xInfo->run(); - } + return bRet; } -bool SwWrtShell::DelRight() +bool SwWrtShell::DelRight(bool const isReplaceHeuristic) { // Will be or'ed, if a tableselection exists; // will here be implemented on SelectionType::Table @@ -315,7 +310,7 @@ bool SwWrtShell::DelRight() { SwActContext aActContext(this); ResetCursorStack(); - Delete(); + Delete(isReplaceHeuristic); UpdateAttr(); } if( IsBlockMode() ) @@ -398,14 +393,8 @@ bool SwWrtShell::DelRight() OpenMark(); SwCursorShell::Right(1, CRSR_SKIP_CELLS); - bRet = Delete(); + bRet = Delete(true); CloseMark( bRet ); - if (!bRet) - { // false indicates HasReadonlySel failed - std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); - std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); - xInfo->run(); - } break; case SelectionType::Frame: @@ -510,7 +499,7 @@ void SwWrtShell::DelToEndOfPara() Pop(SwCursorShell::PopMode::DeleteCurrent); return; } - bool bRet = Delete(); + bool bRet = Delete(false); Pop(SwCursorShell::PopMode::DeleteCurrent); if( bRet ) UpdateAttr(); @@ -527,7 +516,7 @@ void SwWrtShell::DelToStartOfPara() Pop(SwCursorShell::PopMode::DeleteCurrent); return; } - bool bRet = Delete(); + bool bRet = Delete(false); Pop(SwCursorShell::PopMode::DeleteCurrent); if( bRet ) UpdateAttr(); @@ -542,7 +531,7 @@ void SwWrtShell::DelToStartOfSentence() if(IsStartOfDoc()) return; OpenMark(); - bool bRet = BwdSentence_() && Delete(); + bool bRet = BwdSentence_() && Delete(false); CloseMark( bRet ); } @@ -574,7 +563,7 @@ bool SwWrtShell::DelToEndOfSentence() } else { - bRet = FwdSentence_() && Delete(); + bRet = FwdSentence_() && Delete(false); } CloseMark( bRet ); return bRet; @@ -595,7 +584,7 @@ void SwWrtShell::DelNxtWord() else EndWrd(); - bool bRet = Delete(); + bool bRet = Delete(false); if( bRet ) UpdateAttr(); else @@ -619,7 +608,7 @@ void SwWrtShell::DelPrvWord() else SttWrd(); } - bool bRet = Delete(); + bool bRet = Delete(false); if( bRet ) UpdateAttr(); else diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx index 76f4baedaba5..b0dcdc522de7 100644 --- a/sw/source/uibase/wrtsh/move.cxx +++ b/sw/source/uibase/wrtsh/move.cxx @@ -218,12 +218,25 @@ bool SwWrtShell::GoStart( bool bKeepArea, bool *pMoveTable, *pMoveTable = false; return true; } + SwTableNode const*const pTable(getShellCursor(false)->GetPoint()->nNode.GetNode().FindTableNode()); + assert(pTable); if( MoveTable( GotoCurrTable, fnTableStart ) || bDontMoveRegion ) { if ( pMoveTable ) *pMoveTable = true; return true; } + else if (SwCursor const*const pCursor = getShellCursor(false); + pTable->GetNodes()[pTable->GetIndex()+1]->EndOfSectionIndex() + < pCursor->GetPoint()->nNode.GetNode().GetIndex() + && pMoveTable != nullptr // only set by SelAll() + // problem: cursor isn't inside 1st cell, and didn't move there + // workaround: try to move cursor outside of table for SelAll() + && MoveOutOfTable()) + { + assert(!*pMoveTable); + return true; + } else if( bBoxSelection && pMoveTable ) { // JP 09.01.96: We have a box selection (or an empty cell) @@ -258,15 +271,40 @@ bool SwWrtShell::GoStart( bool bKeepArea, bool *pMoveTable, else if ( bKeepArea ) return true; } - // Regions ??? + + // first try to move to the start of the current SwSection return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionStart ) || - SwCursorShell::SttEndDoc(true); + (pMoveTable != nullptr + // move to start of text - if in different table, move out + ? MoveStartText() + // TODO who needs SttEndDoc for other case? + : SwCursorShell::SttEndDoc(true)); } bool SwWrtShell::GoEnd(bool bKeepArea, const bool *pMoveTable) { - if ( pMoveTable && *pMoveTable ) - return MoveTable( GotoCurrTable, fnTableEnd ); + if (pMoveTable && *pMoveTable) // only in SelAll() + { + SwTableNode const*const pTable(getShellCursor(false)->GetPoint()->nNode.GetNode().FindTableNode()); + assert(pTable); + if (MoveTable(GotoCurrTable, fnTableEnd)) + { + return true; + } + else if (SwCursor const*const pCursor = getShellCursor(false); + pCursor->GetPoint()->nNode.GetNode().GetIndex() + < pTable->GetNodes()[pTable->EndOfSectionIndex()-1]->StartOfSectionIndex() + // problem: cursor isn't inside 1st cell, and didn't move there + // workaround: try to move cursor outside of table for SelAll() + && MoveOutOfTable()) + { + return true; + } + else + { + return false; + } + } if ( IsCursorInTable() ) { diff --git a/sw/source/uibase/wrtsh/select.cxx b/sw/source/uibase/wrtsh/select.cxx index 90664ae098f5..17d4c068416a 100644 --- a/sw/source/uibase/wrtsh/select.cxx +++ b/sw/source/uibase/wrtsh/select.cxx @@ -141,7 +141,10 @@ void SwWrtShell::SelAll() bool bHasWholeTabSelection = HasWholeTabSelection(); bool bIsCursorInTable = IsCursorInTable(); - if (!bHasWholeTabSelection) + if (!bHasWholeTabSelection + && ( !bIsCursorInTable + || getShellCursor(false)->GetNode(false).FindTableNode() == nullptr + || !ExtendedSelectedAll())) // ESA inside table -> else branch { if ( IsSelection() && IsCursorPtAtEnd() ) SwapPam(); @@ -157,30 +160,35 @@ void SwWrtShell::SelAll() bIsFullSel &= !MoveSection( GoCurrSection, fnSectionEnd); Pop(SwCursorShell::PopMode::DeleteCurrent); GoStart(true, &bMoveTable, false, !bIsFullSel); + SttSelect(); + GoEnd(true, &bMoveTable); } else { - EnterStdMode(); - SttEndDoc(true); + if (MoveOutOfTable()) + { // select outer text + EnterStdMode(); // delete m_pTableCursor +// GoStart(true, &bMoveTable, false, true); + MoveSection(GoCurrSection, fnSectionStart); // don't move into prev table + SttSelect(); + MoveSection(GoCurrSection, fnSectionEnd); // don't move to different cell + } + else + { + TrySelectOuterTable(); + } } - SttSelect(); - GoEnd(true, &bMoveTable); - bool bNeedsExtendedSelectAll = StartsWithTable(); + bool bNeedsExtendedSelectAll = StartsWith_() != StartsWith::None; - // If the cursor was in a table, then we only need the extended select - // all if the whole table is already selected, to still allow selecting - // only a single cell or a single table before selecting the whole - // document. + // the GoEnd() could have created a table selection, if so avoid ESA. if (bNeedsExtendedSelectAll && bIsCursorInTable) - bNeedsExtendedSelectAll = bHasWholeTabSelection; + { + bNeedsExtendedSelectAll = !HasWholeTabSelection(); + } if (bNeedsExtendedSelectAll) { - // Disable table cursor to make sure getShellCursor() returns m_pCurrentCursor, not m_pTableCursor. - if (IsTableMode()) - TableCursorToCursor(); - // Do the extended select all on m_pCurrentCursor. ExtendedSelectAll(/*bFootnotes =*/ false); } @@ -914,7 +922,7 @@ int SwWrtShell::IntelligentCut(SelectionType nSelection, bool bCut) ClearMark(); SetMark(); SwCursorShell::Left(1,CRSR_SKIP_CHARS); - SwFEShell::Delete(); + SwFEShell::Delete(true); Pop(SwCursorShell::PopMode::DeleteCurrent); } } @@ -928,7 +936,7 @@ int SwWrtShell::IntelligentCut(SelectionType nSelection, bool bCut) ClearMark(); SetMark(); SwCursorShell::Right(1,CRSR_SKIP_CHARS); - SwFEShell::Delete(); + SwFEShell::Delete(true); Pop(SwCursorShell::PopMode::DeleteCurrent); } } diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx index 9eb00d361b9a..d8d7d77e209c 100644 --- a/sw/source/uibase/wrtsh/wrtsh1.cxx +++ b/sw/source/uibase/wrtsh/wrtsh1.cxx @@ -48,6 +48,7 @@ #include <editeng/svxacorr.hxx> #include <editeng/ulspitem.hxx> #include <vcl/graph.hxx> +#include <vcl/svapp.hxx> #include <sfx2/printer.hxx> #include <unotools/charclass.hxx> #include <comphelper/storagehelper.hxx> @@ -118,6 +119,9 @@ #include <comphelper/lok.hxx> #include <memory> +#include "../../core/crsr/callnk.hxx" + + using namespace sw::mark; using namespace com::sun::star; namespace { @@ -213,11 +217,6 @@ void SwWrtShell::Insert( const OUString &rStr ) bCallIns = m_bIns /*|| bHasSel*/; bool bDeleted = false; - typedef svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_RSID - 1, - RES_CHRATR_RSID + 1, RES_CHRATR_END - 1, - RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> CharItems; - SfxItemSet aCharAttrSet(GetAttrPool(), CharItems{}); - if( bHasSel || ( !m_bIns && SelectHiddenRange() ) ) { // Only here parenthesizing, because the normal @@ -235,37 +234,19 @@ void SwWrtShell::Insert( const OUString &rStr ) aRewriter.AddRule(UndoArg3, aTmpStr); } - // tdf#79717 Save character formatting of the start of the selection - const SwPosition *pStart = GetCursor()->Start(); - SwPaM aPaM(pStart->nNode.GetNode(), pStart->nContent.GetIndex(), - pStart->nNode.GetNode(), pStart->nContent.GetIndex() + 1); - GetPaMAttr(&aPaM, aCharAttrSet); - StartUndo(SwUndoId::REPLACE, &aRewriter); bStarted = true; - bDeleted = DelRight(); + Push(); + // let's interpret a selection within the same node as "replace" + bDeleted = DelRight(GetCursor()->GetPoint()->nNode == GetCursor()->GetMark()->nNode); + Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore selection (if tracking changes) + NormalizePam(false); // tdf#127635 put point at the end of deletion + ClearMark(); } bCallIns ? SwEditShell::Insert2( rStr, bDeleted ) : SwEditShell::Overwrite( rStr ); - if( bDeleted ) - { - // tdf#79717 Restore formatting of the deleted selection - SwPosition* pEnd = GetCursor()->Start(); - SwPaM aPaM(pEnd->nNode.GetNode(), pEnd->nContent.GetIndex() - rStr.getLength(), - pEnd->nNode.GetNode(), pEnd->nContent.GetIndex()); - - std::set<sal_uInt16> aAttribs; - for (sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; ++i) - if (i != sal_uInt16(RES_CHRATR_RSID)) - aAttribs.insert(aAttribs.end(), i); - aAttribs.insert(aAttribs.end(), RES_TXTATR_CHARFMT); - ResetAttr(aAttribs, &aPaM); - - SetAttrSet(aCharAttrSet, SetAttrMode::DEFAULT, &aPaM); - } - if( bStarted ) { EndUndo(); @@ -1663,7 +1644,7 @@ void SwWrtShell::AutoCorrect( SvxAutoCorrect& rACorr, sal_Unicode cChar ) StartUndo( SwUndoId::REPLACE, &aRewriter ); bStarted = true; - DelRight(); + DelRight(true); } SwEditShell::AutoCorrect( rACorr, IsInsMode(), cChar ); @@ -1725,7 +1706,13 @@ SwWrtShell::~SwWrtShell() bool SwWrtShell::Pop(SwCursorShell::PopMode const eDelete) { - bool bRet = SwCursorShell::Pop(eDelete); + ::std::unique_ptr<SwCallLink> pLink(::std::make_unique<SwCallLink>(*this)); + return Pop(eDelete, ::std::move(pLink)); +} + +bool SwWrtShell::Pop(SwCursorShell::PopMode const eDelete, ::std::unique_ptr<SwCallLink> pLink) +{ + bool bRet = SwCursorShell::Pop(eDelete, ::std::move(pLink)); if( bRet && IsSelection() ) { m_fnSetCursor = &SwWrtShell::SetCursorKillSel; @@ -2010,4 +1997,24 @@ void SwWrtShell::InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq) } } +void SwWrtShell::InfoReadOnlyDialog() const +{ + std::unique_ptr<weld::Builder> + xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), + "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> + xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xInfo->run(); +} + +bool SwWrtShell::WarnHiddenSectionDialog() const +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder( + GetView().GetFrameWeld(), "modules/swriter/ui/warnhiddensectiondialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery( + xBuilder->weld_message_dialog("WarnHiddenSectionDialog")); + + return (RET_YES == xQuery->run()); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh2.cxx b/sw/source/uibase/wrtsh/wrtsh2.cxx index 82d7c27cb0c6..57769ab6e881 100644 --- a/sw/source/uibase/wrtsh/wrtsh2.cxx +++ b/sw/source/uibase/wrtsh/wrtsh2.cxx @@ -488,30 +488,24 @@ bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter ) return bRet; } -void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, - const OUString& rTargetFrameName ) +static void LoadURL(SwView& rView, const OUString& rURL, LoadUrlFlags nFilter, + const OUString& rTargetFrameName) { - OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" ); - if( rURL.isEmpty() ) - return ; + SwDocShell* pDShell = rView.GetDocShell(); + OSL_ENSURE( pDShell, "No DocShell?!"); + SfxViewFrame& rViewFrame = *rView.GetViewFrame(); - // The shell could be 0 also!!!!! - if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr ) + if (!SfxObjectShell::AllowedLinkProtocolFromDocument(rURL, pDShell, rView.GetFrameWeld())) return; // We are doing tiledRendering, let the client handles the URL loading, // unless we are jumping to a TOC mark. if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#")) { - rVSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8().getStr()); + rView.libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8().getStr()); return; } - //A CursorShell is always a WrtShell - SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh); - - SwDocShell* pDShell = rSh.GetView().GetDocShell(); - OSL_ENSURE( pDShell, "No DocShell?!"); OUString sTargetFrame(rTargetFrameName); if (sTargetFrame.isEmpty() && pDShell) { @@ -526,8 +520,7 @@ void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, OUString sReferer; if( pDShell && pDShell->GetMedium() ) sReferer = pDShell->GetMedium()->GetName(); - SfxViewFrame* pViewFrame = rSh.GetView().GetViewFrame(); - SfxFrameItem aView( SID_DOCFRAME, pViewFrame ); + SfxFrameItem aView( SID_DOCFRAME, &rViewFrame ); SfxStringItem aName( SID_FILE_NAME, rURL ); SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame ); SfxStringItem aReferer( SID_REFERER, sReferer ); @@ -548,10 +541,27 @@ void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, nullptr }; - pViewFrame->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr, + rViewFrame.GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr, SfxCallMode::ASYNCHRON|SfxCallMode::RECORD ); } +void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, + const OUString& rTargetFrameName ) +{ + OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" ); + if( rURL.isEmpty() ) + return ; + + // The shell could be 0 also!!!!! + if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr ) + return; + + //A CursorShell is always a WrtShell + SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh); + + ::LoadURL(rSh.GetView(), rURL, nFilter, rTargetFrameName); +} + void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk, const sal_uInt16 nAction ) { |